Поделиться через


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

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

В этом разделе содержатся следующие подразделы.

  • Пользовательские классы в приложениях или сборках
  • Требования к пользовательскому классу как элементу XAML
  • Требования к свойствам пользовательского класса как атрибутам XAML
  • Требования к синтаксису атрибутов обработчиков событий XAML пользовательского класса
  • Написание свойств коллекции
  • Объявление свойств содержимого XAML
  • Сериализация XAML
  • Связанные разделы

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

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

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

  • Преимущество определения пользовательских классов в приложении состоит в том, что этот способ является относительно простым и сводит к минимуму проблемы развертывания и тестирования, возникающие при введении отдельных сборок за пределами главного исполняемого файла приложения. Однако существенным недостатком является невозможность использования классов, определенных в той же сборке, в качестве корневого других страниц 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  APIs являются некоторыми свойствами, которые принимают тип 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. Таким образом, дочерние элементы свойства коллекции обрабатываются путем добавления их в коллекцию, которая является значением типа свойства коллекции.

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

Каждый из этих типов в среде CLR имеет метод 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. Требования сериализации не описаны в этом разделе. См. разделы Общие сведения о разработке управления и Дерево элементов и сериализация.

См. также

Основные понятия

Общие сведения о языке XAML (WPF)

Пользовательские свойства зависимостей

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

Общие сведения о базовых элементах

Загрузка кода XAML и свойства зависимостей