Conditional "Browsable" Attribute
Asked Answered
I

7

19


Is there a way to make a "Browsable" attribute conditional, so the property that applies it will sometimes appear in the properties page and sometimes not?
thanks :)

Isolationism answered 14/1, 2011 at 11:20 Comment(0)
O
8

There is no easy way.

You can possibly work this out by implementing ICustomTypeDescriptor. Here is a good article about implementing ICustomTypeDescriptor.

Or you can associate your own ControlDesigner with your class and override the PreFilterProperties method to add or remove properties viewed in the property grid.

Removing certain properties from property grid.

Overscore answered 14/1, 2011 at 11:31 Comment(1)
the link for implementing ICustomTypeDescriptor.got pointed to msdn.microsoft.com/magazine/msdn-magazine-issues I think they have removed the original pageUella
J
11

I'm not sure this applies to your situation, but you can adjust the "Browsable" decoration at run-time by calling the function below.

/// <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);
}
Judsen answered 19/11, 2013 at 3:4 Comment(4)
Hi. I am looking for something like this. When I use your code, I am getting a nullValue Exception in the first line PropertyDescriptor theDescriptor = TypeDescriptor.GetProperties(this.GetType())[strPropertyName];. I want to know where to place this functions and how it reflects on the Property grid (did not see a propertyGrid object in the function).Electrolysis
Thank you so much for your solution, i changed the line to PropertyDescriptor theDescriptor = TypeDescriptor.GetProperties(vObject.GetType())[strPropertyName‌​];, where vObject is the object I am altering.Electrolysis
I should say that your solution causes all properties browsable enable or disable which is not the aim of this question.Claudine
This solution does not work in ".NET7". I didn't try in previous versions of .NET core... It was working in .NET framework... See my fix for it: var isBrow = attrib.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(a=>a.Name.Contains("browsable", StringComparison.OrdinalIgnoreCase));Coheman
O
8

There is no easy way.

You can possibly work this out by implementing ICustomTypeDescriptor. Here is a good article about implementing ICustomTypeDescriptor.

Or you can associate your own ControlDesigner with your class and override the PreFilterProperties method to add or remove properties viewed in the property grid.

Removing certain properties from property grid.

Overscore answered 14/1, 2011 at 11:31 Comment(1)
the link for implementing ICustomTypeDescriptor.got pointed to msdn.microsoft.com/magazine/msdn-magazine-issues I think they have removed the original pageUella
O
6

You can do this by providing a custom type-model; at the simplest level, you can provide a custom TypeDescriptor for your type derived from ExpandableObjectConverter, and simply include/exclude the given property at whim - but this only works with PropertyGrid - used by the properties page. A more complex approach is to use ICustomTypeDescriptor / TypeDescriptionProvider - this can then work inside things like DataGridView

Outhaul answered 14/1, 2011 at 11:31 Comment(0)
S
3

John Cummings's solution basically worked for me but had the following two problems due to his introduction of the Generics (which was quite smart though):

1- the version SetBrowsableProperty<T>(T obj, string strPropertyName, bool bIsBrowsable) will fail when a collection is passed as the parameter obj because T, in that case, will be an implementation of IEnumerable (e.g. List, Array etc.) and not the type of the collection that was actually intended.

2- It allows passing in the primitive types as well, which is pointless in this case and will nearly always fail.

Complete revised solution:

So here is the revised solution that tackles these problems and has worked for me: (I've slightly renamed the methods and the variables)

First of all, the actual method that does the main job of changing the Browsable attribute value:

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

Now the variant proposed in John Cummings's solution with <T>:

public static void SetBrowsableAttributeOfAProperty<T>(string propertyName, bool isBrowsable)
{
    SetBrowsableAttributeOfAProperty(typeof(T), propertyName, isBrowsable);
}

Now the overload that had the problem no. 1, but the following modification handles it now:

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

and here is a utility function to get all C# system built-in (primitive) types:

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

Usage:

 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
}
Sabah answered 30/5, 2019 at 10:49 Comment(1)
This is very clever - but it turns off/on Browsable on all instances of a class. Is there way to apply it to a specific instance?Damnedest
A
2

As an improvement on @neoikon's answer above and to avoid the exception Ganesh mentioned in the comments, here is a version that uses generics to get the type:

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

You can then also add a version that takes an instance:

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

Usage:

    class Foo
    {
        [Browsable(false)]
        public string Bar { get; set; }
    }
    void Example()
    {
        SetBrowsableProperty<Foo>("Bar", true);
        Foo foo = new Foo();
        SetBrowsableProperty(foo, "Bar", false);
    }
Apologia answered 15/12, 2017 at 16:35 Comment(1)
Besides giving this solution an up vote as it basically worked for me, I must mention that it has two problems due to the introduction of generics, that would cause it to fail. Please see the problems and a complete improved solution here: https://mcmap.net/q/638378/-conditional-quot-browsable-quot-attributeSabah
C
1

I updated @neoikon's solution. Because it was not working in .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);
}
Coheman answered 23/10, 2023 at 7:20 Comment(0)
A
0

I came across this in search of a way to declare certain members visible or hidden in IntelliSense and be able to change it once for all that needed to be hidden at compile time. I can't tell if that's what you were looking for or not, but I found an answer to my question... figured it couldn't hurt to share.

I set a conditional compilation symbol (found in the Build tab of project properties) IS_VIS (value being true if you want certain members to show, false if your want to hide them) and then:

#if IS_VIS
    public const System.ComponentModel.EditorBrowsableState isVis =
        ComponentModel.EditorBrowsableState.Always;
#else
    public const System.ComponentModel.EditorBrowsableState isVis =
        ComponentModel.EditorBrowsableState.Never;
#endif

you then reference the isVis variable in the attribute:

[EditorBrowsable(isVis)]
public string myMethod...

I did this in VB and this was hastily converted to c#. If something doesn't work right, let me know.

Aqua answered 25/10, 2012 at 21:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.