Условный атрибут «просмотра»
-
11-10-2019 - |
Вопрос
Есть ли способ сделать «просмотр» атрибута условным, поэтому свойство, которое применяется, иногда появляется на странице свойств, а иногда нет?
Благодарность :)
Решение
Нет простого способа.
Вы можете выработать это, внедрив Icustomtypedescriptor. Вот хорошая статья о Реализация icustomtypedescriptor.
Или вы можете связать свой собственный Controldesigner с вашим классом и переопределите PrefilterProperties Метод для добавления или удаления свойств, просматриваемых в сетке свойств.
Другие советы
Я не уверен, что это относится к вашей ситуации, но вы можете настроить «просмотр» украшения во время выполнения, позвонив по функции ниже.
/// <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);
}
Вы можете сделать это, предоставив пользовательскую модель типа; в простейший Уровень, вы можете предоставить пользовательский TypeDescriptor
Для вашего типа, полученного из ExpandableObjectConverter
, и просто включите/исключите данное свойство по прихоти - но это работает только с PropertyGrid
- используется страницей свойств. Более сложный подход - использовать ICustomTypeDescriptor
/ TypeDescriptionProvider
- тогда это может работать внутри таких вещей, как DataGridView
В качестве улучшения ответа @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);
}
Я наткнулся на это в поисках способа объявить определенных членов видимых или скрытых в Intellisense и иметь возможность изменить его один раз для всего, что нужно было скрыть во время компиляции. Я не могу сказать, это то, что вы искали или нет, но я нашел ответ на свой вопрос ... подумал, что это не повредит.
Я установил символ условного компиляции (найденный на вкладке «Строитель» свойств проекта) is_vis (значение истинности, если вы хотите, чтобы определенные участники показали, 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#. Если что -то не работает правильно, дайте мне знать.
Решение Джона Каммингса в основном работало для меня, но у него были следующие две проблемы из -за его представления об генериках (что было довольно умным):
1- версия SetBrowsableProperty<T>(T obj, string strPropertyName, bool bIsBrowsable)
потерпит неудачу, когда коллекция будет передана как параметр obj, потому что T, в этом случае, будет реализацией Ienumerable (например, список, массив и т. Д.), а не тип коллекции, который был фактически предназначен.
2- Это также позволяет проходить и примитивных типах, что в этом случае бессмысленно и почти всегда терпит неудачу.
Полное пересмотренное решение:
Итак, вот пересмотренное решение, которое решает эти проблемы и работает для меня: (я слегка переименовал методы и переменные)
Сначала фактический метод, который выполняет основную задачу изменения значения атрибута просмотра:
/// <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
}