如何在类实例化后修改其字段的Browsable属性以适配PropertyGrid展示
动态修改PropertyGrid绑定属性Browsable状态的实现方案
核心结论
不可以直接修改编译后嵌入类元数据的BrowsableAttribute特性值,但可以通过运行时修改属性描述的判断逻辑,实现同一个类的不同实例在PropertyGrid中展示不同字段的需求。
实现方案
我们以下面的示例业务类为基础演示两种实现方式:
using System.ComponentModel; public class UserInfo { public int Id { get; set; } public string UserName { get; set; } public string Email { get; set; } public int Age { get; set; } }
方案1:反射修改特性私有字段(轻量单次使用)
该方法通过反射修改BrowsableAttribute的私有存储字段,适合临时单次控制属性可见性的场景:
using System.Reflection; // 1. 展示全部字段的场景:直接绑定即可 var fullDisplayUser = new UserInfo { Id = 1, UserName = "张三", Email = "zhangsan@example.com", Age = 25 }; propertyGrid1.SelectedObject = fullDisplayUser; // 2. 仅展示指定字段的场景:比如仅展示UserName、Age var filteredDisplayUser = new UserInfo { Id = 2, UserName = "李四", Email = "lisi@example.com", Age = 30 }; // 定义需要展示的属性名列表 var allowedProperties = new HashSet<string> { "UserName", "Age" }; // 取出类型的所有属性描述 var properties = TypeDescriptor.GetProperties(filteredDisplayUser); foreach (PropertyDescriptor prop in properties) { // 反射修改Browsable特性的内部值 var browsableAttr = (BrowsableAttribute)prop.Attributes[typeof(BrowsableAttribute)]; var browsableField = typeof(BrowsableAttribute).GetField("browsable", BindingFlags.Instance | BindingFlags.NonPublic); browsableField.SetValue(browsableAttr, allowedProperties.Contains(prop.Name)); } // 绑定后仅会展示指定字段 propertyGrid1.SelectedObject = filteredDisplayUser;
注意:该方案依赖.NET框架
BrowsableAttribute的内部私有字段命名,不同框架版本可能存在兼容风险。
方案2:自定义TypeDescriptionProvider(稳定复用方案)
该方案通过自定义类型描述提供器过滤属性集合,不会修改全局元数据,仅对指定实例生效,稳定性和复用性更高:
首先实现自定义类型描述相关类:
using System.ComponentModel; public class FilteredPropertyProvider : TypeDescriptionProvider { private readonly TypeDescriptionProvider _defaultProvider; private readonly HashSet<string> _allowedProperties; public FilteredPropertyProvider(Type targetType, HashSet<string> allowedProperties) { _defaultProvider = TypeDescriptor.GetProvider(targetType); _allowedProperties = allowedProperties; } public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) { return new FilteredPropertyDescriptor(_defaultProvider.GetTypeDescriptor(objectType, instance), _allowedProperties); } } public class FilteredPropertyDescriptor : CustomTypeDescriptor { private readonly HashSet<string> _allowedProperties; public FilteredPropertyDescriptor(ICustomTypeDescriptor parent, HashSet<string> allowedProperties) : base(parent) { _allowedProperties = allowedProperties; } public override PropertyDescriptorCollection GetProperties() { return Filter(base.GetProperties()); } public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) { return Filter(base.GetProperties(attributes)); } private PropertyDescriptorCollection Filter(PropertyDescriptorCollection originalProps) { var filtered = originalProps.Cast<PropertyDescriptor>() .Where(p => _allowedProperties.Contains(p.Name)) .ToArray(); return new PropertyDescriptorCollection(filtered); } }
使用方式:
// 定义需要展示的属性 var allowedProps = new HashSet<string> { "UserName", "Email" }; var user = new UserInfo { Id = 3, UserName = "王五", Email = "wangwu@example.com", Age = 28 }; // 给当前实例关联自定义属性过滤器,不会影响其他同类型实例 TypeDescriptor.AddProvider(new FilteredPropertyProvider(typeof(UserInfo), allowedProps), user); // 绑定后仅展示指定字段 propertyGrid1.SelectedObject = user;
内容的提问来源于stack exchange,提问作者Fabiano Augusto
相关产品推荐
相关产品推荐

