如何仅针对单个类实例启用/禁用属性浏览,不影响其他实例?
问题描述
现有如下C#代码,当在Myxyz类中创建BaseFilter的多个实例(如Day和Week)时,启用Day的EnableFilter属性会导致Week的SetMinFrom和SetMinTo属性也被显示,需要修改代码让属性的显示/隐藏仅作用于当前操作的实例。
现有代码
BaseFilter类
public class BaseFilter { private bool _enableFliter; private int _setMinTo; private int _setMinFrom; private string _name; [RefreshProperties(RefreshProperties.All)] public bool EnableFilter { get => _enableFliter; set { if (_enableFliter != value) { SetBrowsableAttributeValue(nameof(SetMinFrom), value); SetBrowsableAttributeValue(nameof(SetMinTo), value); } _enableFliter = value; } } [Browsable(false)] [Display(Name = "min from")] [Range(0, 100)] public int SetMinFrom { get => _setMinFrom; set { _setMinFrom = value; OnPropertyChanged(); } } [Browsable(false)] [Display(Name = "min to")] [Range(0, 100)] public int SetMinTo { get => _setMinTo; set { _setMinTo = value; OnPropertyChanged(); } } public BaseFilter(string argname) { _enableFliter = false; _setMinTickFrom = 0; // 原代码字段名错误,应为_setMinFrom _setMinTickTo = 0; // 原代码字段名错误,应为_setMinTo _name = argname; } private void SetBrowsableAttributeValue(string attribute, object value) { var descriptor = TypeDescriptor.GetProperties(GetType())[attribute]; var attrib = (BrowsableAttribute)descriptor.Attributes[typeof(BrowsableAttribute)]; var isBrow = attrib.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(a => a.Name.Contains("browsable", StringComparison.OrdinalIgnoreCase)); isBrow.SetValue(attrib, value); } }
Myxyz类
public class Myxyz { public BaseFilter Day { get; set; } = new("Day"); public BaseFilter Week { get; set; } = new("Week"); // 其他实例... }
问题根源
原代码中SetBrowsableAttributeValue方法通过TypeDescriptor.GetProperties(GetType())获取的是类级别的属性描述符,所有BaseFilter实例共享同一套属性描述符集合。修改这个集合里的BrowsableAttribute会影响所有实例,导致一个实例启用过滤时,其他实例的对应属性也会显示。
解决方案
要实现实例级的属性显示控制,需要为每个实例维护独立的可浏览状态,并通过自定义TypeDescriptionProvider动态返回属性的可浏览性。
修改后的代码
1. 改进BaseFilter类,添加实例级状态标记
using System.ComponentModel; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; [TypeDescriptionProvider(typeof(BaseFilterTypeDescriptionProvider))] public class BaseFilter : INotifyPropertyChanged { private bool _enableFilter; private int _setMinTo; private int _setMinFrom; private string _name; // 维护当前实例的属性可浏览状态 private readonly Dictionary<string, bool> _propertyBrowsableStates = new() { { nameof(SetMinFrom), false }, { nameof(SetMinTo), false } }; [RefreshProperties(RefreshProperties.All)] public bool EnableFilter { get => _enableFilter; set { if (_enableFilter != value) { _propertyBrowsableStates[nameof(SetMinFrom)] = value; _propertyBrowsableStates[nameof(SetMinTo)] = value; _enableFilter = value; OnPropertyChanged(); // 通知属性网格刷新显示 TypeDescriptor.Refresh(this); } } } [Browsable(false)] [Display(Name = "min from")] [Range(0, 100)] public int SetMinFrom { get => _setMinFrom; set { _setMinFrom = value; OnPropertyChanged(); } } [Browsable(false)] [Display(Name = "min to")] [Range(0, 100)] public int SetMinTo { get => _setMinTo; set { _setMinTo = value; OnPropertyChanged(); } } public BaseFilter(string argname) { _enableFilter = false; _setMinFrom = 0; _setMinTo = 0; _name = argname; } // 供自定义描述器获取当前实例的属性可浏览状态 internal bool IsPropertyBrowsable(string propertyName) { if (_propertyBrowsableStates.TryGetValue(propertyName, out var state)) { return state; } // 默认返回属性原有Browsable标记 var propInfo = GetType().GetProperty(propertyName); var attrib = propInfo?.GetCustomAttribute<BrowsableAttribute>(); return attrib?.Browsable ?? true; } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 自定义类型描述器相关类
public class BaseFilterTypeDescriptionProvider : TypeDescriptionProvider { private static readonly TypeDescriptionProvider _defaultProvider = TypeDescriptor.GetProvider(typeof(BaseFilter)); public BaseFilterTypeDescriptionProvider() : base(_defaultProvider) { } public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) { var defaultDescriptor = base.GetTypeDescriptor(objectType, instance); return instance is BaseFilter filter ? new BaseFilterCustomTypeDescriptor(defaultDescriptor, filter) : defaultDescriptor; } } public class BaseFilterCustomTypeDescriptor : CustomTypeDescriptor { private readonly BaseFilter _filterInstance; public BaseFilterCustomTypeDescriptor(ICustomTypeDescriptor parent, BaseFilter filter) : base(parent) { _filterInstance = filter; } public override PropertyDescriptorCollection GetProperties() { return GetProperties(new Attribute[0]); } public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) { var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>() .Select(p => new BaseFilterPropertyDescriptor(p, _filterInstance)) .ToArray(); return new PropertyDescriptorCollection(properties); } } public class BaseFilterPropertyDescriptor : PropertyDescriptor { private readonly PropertyDescriptor _originalDescriptor; private readonly BaseFilter _filterInstance; public BaseFilterPropertyDescriptor(PropertyDescriptor original, BaseFilter filter) : base(original) { _originalDescriptor = original; _filterInstance = filter; } // 动态返回当前实例的属性可浏览状态 public override bool IsBrowsable => _filterInstance.IsPropertyBrowsable(Name) && _originalDescriptor.IsBrowsable; // 转发其他属性描述器方法 public override object GetValue(object component) => _originalDescriptor.GetValue(component); public override void SetValue(object component, object value) => _originalDescriptor.SetValue(component, value); public override bool CanResetValue(object component) => _originalDescriptor.CanResetValue(component); public override void ResetValue(object component) => _originalDescriptor.ResetValue(component); public override bool ShouldSerializeValue(object component) => _originalDescriptor.ShouldSerializeValue(component); public override Type PropertyType => _originalDescriptor.PropertyType; public override Type ComponentType => _originalDescriptor.ComponentType; }
说明
- 每个
BaseFilter实例通过_propertyBrowsableStates字典维护自己的属性显示状态,互不干扰 - 自定义的
TypeDescriptionProvider会为每个实例生成专属的属性描述器,动态覆盖IsBrowsable值 - 修改
EnableFilter时调用TypeDescriptor.Refresh(this),触发属性网格刷新显示
内容的提问来源于stack exchange,提问作者chrisk
相关产品推荐
相关产品推荐

