有没有办法使“可浏览”属性有条件,所以应用它的属性有时会出现在属性页面中,有时不会出现?
谢谢 :)
6 回答
我不确定这是否适用于您的情况,但您可以通过调用以下函数在运行时调整“可浏览”装饰。
/// <summary>
/// Set the Browsable property.
/// NOTE: Be sure to decorate the property with [Browsable(true)]
/// </summary>
/// <param name="PropertyName">Name of the variable</param>
/// <param name="bIsBrowsable">Browsable Value</param>
private void setBrowsableProperty(string strPropertyName, bool bIsBrowsable)
{
// Get the Descriptor's Properties
PropertyDescriptor theDescriptor = TypeDescriptor.GetProperties(this.GetType())[strPropertyName];
// Get the Descriptor's "Browsable" Attribute
BrowsableAttribute theDescriptorBrowsableAttribute = (BrowsableAttribute)theDescriptor.Attributes[typeof(BrowsableAttribute)];
FieldInfo isBrowsable = theDescriptorBrowsableAttribute.GetType().GetField("Browsable", BindingFlags.IgnoreCase | BindingFlags.NonPublic | BindingFlags.Instance);
// Set the Descriptor's "Browsable" Attribute
isBrowsable.SetValue(theDescriptorBrowsableAttribute, bIsBrowsable);
}
没有简单的方法。
您可以通过实现 ICustomTypeDescriptor 来解决这个问题。这是一篇关于实现 ICustomTypeDescriptor的好文章。
或者您可以将您自己的ControlDesigner与您的类相关联并覆盖PreFilterProperties方法以添加或删除在属性网格中查看的属性。
您可以通过提供自定义类型模型来做到这一点;在最简单的级别上,您可以为TypeDescriptor
从 派生的类型提供自定义ExpandableObjectConverter
,并随心所欲地简单地包含/排除给定的属性 - 但这仅适用于PropertyGrid
- 由属性页面使用。一种更复杂的方法是使用ICustomTypeDescriptor
/ TypeDescriptionProvider
- 然后它可以在类似的东西中工作DataGridView
作为对上述@neoikon 答案的改进并避免评论中提到的 Ganesh 异常,这里是一个使用泛型获取类型的版本:
/// <summary>
/// Set the Browsable property.
/// NOTE: Be sure to decorate the property with [Browsable(true)]
/// </summary>
/// <param name="PropertyName">Name of the variable</param>
/// <param name="bIsBrowsable">Browsable Value</param>
private void SetBrowsableProperty<T>(string strPropertyName, bool bIsBrowsable)
{
// Get the Descriptor's Properties
PropertyDescriptor theDescriptor = TypeDescriptor.GetProperties(typeof(T))[strPropertyName];
// Get the Descriptor's "Browsable" Attribute
BrowsableAttribute theDescriptorBrowsableAttribute = (BrowsableAttribute)theDescriptor.Attributes[typeof(BrowsableAttribute)];
FieldInfo isBrowsable = theDescriptorBrowsableAttribute.GetType().GetField("Browsable", BindingFlags.IgnoreCase | BindingFlags.NonPublic | BindingFlags.Instance);
// Set the Descriptor's "Browsable" Attribute
isBrowsable.SetValue(theDescriptorBrowsableAttribute, bIsBrowsable);
}
然后,您还可以添加一个采用实例的版本:
/// <summary>
/// Set the Browsable property.
/// NOTE: Be sure to decorate the property with [Browsable(true)]
/// </summary>
/// <param name="obj">An instance of the object whose property should be modified.</param>
/// <param name="PropertyName">Name of the variable</param>
/// <param name="bIsBrowsable">Browsable Value</param>
private void SetBrowsableProperty<T>(T obj, string strPropertyName, bool bIsBrowsable)
{
SetBrowsableProperty<T>(strPropertyName, bIsBrowsable);
}
用法:
class Foo
{
[Browsable(false)]
public string Bar { get; set; }
}
void Example()
{
SetBrowsableProperty<Foo>("Bar", true);
Foo foo = new Foo();
SetBrowsableProperty(foo, "Bar", false);
}
John Cummings 的解决方案基本上对我有用,但由于他引入了泛型(虽然这很聪明),所以有以下两个问题:
1-SetBrowsableProperty<T>(T obj, string strPropertyName, bool bIsBrowsable)
当集合作为参数 obj 传递时,版本将失败,因为在这种情况下,T 将是 IEnumerable(例如 List、Array 等)的实现,而不是实际预期的集合类型。
2-它也允许传入原始类型,这在这种情况下毫无意义,并且几乎总是会失败。
完整的修订解决方案:
所以这是解决这些问题并为我工作的修订解决方案:(我稍微重命名了方法和变量)
首先,完成更改 Browsable 属性值的主要工作的实际方法:
/// <summary>
/// Sets the Browsable attribute value of a property of a non premitive type.
/// NOTE: The class property must be decorated with [Browsable(...)] attribute.
/// </summary>
/// <param name="type">The type that contains the property, of which the Browsable attribute value needs to be changed</param>
/// <param name="propertyName">Name of the type property, of which the Browsable attribute value needs to be changed</param>
/// <param name="isBrowsable">The new Browsable value</param>
public static void SetBrowsableAttributeOfAProperty(Type type, string propertyName, bool isBrowsable)
{
//Validate type - disallow primitive types (this will eliminate problem-2 as mentioned above)
if (type.IsEnum || BuiltInTypes.Contains(type))
throw new Exception($"The type '{type.Name}' is not supported");
var objPropertyInfo = TypeDescriptor.GetProperties(type);
// Get the Descriptor's Properties
PropertyDescriptor theDescriptor = objPropertyInfo[propertyName];
if (theDescriptor == null)
throw new Exception($"The property '{propertyName}' is not found in the Type '{type}'");
// Get the Descriptor's "Browsable" Attribute
BrowsableAttribute theDescriptorBrowsableAttribute = (BrowsableAttribute)theDescriptor.Attributes[typeof(BrowsableAttribute)];
FieldInfo browsablility = theDescriptorBrowsableAttribute.GetType().GetField("Browsable", BindingFlags.IgnoreCase | BindingFlags.NonPublic | BindingFlags.Instance);
// Set the Descriptor's "Browsable" Attribute
browsablility.SetValue(theDescriptorBrowsableAttribute, isBrowsable);
}
现在约翰卡明斯的解决方案中提出的变体<T>
:
public static void SetBrowsableAttributeOfAProperty<T>(string propertyName, bool isBrowsable)
{
SetBrowsableAttributeOfAProperty(typeof(T), propertyName, isBrowsable);
}
现在有问题的超载没有。1,但以下修改现在处理它:
/// <summary>
/// Sets the Browsable attribute value of a property of a non premitive type.
/// NOTE: The class property must be decorated with [Browsable(...)] attribute.
/// </summary>
/// <param name="obj">An instance of the type that contains the property, of which the Browsable attribute value needs to be changed.</param>
/// <param name="propertyName">Name of the type property, of which the Browsable attribute value needs to be changed</param>
/// <param name="isBrowsable">Browsable Value</param>
public static void SetBrowsableAttributeOfAProperty<T>(T obj, string propertyName, bool isBrowsable)
{
if (typeof(T).GetInterface("IEnumerable") != null && typeof(T) != typeof(string)) //String type needs to be filtered out as String also implements IEnumerable<char> but its not a normal collection rather a primitive type
{
//Get the element type of the IEnumerable collection
Type objType = obj.GetType().GetGenericArguments()?.FirstOrDefault(); //when T is a collection that implements IEnumerable except Array
if (objType == null) objType = obj.GetType().GetElementType(); //when T is an Array
SetBrowsableAttributeOfAProperty(objType, propertyName, isBrowsable);
}
else
SetBrowsableAttributeOfAProperty(typeof(T), propertyName, isBrowsable);
这是一个实用函数,用于获取所有 C# 系统内置(原始)类型:
public static List<Type> BuiltInTypes
{
get
{
if (builtInTypes == null)
builtInTypes = Enum.GetValues(typeof(TypeCode)).Cast<TypeCode>().Select(t => Type.GetType("System." + Enum.GetName(typeof(TypeCode), t)))
.ToList();
return builtInTypes;
}
}
用法:
class Foo
{
[Browsable(false)]
public string Bar { get; set; }
}
void Example()
{
SetBrowsableAttributeOfAProperty<Foo>("Bar", true); //works
Foo foo = new Foo();
SetBrowsableAttributeOfAProperty(foo, "Bar", false); //works
List<Foo> foos = new List<Foo> { foo, new Foo { Bar = "item2" } };
SetBrowsableAttributeOfAProperty(foos, "Bar", true); //works now, whereas it would crash with an exception in John Cummings's solution
}
我在寻找一种方法来声明某些成员在 IntelliSense 中可见或隐藏并且能够更改一次以在编译时隐藏所有需要隐藏的成员时遇到了这个问题。我不知道这是否是你要找的东西,但我找到了我的问题的答案......我认为分享它不会有什么坏处。
我设置了一个条件编译符号(在项目属性的 Build 选项卡中找到)IS_VIS(如果您希望某些成员显示,则值为 true,如果您想隐藏它们,则值为 false),然后:
#if IS_VIS
public const System.ComponentModel.EditorBrowsableState isVis =
ComponentModel.EditorBrowsableState.Always;
#else
public const System.ComponentModel.EditorBrowsableState isVis =
ComponentModel.EditorBrowsableState.Never;
#endif
然后在属性中引用 isVis 变量:
[EditorBrowsable(isVis)]
public string myMethod...
我在 VB 中做了这个,然后匆忙转换为 c#。如果有什么事情不正常,请告诉我。