Язык XAML и пользовательские классы
Обновлен: Ноябрь 2007
Язык XAML (Extensible Application Markup Language) поддерживает возможность определения пользовательского класса или структуры на любом языке среда CLR (common language runtime) и получения доступа к этому классу с помощью разметки XAML, в том числе с помощью сочетания XAML, определенного Windows Presentation Foundation (WPF), и тегов пользовательского класса XAML в одном и том же файле разметки. В этом разделе обсуждаются требования, которым должен соответствовать пользовательский класс, чтобы использоваться в качестве элемента XAML.
В этом разделе содержатся следующие подразделы.
- Пользовательские классы в приложениях или сборках
- Требования к пользовательскому классу как элементу XAML
- Требования к свойствам пользовательского класса как атрибутам XAML
- Требования к синтаксису атрибутов обработчиков событий XAML пользовательского класса
- Написание свойств коллекции
- Объявление свойств содержимого XAML
- Сериализация XAML
- Связанные разделы
Пользовательские классы в приложениях или сборках
Пользовательские классы, используемые в XAML, можно определить двумя способами: в коде программной части или другом коде, который создает основное приложение Windows Presentation Foundation (WPF), или как класс в отдельной сборке, например как исполняемый файл или DLL, используемые в качестве библиотеки классов. Каждый из этих подходов имеет определенные преимущества и недостатки.
Преимуществом создания библиотеки классов является то, что все подобные пользовательские классы могут совместно использоваться несколькими различными приложениями. Отдельная библиотека также упрощает управление версиями приложений и упрощает создание класса, который предполагается использовать в качестве корневого элемента на странице XAML.
Преимуществом определения пользовательских классов в приложении является то, что этот способ является относительно легковесным и сводит к минимуму проблемы развертывания и тестирования, возникающие при введении отдельных сборок за пределами главного исполняемого файла. Однако, важным недостатком является невозможность использования классов, определенных в той же сборке в качестве корневого элемента страницы XAML.
Независимо от того, определены ли пользовательские классы в одной или разных сборках, они должны быть распределены между пространством имен CLR и пространством имен XML, чтобы их можно было использовать в XAML в качестве элементов. См. раздел Пространства имен XAML и сопоставление пространств имен.
Требования к пользовательскому классу как элементу XAML
Чтобы обеспечить себе возможность создания в качестве объектного элемента, класс должен удовлетворять следующим требованиям:
Пользовательский класс должен быть открытым и должен поддерживать открытый конструктор по умолчанию (без параметров). (Структуры, управляемые кодом, неявно поддерживают такой конструктор.)
Пользовательский класс не должен быть вложенным классом (вложенные классы и "точка" в их синтаксисе конфликтует с другими свойствами WPF, например с вложенными свойствами зависимостей).
В дополнение к разрешению синтаксиса элемента объекта, синтаксис элемента свойства также позволяется для всех других открытых свойств, которые принимают объект в качестве типа значения. Это происходит потому, что объект теперь может быть создан в качестве элемента объекта и может заполнить значение элемента такого свойства.
структур;
Структуры, определяемые как настраиваемые типы, всегда следует создавать в XAML в WPF. Причина заключается в том, что компиляторы CLR неявным образом создают для структуры конструктор по умолчанию, который инициализирует все свойства значениями по умолчанию. В некоторых случаях поведение конструктора по умолчанию или использование элементов объекта для структуры является нежелательным. Это возможно в тех случаях, когда структура используется в качестве объединения, в котором хранятся взаимоисключающие значения, и поэтому ни одному из свойств нельзя присвоить значение. Примером такой структуры в WPF является GridLength. Как правило, в таких структурах необходимо реализовать преобразователь типов, чтобы значения можно было представить в виде атрибутов, используя преобразования строк для создания различных интерпретаций или режимов значений структуры; в структуре такое поведение для создания кода должно быть реализовано через нестандартный конструктор.
Требования к свойствам пользовательского класса как атрибутам XAML
Свойства должны ссылаться на тип, передаваемый по значению (например, примитив), или использовать класс для типа, имеющего конструктор по умолчанию или преобразователь выделенного типа на уровне класса.
Помимо этого, свойство может ссылаться на абстрактный тип класса или интерфейс. Для абстрактных классов или интерфейсов ожидаемый результат во время выполнения заключается в том, что значение свойства должно быть заполнено практическими экземплярами класса, которые реализуют интерфейс, или экземплярами класса, производными от абстрактного класса.
Свойства можно объявить в абстрактном классе, однако их значения можно устанавливать только в практических классах, производных от абстрактного, поскольку для создания элемента объекта для класса принципиально требуется открытый конструктор по умолчанию.
Синтаксис включенного атрибута преобразователя типов
Если поддерживается выделенный атрибутивный преобразователь типов на уровне класса, то применяемый тип преобразования включает синтаксис атрибута для любого свойства, для которого необходимо создать экземпляр этого типа. Преобразователь типа не включает использование элемента объекта типа — только наличие конструктора по умолчанию для данного типа развешает использование элемента объекта. Таким образом, свойства, разрешенные преобразователем типов, обычно не используются в синтаксисе свойств, если только сам тип не поддерживает синтаксис элемента объекта. Исключением из этого является указание синтаксиса элемента свойства при наличии элемента свойства, содержащего строку. На самом деле, такое использование эквивалентно использованию синтаксиса атрибута, и оно не часто применяется, если нет необходимости в более надежной обработке пробелов в значении атрибута. Например, ниже приведено использование элемента свойства, который принимает строку, а использование атрибута эквивалентно:
<Button>Hallo!
<Button.Language>
de-DE
</Button.Language>
</Button>
<Button Language="de-DE">Hallo!</Button>
Примерами свойств, где синтаксис атрибута разрешен, но синтаксис элемента свойства, содержащего элемент объекта, запрещен XAML, являются различные свойства, принимающие тип Cursor. Класс Cursor имеет выделенный преобразователь типа CursorConverter, но не предоставляет конструктор по умолчанию, поэтому свойство Cursor может быть установлено только через синтаксис атрибута, даже если фактический тип Cursor является ссылочным типом.
Преобразователь типа каждого свойства
В качестве альтернативы само свойство может объявлять преобразователь типов на уровне свойств. Это допускает "мини-язык", который создает объекты типа встроенного свойства путем обработки входящих строковых значений атрибута в качестве входных данных для операции ConvertFrom, основанной на соответствующем типе. Обычно это делается для предоставления удобного доступа, а не как отдельное средство для установки свойства в XAML. Кроме того, можно также использовать преобразователь типов для атрибутов, если необходимо использовать существующие типы CLR, которые не предоставляют конструктор по умолчанию или преобразователь типов атрибутов. Примерами в WPF API-интерфейсы являются определенные свойства, принимающие тип CultureInfo. В этом случае WPF использовал существующий тип Microsoft .NET Framework CultureInfo для лучшей совместимости адресов и сценариев миграции, которые использовались в предыдущих версиях среды, но тип CultureInfo не поддерживал необходимые конструкторы или преобразования типов на уровне типа, непосредственно используемых в качестве значения свойства XAML.
При каждом предоставлении свойства, использующего XAML (особенно в том случае, если вы являетесь автором элемента управления), вам следует принять во внимание необходимость резервного копирования этого свойства с помощью свойства зависимостей. Это особенно важно, если используется существующая реализация Windows Presentation Foundation (WPF) обработчика XAML, так как с помощью резервного копирования DependencyProperty можно повысить производительность. Свойство зависимости предоставит возможности системы свойств для данного свойства, так что пользователям будет поставлено доступное свойство XAML. В это число входят такие возможности как анимация, привязка данных и поддержка стилей. Дополнительные сведения см. в разделах Пользовательские свойства зависимостей и Загрузка кода XAML и свойства зависимостей.
Написание и установка атрибутов преобразователя типов
Часто возникает необходимость написания пользовательского производного класса TypeConverter для предоставления преобразования для типа свойства. Информация о производных классах, создании преобразователя типов, поддерживающего использование XAML, и способах применения TypeConverterAttribute содержится в разделе TypeConverters и XAML.
Требования к синтаксису атрибутов обработчиков событий XAML пользовательского класса
Чтобы использоваться в качестве события CLR, событие должно быть предоставлено в качестве открытого события класса, поддерживающего конструктор по умолчанию, или абстрактного класса, где событие может быть доступно из производных классов. Чтобы событие CLR могло использоваться как перенаправленное событие, оно должно явным образом реализовывать методы add и remove, которые добавляют и удаляют обработчики для подписи события CLR и направляют эти обработчики в методы AddHandler и RemoveHandler. Эти методы добавляют или удалят обработчики из хранилища обработчиков перенаправленных событий экземпляра, к которому присоединено событие.
![]() |
---|
Возможно регистрировать обработчики непосредственно для маршрутизируемых событий с помощью AddHandler, и намеренно не определять событие CLR, которое предоставляет перенаправленное событие. Обычно это не рекомендуется, так как событие не включит синтаксис атрибута XAML для присоединяющих обработчиков, а результирующий класс предложит менее прозрачное представление XAML модели объектов класса. |
Написание свойств коллекции
Свойства, принимающие тип коллекции, имеют синтаксис XAML, который позволяет определять объекты, добавляемые в коллекцию. Этот синтаксис имеет две важные функции.
Объект, являющийся объектом коллекции, необязательно определять в синтаксисе элемента объекта. Присутствие этого типа коллекции подразумевается всякий раз, когда указывается свойство в XAML, принимающее тип коллекции.
Дочерние элементы свойства коллекции обрабатываются для того, чтобы стать членами коллекции. Обычно доступ из кода к членам коллекции осуществляется через методы коллекции (например Add) или через свойство индексатора коллекции. Но синтаксис XAML не поддерживает методы или индексаторы. Коллекции являются очевидно очень общим требованием для построения дерева элементов, и требуется какой-нибудь способ заполнения этих коллекций в декларативном XAML. Таким образом, дочерние элементы свойства коллекции обрабатываются путем добавления их в коллекцию, которая является значением типа свойства коллекции.
Обработчик WPF XAML использует следующее определение для того, что составляет свойство коллекции. Тип свойства должен реализовывать одно из нижеперечисленного:
Реализовывать объект IList.
Реализовывать IDictionary или универсальный эквивалент (IDictionary<TKey, TValue>).
Происходить из объекта Array (дополнительные сведения о массивах в XAML см. в разделе Расширение разметки x:Array.)
Реализовывать IAddChild (интерфейс определяется WPF).
Каждый из этих типов имеет метод Add, который используется обработчиком XAML для добавления элементов в основную коллекцию.
![]() |
---|
Универсальные интерфейсы List и Dictionary (IList<T> и IDictionary<TKey, TValue>) не поддерживаются при обнаружении коллекций обработчиком WPF XAML. Тем не менее можно использовать класс List<T> как базовый класс, так как он непосредственно реализует IList или Dictionary<TKey, TValue> как базовый класс, потому что он непосредственно реализует IDictionary. |
При объявлении свойства, принимающего коллекцию, будьте осторожны при инициализации значения свойства в новых экземплярах типа. Если свойство не реализуется как свойство зависимости, то со свойством достаточно использовать резервное поле, вызывающее конструктор типа коллекции. Если свойство является свойством зависимости, то может потребоваться инициалиализация свойства коллекции как части конструктора типа по умолчанию. Это обусловлено тем, что свойство зависимости принимает по умолчанию значение из метаданных и обычно нежелательно, чтобы начальное значение свойства коллекции было статической общей коллекцией (экземпляр коллекции должен быть для каждого экземпляра, содержащего тип). Дополнительные сведения см. в разделе Пользовательские свойства зависимостей.
Можно реализовать пользовательский тип коллекции для свойства коллекции. Из-за неявной обработки свойства коллекции пользовательскому типу коллекции не требуется предоставлять конструктор по умолчанию, который будет использоваться в XAML неявно. Однако, при необходимости можно предоставить конструктор по умолчанию для типа коллекции. Это может быть полезным приемом, поскольку, если не предоставить конструктор по умолчанию, невозможно явно объявить коллекцию в качестве элемента объекта. Некоторые авторы разметок могут предпочесть просмотр явной коллекции в качестве стиля разметки. Кроме того, конструктор по умолчанию может уменьшить требования к инициализации при создании новых объектов, использующих тип коллекций в качестве значения свойства.
Объявление свойств содержимого XAML
Язык XAML определяет концепцию свойства содержимого XAML. Каждый класс, используемый в синтаксисе объекта, может иметь только одно свойство содержимого XAML. Чтобы объявить свойство в качестве свойства содержимого XAML для класса, следует применить ContentPropertyAttribute как часть определения класса. Укажите имя предполагаемого свойства содержимого XAML как Name в атрибуте.
Можно определить свойство коллекции как свойство содержимого XAML. Это отражается на использовании этого свойства, так как элемент объекта может иметь один или несколько дочерних элементов без промежуточных элементов объектов коллекции или тегов элементов свойства. Эти элементы затем рассматриваются как значение для свойства содержимого XAML и добавляются к экземпляру коллекции резервного копирования.
Некоторые существующие свойства содержимого WPF XAML используют тип свойства Object. Это допускает использование свойства содержимого XAML, которое может принимать как простые значения (например String) так и значение ссылки на объект. Если следовать этой модели, то тип будет отвечать как за определение типа, так и за обработку возможных типов. Типичной причиной построения модели типа Object является поддержка как простых средств добавления содержимого объекта в виде строки (которая получает обработку представления по умолчанию), так и улучшенных средств для добавления содержимого объекта, который определяет нестандартное представление.
Сериализация XAML
Для некоторых сценариев (например, если вы являетесь автором элемента управления) следует убедиться, что любое представление объекта, которое может быть создано в XAML, также может быть обратно сериализовано обратно в эквивалентный XAML. Требования сериализации не описаны в этом разделе. См. разделы Общие сведения о разработке управления и Дерево элементов и сериализация.
См. также
Основные понятия
Пользовательские свойства зависимостей
Общие сведения о разработке управления