Код XAML и пользовательские классы для WPF
Реализация языка XAML в платформах CLR поддерживает возможность определения пользовательского класса или структуры на любом языке CLR и последующего доступа к этому классу с помощью разметки XAML. В одном файле разметки можно использовать сочетание определенных в Windows Presentation Foundation (WPF) типов и пользовательских типов, обычно сопоставляя пользовательские типы с префиксом пространства имен XAML. В этом разделе обсуждаются требования, которым должен соответствовать пользовательский класс, чтобы его можно было использовать в качестве элемента XAML.
Пользовательские классы в приложениях или сборках
Пользовательские классы, используемые в XAML, можно определить двумя способами: в коде программной части или другом коде, который создает основное приложение Windows Presentation Foundation (WPF), или как класс в отдельной сборке, например как исполняемый файл или библиотеку DLL, используемые в качестве библиотеки классов. Каждый из этих подходов имеет определенные преимущества и недостатки.
Преимуществом создания библиотеки классов является то, что все подобные пользовательские классы могут совместно использоваться несколькими различными приложениями. Отдельная библиотека также облегчает управление версиями приложений и упрощает создание класса, который предполагается использовать в качестве корневого элемента на странице XAML.
Преимущество определения пользовательских классов в приложении состоит в том, что этот способ является относительно простым и сводит к минимуму проблемы развертывания и тестирования, возникающие при введении отдельных сборок за пределами главного исполняемого файла приложения.
Независимо от того, определены ли пользовательские классы в одной или разных сборках, они должны быть распределены между пространством имен CLR и пространством имен XML, чтобы их можно было использовать в XAML в качестве элементов. См. раздел Пространства имен XAML и сопоставление пространств имен для WPF XAML.
Требования к пользовательскому классу как элементу XAML
Чтобы можно было создавать экземпляр класса в качестве объектного элемента, класс должен удовлетворять указанным ниже требованиям.
Пользовательский класс должен быть открытым и должен поддерживать открытый конструктор по умолчанию (без параметров). (Примечания о структурах см. в следующем разделе.)
Пользовательский класс не должен быть вложенным. Вложенные классы и "точка" в их общем синтаксисе использования в среде CLR конфликтуют с другими функциями WPF и XAML, такими как присоединенные свойства.
В дополнение к разрешению синтаксиса объектных элементов определение объекта также разрешает синтаксис элемента свойства для всех других открытых свойств, которые принимают объект в качестве типа значения. Это происходит потому, что экземпляр объекта теперь может быть создан в качестве объектного элемента и может заполнить значение элемента свойства.
Структуры
Структуры, определяемые как настраиваемые типы, всегда следует создавать в XAML в WPF. Причина заключается в том, что компиляторы CLR неявным образом создают для структуры конструктор по умолчанию, который инициализирует все свойства со значениями по умолчанию. В некоторых случаях поведение конструктора по умолчанию или использование объектных элементов для структуры является нежелательным. Это возможно в тех случаях, когда структура используется для заполнения значений и функционирует в качестве объединения, в котором хранятся взаимоисключающие значения, и поэтому ни одному из свойств нельзя присвоить значение. Объект WPF Примером такой структуры является GridLength. Как правило, в таких структурах необходимо реализовать преобразователь типов, чтобы значения можно было представить в виде атрибутов, используя преобразования строк для создания различных интерпретаций или режимов значений структуры. Структура также должна реализовывать аналогичное поведение для конструкции кода с помощью конструктора, не являющегося конструктором по умолчанию.
Требования к свойствам пользовательского класса как атрибутам XAML
Свойства должны ссылаться на тип, передаваемый по значению (такой как примитив), или использовать класс для типа, имеющего конструктор по умолчанию или преобразователь выделенного типа, к которому может получить доступ процессор XAML. В реализации XAML среды CLR процессоры XAML находят такие преобразователи с помощью собственной поддержки примитивов языка либо путем применения TypeConverterAttribute для типа или члена в определениях резервных типов.
Помимо этого, свойство может ссылаться на абстрактный тип класса или интерфейс. Для абстрактных классов или интерфейсов ожидаемый результат синтаксического анализа XAML заключается в том, что значение свойства должно быть заполнено практическими экземплярами класса, которые реализуют интерфейс, или экземплярами типов, которые являются производными от абстрактного класса.
Свойства могут объявляться в абстрактном классе, но могут быть заданы только в практических классах, производных от абстрактных классов. Это объясняется тем, что для создания объектного элемента для класса в целом требуется открытый конструктор по умолчанию в классе.
Синтаксис атрибута с поддержкой TypeConverter
Если поддерживается выделенный преобразователь типов атрибутов на уровне класса, то применяемый тип преобразования включает синтаксис атрибута для любого свойства, для которого необходимо создать экземпляр этого типа. Преобразователь типа не включает использование объектного элемента типа — только наличие конструктора по умолчанию для данного типа разрешает использование объектного элемента. Таким образом, свойства, разрешенные преобразователем типов, обычно не используются в синтаксисе свойств, если только сам тип не поддерживает синтаксис объектных элементов. Исключением из этого является указание синтаксиса элемента свойства при наличии элемента свойства, содержащего строку. Это использование действительно фактически эквивалентна использованию синтаксиса атрибута и такое использование не часто, если не существует потребность в более надежную обработку пробела значение атрибута. Например, ниже показано использование элемента свойства, который принимает строку, а использование атрибута эквивалентно.
<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 методы. Эти методы добавляют или удаляют обработчики из хранилища обработчиков перенаправленных событий экземпляра, к которому присоединено событие.
Note
Можно регистрировать обработчики непосредственно для перенаправленных событий с помощью AddHandlerи намеренно не определять CLR событие, которое предоставляет перенаправленное событие. Обычно это не рекомендуется, так как событие не включит синтаксис атрибута XAML для подключения обработчиков и результирующий класс предложит менее прозрачное представление XAML возможностей этого типа.
Написание свойств коллекции
Свойства, принимающие тип коллекции, имеют синтаксис XAML, который позволяет определять объекты, добавляемые в коллекцию. Этот синтаксис имеет две важные функции.
Объект, являющийся объектом коллекции, необязательно определять в синтаксисе объектного элемента. Присутствие этого типа коллекции подразумевается всякий раз, когда в XAML указывается свойство, принимающее тип коллекции.
Дочерние элементы свойства коллекции в разметке обрабатываются для того, чтобы они стали элементами коллекции. Обычно доступ кода к элементам коллекции осуществляется через методы списка или словаря, такие как
Add
, или через индексатор. Но синтаксис XAML не поддерживает методы или индексаторы (исключение: XAML 2009 может поддерживать методы, но с помощью XAML 2009 ограничивает возможные способы использования WPF; см. в разделе возможности языка XAML 2009). Коллекции, очевидно, являются очень общим требованием для построения дерева элементов, и требуется какой-нибудь способ заполнения этих коллекций в декларативном XAML. Таким образом, дочерние элементы свойства коллекции обрабатываются путем добавления их в коллекцию, которая является значением типа свойства коллекции.
Реализация служб XAML в .NET Framework и, следовательно, процессор XAML WPF используют указанное ниже определение того, что составляет свойство коллекции. Тип свойства должен реализовывать один из следующих интерфейсов:
Реализует IList.
Реализует IDictionary или универсальный эквивалент (IDictionary<TKey,TValue>).
Является производным от Array (Дополнительные сведения о массивах в XAML см. в разделе расширение разметки x: Array.)
Реализует IAddChild (интерфейс определяется WPF).
Каждый из этих типов в среде CLR имеет метод Add
, который используется процессором XAML для добавления элементов в базовую коллекцию при создании графа объекта.
Note
Универсальный 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 в атрибуте. Свойство задается как строка по имени, не как конструкция отражения таких как PropertyInfo.
Можно определить свойство коллекции как свойство содержимого XAML. Это отражается на использовании этого свойства, так как объектный элемент может иметь один или несколько дочерних элементов без промежуточных объектных элементов коллекции или тегов элементов свойства. Эти элементы затем рассматриваются как значение для свойства содержимого XAML и добавляются к резервному экземпляру коллекции.
Некоторые существующие свойства содержимого XAML используют тип свойства Object
. Это позволяет XAML, содержимого, свойство, которое может принимать как простые значения, такие как String так и значение ссылки на объект. Если следовать этой модели, то тип будет отвечать как за определение типа, так и за обработку возможных типов. Типичной причиной Object содержимого типа является поддержка как простых средств добавления содержимого объекта в виде строки (которая получает обработку представления по умолчанию) или улучшенных средств для добавления содержимого объекта, который задает нестандартное представление или Дополнительные данные.
Сериализация XAML
В некоторых сценариях (например, если вы являетесь автором элемента управления) следует убедиться в том, что любое представление объекта, которое может быть создано в XAML, также может быть сериализовано обратно в эквивалентную разметку XAML. Требования к сериализации не описываются в этом разделе. См. разделы Общие сведения о разработке элементов управления и Дерево элементов и сериализация.