Код XAML и пользовательские классы для WPF

Реализация языка XAML в платформах общеязыковой среды выполнения (CLR) поддерживает возможность определения пользовательского класса или структуры на любом языке среды CLR и последующего доступа к этому классу с помощью разметки XAML. В одном файле разметки можно использовать сочетание определенных в Windows Presentation Foundation (WPF) типов и пользовательских типов, обычно сопоставляя пользовательские типы с префиксом пространства имен XAML. В этом разделе обсуждаются требования, которым должен соответствовать пользовательский класс, чтобы его можно было использовать в качестве элемента XAML.

Пользовательские классы в приложениях или сборках

Пользовательские классы, используемые в XAML, можно определить двумя способами: в коде программной части или другом коде, который создает основное приложение 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 обработчика XAML, так как с помощью резервирования DependencyProperty можно повысить производительность. Свойство зависимости предоставит возможности системы свойств для данного свойства, которые пользователи ожидают от доступного в XAML свойства. В число этих возможностей входят анимация, привязка данных и поддержка стилей. Дополнительные сведения см. в разделах Пользовательские свойства зависимостей и Загрузка кода XAML и свойства зависимостей.

Написание и установка атрибутов преобразователя типов

Иногда бывает необходимо написать пользовательский производный класс TypeConverter, чтобы предоставить преобразование для типа свойства. Инструкции по наследованию и созданию преобразователя типов, поддерживающего использование XAML, и способах применения TypeConverterAttribute см. в разделе TypeConverters и XAML.

Требования к синтаксису атрибутов обработчиков событий XAML пользовательского класса

Чтобы событие можно было использовать в качестве события СДК, оно должно быть предоставлено в качестве открытого события класса, поддерживающего конструктор без параметров, или абстрактного класса, где событие может быть доступно в производных классах. Чтобы событие 0СДК могло использоваться как перенаправленное событие, оно должно явным образом реализовывать методы add и remove, которые добавляют и удаляют обработчики для сигнатуры события CLR и направляют эти обработчики в методы AddHandler и RemoveHandler. Эти методы добавляют или удаляют обработчики из хранилища обработчиков перенаправленных событий экземпляра, к которому присоединено событие.

Примечание.

Возможно регистрировать обработчики непосредственно для перенаправленных событий с помощью AddHandler и намеренно не определять событие CLR, которое предоставляет перенаправленное событие. Обычно это не рекомендуется, так как событие не включит синтаксис атрибута XAML для подключения обработчиков и результирующий класс предложит менее прозрачное представление XAML возможностей этого типа.

Написание свойств коллекции

Свойства, принимающие тип коллекции, имеют синтаксис XAML, который позволяет определять объекты, добавляемые в коллекцию. Этот синтаксис имеет две важные функции.

  • Объект, являющийся объектом коллекции, необязательно определять в синтаксисе объектного элемента. Присутствие этого типа коллекции подразумевается всякий раз, когда в XAML указывается свойство, принимающее тип коллекции.

  • Дочерние элементы свойства коллекции в разметке обрабатываются для того, чтобы они стали элементами коллекции. Обычно доступ кода к элементам коллекции осуществляется через методы списка или словаря, такие как Add, или через индексатор. Но синтаксис XAML не поддерживает методы или индексаторы. (Исключением является версия XAML 2009, которая поддерживает методы, но ее применение ограничивает возможные способы использования WPF. См. раздел Возможности языка XAML 2009.) Коллекции, очевидно, являются очень общим требованием для построения дерева элементов, и требуется какой-нибудь способ заполнения этих коллекций в декларативном XAML. Таким образом, дочерние элементы свойства коллекции обрабатываются путем добавления их в коллекцию, которая является значением типа свойства коллекции.

Реализация служб XAML в .NET Framework и, следовательно, процессор XAML WPF используют указанное ниже определение того, что составляет свойство коллекции. Тип свойства должен реализовывать один из следующих интерфейсов:

Каждый из этих типов в среде CLR имеет метод Add, который используется процессором XAML для добавления элементов в базовую коллекцию при создании графа объекта.

Примечание.

Универсальные интерфейсы списков и словарей List и Dictionary (IList<T> и IDictionary<TKey,TValue>) для обнаружения коллекции не поддерживаются обработчиком XAML для WPF. Но можно в качестве базового использовать класс 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. Требования к сериализации не описываются в этом разделе. См. разделы Общие сведения о разработке элементов управления и Дерево элементов и сериализация.

См. также