logo
03

Использование атрибутов

Дизайнер форм не знает предназначения свойства вашего контрола - предназначено ли оно для дизайнера или только для работы из кода, и какое у него значение по умолчанию. Поэтому всегда размечайте каждое публичное свойство соответствующими атрибутами Category, Description, DefaultValue, Browsable, DesignerSerializationVisibility. При отсутствии некоторых из этих атрибутов среда самостоятельно предполагает некоторое значение по умолчанию, но тем не менее лучше размечать все свойства, поскольку в какой-либо другой среде, другом дизайнере форм или вообще отдельном приложении используемые по умолчанию значения могут быть совершенно другими. Краткое описание этих атрибутов:

Помечайте свойства атрибутом DefaultValue, чтобы сообщить дизайнеру форм о значении по умолчанию. Если значение по умолчанию вашего свойства не может быть сохранено в метаданных сборки (т.е. это не простой тип, не строка и не Type), то вы не сможете применить атрибут DefaultValue напрямую. Если у вас есть TypeConverter для типа свойства, и он может преобразовать строку к экземпляру нужного типа, то атрибут DefaultValue можно использовать в виде [DefaultValue(Type, string)], например, для свойства типа Size можно использовать такой вариант: [DefaultValue(typeof(Size), "32;32")]. Учтите, что при этом всегда используется Invariant Culture.

Если вы не можете использовать атрибут DefaultValue из-за отсутствия TypeConverter или нетривиальной логики, можно реализовать bool ShouldSerializeNNNN() и void ResetNNNN(), где NNNN – это имя свойства. ShouldSerialize должен возвращать true, если значение свойства отлично от значения по умолчанию. Reset должен вернуть свойство к значению по умолчанию. Дизайнер, обнаружив эти методы и не обнаружив атрибута DefaultValue, будет использовать их по необходимости. Следите за тем, чтобы значение по умолчанию действительно выставлялось в конструкторе. Эти методы не обязаны быть публичными, потому что среда использует их через рефлексию (reflection). Тем не менее, часто их выгодно делать внутренними (internal) для последующего использования в TypeConverter.

Используйте атрибут DefaultProperty у класса контрола для того, чтобы сказать дизайнеру, какое свойство является главным. При выборе вашего контрола такое свойство будет сделано текущим. Учтите, что если вы редактировали свойство одного контрола, а потом переключились на другой контрол, в котором есть свойство с таким же именем, дизайнер сохранит текущее свойство независимо от атрибута DeafultProperty. Аналогично выставляйте атрибут DefaultEvent у класса, указывая основное событие, например Click. При двойном щелчке на контроле в режиме дизайна автоматически будет создан (или открыт, если уже создан) обработчик этого события.

Если некоторый метод или свойство являются потенциально опасными и требуют дополнительных знаний об использовании, имеет смысл пометить такой член класса атрибутом [EditorBrowsable(EditorBrowsableState.Advanced)]. В таком случае его увидят в intellisense только те пользователи, у которых в свойствах Visual Studio сброшен флажок Hide advanced members. Можно и вообще спрятать свойство, используя [EditorBrowsable(EditorBrowsableState.Never)]. Например, в классе Control есть метод ResetBackColor, являющийся публичным и виртуальным. Однако он не подсказывается редактором, и набрать его можно только вручную.

Если некоторый класс является компонентом (реализует интерфейс IComponent), а вы не хотите, чтобы он появлялся под редактором формы в области компонентов (component tray), используйте атрибут [DesignTimeVisible(false)].

Чтобы сообщить среде, что вы хотите видеть свой класс на панели доступных контролов и компонентов (Tool Box), используйте атрибут ToolboxItem(true). Задать картинку для контрола можно с помощью атрибута ToolboxBitmap. Например, пометка атрибутом ToolboxBitmap(typeof(MyControl)] приведет к поиску ресурса с именем namespace.MyControl.bmp в сборке с типом MyControl и использование этой картинки на панели инструментов. Картинка должна быть 16-цветной, размером 16х16 пикселей. Цвет левого нижнего пикселя будет считаться прозрачным. Если вы хотите использовать иконку, а не bmp-файл, вам придётся указать это явно, используя вариант атрибута: [ToolboxBitmap(typeof(MyControl), "ToolboxIcons.MyControl.ico")].

Если некоторое свойство должно наследовать своё значение от контейнера, иначе говоря, быть прозрачным при некоторых условиях, используйте атрибут AmbientValue вместо DefaultValue. Это сообщит дизайнеру, знакомому со структурой объектов, что реальное значение нужно брать у контейнера. Это особенно важно использовать для локализуемых свойств, потому что иначе для них будет сгенерирован код чтения из ресурсов. Например, атрибут [AmbientValue(null)] указывает на то, что при выставлении свойства в null контрол начинает использовать аналогичное свойство родителя. Примерами таких свойств могут быть Font или BackColor. Учтите, что, несмотря на этот атрибут в коде getter-а, всё равно необходимо опрашивать родителя самостоятельно из кода геттера (getter) свойства, дизайнер просто учитывает это при отображении значений в PropertyGrid и генерации кода в InitializeComponent.

Если изменение некоторого свойства влечет за собой изменение значения другого свойства, необходимо сообщить об этом системе дизайна. Для этого используется атрибут RefreshProperties. Например, если свойство помечено [RefreshProperties(RefreshProperties.All)], то при его изменении все свойства данного компонента будут заново опрошены, и вся информация обновлена.

Если необходимо отображать свойство как «специальное», т.е. с круглыми скобками вокруг имени, наподобие (Name) или (DataBindings), используйте атрибут [ParenthesizeProperty(true)]. Не стоит злоупотреблять этой возможностью, поскольку такие свойства выбиваются из общей сортировки по именам – они отображаются в самом верху окна свойств (PropertyGrid) или соответствующей категории.