条件 "Browsable" 属性

19


有没有一种方法可以使"Browsable"属性有条件地出现,这样应用它的属性有时会出现在属性页上,有时则不会?
谢谢 :)

7个回答

11

我不确定这是否适用于你的情况,但你可以通过调用下面的函数来在运行时调整“Browsable”修饰。

/// <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);
}

你好。我正在寻找类似于这样的东西。当我使用你的代码时,在第一行 PropertyDescriptor theDescriptor = TypeDescriptor.GetProperties(this.GetType())[strPropertyName]; 中出现了 nullValue 异常。我想知道在哪里放置这些函数以及它如何反映在属性网格上(在函数中没有看到 propertyGrid 对象)。 - Ganesh Kamath - 'Code Frenzy'
1
非常感谢您的解决方案,我将该行更改为 PropertyDescriptor theDescriptor = TypeDescriptor.GetProperties(vObject.GetType())[strPropertyName‌​];,其中 vObject 是我正在修改的对象。 - Ganesh Kamath - 'Code Frenzy'
我应该说,你的解决方案会使所有属性的可浏览启用或禁用,这不是这个问题的目的。 - Nastaran Hakimi
这个解决方案在".NET7"中不起作用。我没有在之前的.NET Core版本中尝试过... 它在.NET Framework中是有效的... 请看我的修复方法:var isBrow = attrib.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(a=>a.Name.Contains("browsable", StringComparison.OrdinalIgnoreCase)); - undefined

8

2
实现ICustomTypeDescriptor的链接指向https://msdn.microsoft.com/magazine/msdn-magazine-issues,我认为他们已经删除了原始页面。 - gg89

6
您可以通过提供自定义类型模型来实现此操作;在最简单的级别上,您可以为您的类型提供一个派生自ExpandableObjectConverter的自定义TypeDescriptor,并根据需要包含/排除给定属性 - 但这仅适用于属性页使用的PropertyGrid。更复杂的方法是使用ICustomTypeDescriptor/TypeDescriptionProvider - 这样可以在诸如DataGridView之类的东西中工作。

3

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);
}

现在,John Cummings提出了一种使用<T>的变体解决方案:
public static void SetBrowsableAttributeOfAProperty<T>(string propertyName, bool isBrowsable)
{
    SetBrowsableAttributeOfAProperty(typeof(T), propertyName, isBrowsable);
}

现在,之前出现问题的过载(overload)已得到解决,以下修改代码已经解决了它:

/// <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
}

这很聪明 - 但它会在类的所有实例上关闭/打开Browsable。有没有办法将其应用于特定实例? - skavan

2
作为对@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);
    }

1
除了给这个解决方案点赞,因为它基本上对我有用,我必须提到由于引入泛型而导致它失败的两个问题。请在此处查看问题和完整的改进解决方案:https://dev59.com/Xm445IYBdhLWcg3w0NSu#56376788 - Umar T.

0

我在搜索中遇到了这个问题,想要声明某些成员在 IntelliSense 中可见或隐藏,并能够一次性更改所有需要在编译时隐藏的内容。我不确定这是否是您正在寻找的,但我找到了我的问题的答案...认为分享一下也无妨。

我设置了一个条件编译符号(在项目属性的“生成”选项卡中找到)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#。如果有什么不对的地方,请告诉我。


0
我更新了@neoikon的解决方案,因为它在.NET7中无法正常工作。
public static void SetBrowsableAttributeValue(Type type, string propertyName, object value)
{
    // Get the Descriptor's Properties
    var descriptor = TypeDescriptor.GetProperties(type)[propertyName];

    // Get the Descriptor's "Browsable" Attribute
    var attrib = (BrowsableAttribute) descriptor.Attributes[typeof(BrowsableAttribute)];
    var isBrow = attrib.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(a=>a.Name.Contains("browsable", StringComparison.OrdinalIgnoreCase));

    // Set the Descriptor's "Browsable" Attribute
    isBrow?.SetValue(attrib, value);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接