Ответ 1
Объединение атрибутов может иметь смысл для контекста, который использует и интерпретирует атрибуты. Например, для тех контекстов, которые используют механизмы .Net Type Description, вы можете настроить описание типа, которое .Net возвращает потребителям.
Можно предоставлять собственные метаданные для типов, используя для этого стандартный механизм .Net, регистрируя собственный дескриптор типа для вашего объекта.
Идея будет работать таким образом, вы создаете собственный дескриптор типа для вашего типа. В дескрипторе пользовательского типа вы возвращаете дескрипторы пользовательских свойств для свойств вашего типа, а в дескрипторе свойства вы возвращаете пользовательский набор атрибутов для свойства.
Подход требует больше кода, но он действительно интересен и дает хорошее представление о том, как предоставить пользовательские метаданные для ваших типов:
Интерфейс IMetedataAttribute
Использование предоставляет стандартный способ создания MetaDataAttributes. Каждый атрибут, который реализует этот интерфейс, будет использоваться в качестве метаданных, и вместо атрибута будут использоваться те, которые он возвращает в методе Process
:
public interface IMetadatAttribute
{
Attribute[] Process();
}
Образец атрибута метаданных
Это образец атрибута метаданных, который возвращает некоторый атрибут при обработке атрибута:
public class MySampleMetadataAttribute : Attribute, IMetadatAttribute
{
public Attribute[] Process()
{
var attributes = new Attribute[]{
new BrowsableAttribute(false),
new EditorBrowsableAttribute(EditorBrowsableState.Never),
new BindableAttribute(false),
new DesignerSerializationVisibilityAttribute(
DesignerSerializationVisibility.Hidden),
new ObsoleteAttribute("", true)
};
return attributes;
}
}
Дескриптор свойства
Этот класс будет использоваться дескриптором настраиваемого типа для предоставления настраиваемого списка атрибутов для свойства:
public class MyPropertyDescriptor : PropertyDescriptor
{
PropertyDescriptor original;
public MyPropertyDescriptor(PropertyDescriptor originalProperty)
: base(originalProperty) { original = originalProperty;}
public override AttributeCollection Attributes
{
get
{
var attributes = base.Attributes.Cast<Attribute>();
var result = new List<Attribute>();
foreach (var item in attributes)
{
if(item is IMetadatAttribute)
{
var attrs = ((IMetadatAttribute)item).Process();
if(attrs !=null )
{
foreach (var a in attrs)
result.Add(a);
}
}
else
result.Add(item);
}
return new AttributeCollection(result.ToArray());
}
}
// Implement other properties and methods simply using return original
// The implementation is trivial like this one:
// public override Type ComponentType
// {
// get { return original.ComponentType; }
// }
}
Тип дескриптора
Это дескриптор типа, который предоставляет пользовательское описание для вашего типа. В этом примере он использует дескрипторы пользовательских свойств для предоставления пользовательских атрибутов, установленных для свойств вашего класса:
public class MyTypeDescriptor : CustomTypeDescriptor
{
ICustomTypeDescriptor original;
public MyTypeDescriptor(ICustomTypeDescriptor originalDescriptor)
: base(originalDescriptor)
{
original = originalDescriptor;
}
public override PropertyDescriptorCollection GetProperties()
{
return this.GetProperties(new Attribute[] { });
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
.Select(p => new MyPropertyDescriptor(p))
.ToArray();
return new PropertyDescriptorCollection(properties);
}
}
Поставщик дескрипторов типов
Этот класс будет использоваться в атрибуте над вашим типом для представления дескриптора пользовательского типа, который мы создали как механизм метаданных для типа:
public class MyTypeDescriptionProvider : TypeDescriptionProvider
{
public MyTypeDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(object))) { }
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,
object instance)
{
ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(objectType, instance);
return new MyTypeDescriptor(baseDescriptor);
}
}
Образец класса
Вот мой пример класса, его свойство Name
оформлено с использованием MySampleMetadataAttribute
, и сам класс зарегистрирован для использования нашего поставщика дескрипторов пользовательских типов:
[TypeDescriptionProvider(typeof(MyTypeDescriptionProvider))]
public class MySampleClass
{
public int Id { get; set; }
[MySampleMetadataAttribue]
[DisplayName("My Name")]
public string Name { get; set; }
}
Чтобы увидеть результат, достаточно создать экземпляр класса и увидеть результат в PropertyGrid
:
var o = new MySampleClass();
this.propertyGrid1.SelectedObject = o;
Некоторые примечания к ответу
- Возможно, это не так просто, как вы ожидали для такой задачи. Но это работает.
- Это длинный ответ, но содержит полный рабочий пример того, как вы можете применять дескрипторы типов к вашим типам для предоставления пользовательских метаданных.
- Подход не будет работать для двигателей, которые используют отражение вместо описания типа. Но он полностью работает, например, с элементом управления
PropertyGrid
, который работает с описанием типа.