Условный «просматриваемый» атрибут - PullRequest
17 голосов
/ 14 января 2011


Есть ли способ сделать атрибут «Browsable» условным, чтобы применяемое свойство иногда отображалось на странице свойств, а иногда нет?
спасибо:)

Ответы [ 6 ]

11 голосов
/ 19 ноября 2013

Я не уверен, что это применимо к вашей ситуации, но вы можете настроить оформление "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);
}
7 голосов
/ 14 января 2011

Легкого пути нет.

Вы можете решить эту проблему, внедрив ICustomTypeDescriptor. Вот хорошая статья о реализации ICustomTypeDescriptor .

Или вы можете связать свой собственный ControlDesigner с вашим классом и переопределить метод PreFilterProperties для добавления или удаления свойств, просматриваемых в сетке свойств.

Удаление определенных свойств из сетки свойств.

6 голосов
/ 14 января 2011

Вы можете сделать это, предоставив собственную модель типа;на простейшем уровне вы можете предоставить пользовательский TypeDescriptor для вашего типа, производного от ExpandableObjectConverter, и просто включить / исключить данное свойство прихоти - но это работает только с PropertyGrid - используетсястраница свойств.Более сложный подход заключается в использовании ICustomTypeDescriptor / TypeDescriptionProvider - тогда это может работать внутри таких вещей, как DataGridView

2 голосов
/ 15 декабря 2017

В качестве улучшения ответа @ neoikon выше и во избежание исключения, упомянутого в комментариях Ганешем, вот версия, в которой для получения типа используются обобщенные значения:

    /// <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);
    }
0 голосов
/ 30 мая 2019

Решение Джона Каммингса в основном сработало для меня, но у него были следующие две проблемы из-за его введения Обобщения (хотя это было довольно умно):

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
}
0 голосов
/ 26 октября 2012

Я столкнулся с этим в поисках способа объявить определенные элементы видимыми или скрытыми в 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 #. Если что-то не работает, дайте мне знать.

...