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


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

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

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

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

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

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

  • Для использования пользовательских классов в XAML в качестве элементов необходимо сопоставить их между пространством имен CLR и пространством имен XML, независимо от того, определены ли они в той же сборке или в другой. См. раздел о пространствах имен XAML и сопоставлении пространств имен для WPF XAML.

Требования к кастомному классу в качестве элемента XAML

Чтобы можно было создать экземпляр класса как объект, ваш класс должен соответствовать следующим требованиям:

  • Пользовательский класс должен быть общедоступным и поддерживать общедоступный конструктор по умолчанию (без параметров). (См. следующий раздел для примечаний о структурах.)

  • Пользовательский класс не должен быть вложенным. Вложенные классы и точка в их общем синтаксисе использования CLR влияют на другие функции WPF и/или XAML, такие как присоединенные свойства.

Помимо включения синтаксиса элемента объекта, определение объекта также включает синтаксис элемента свойства для любых других общедоступных свойств, которые принимают этот объект в качестве типа значения. Это связано с тем, что объект теперь может быть создан в качестве элемента типа 'объект' и может заполнить значение для элемента такого свойства.

Структуры

Структуры, которые вы определяете как пользовательские типы, всегда могут быть созданы в XAML в WPF. Это связано с тем, что компиляторы CLR неявно создают конструктор без параметров для структуры, что инициализирует все значения свойств по умолчанию. В некоторых случаях поведение построения по умолчанию и (или) использование элемента объекта для структуры нежелательно. Это может быть связано с тем, что структура предназначена для заполнения значений и функций концептуально в виде объединения, где содержащиеся значения могут иметь взаимоисключающие интерпретации и поэтому ни один из его свойств не задан. Пример такой структуры WPF — GridLength. Как правило, такие структуры должны реализовать преобразователь типов, чтобы значения могли быть выражены в форме атрибута, используя строковые соглашения, которые создают различные интерпретации или режимы значений структуры. Структура также должна предоставлять аналогичное поведение для построения кода с помощью конструктора без параметров.

Требования к свойствам настраиваемого класса в качестве атрибутов XAML

Свойства должны ссылаться на тип по значению (например, примитив) или использовать класс для типа, который имеет либо конструктор без параметров, либо выделенный преобразователь типов, к которому может получить доступ обработчик XAML. В реализации XAML среды CLR XAML-процессоры находят такие преобразователи, используя встроенную поддержку примитивов языка или применение TypeConverterAttribute к типу или члену в поддерживающих определениях типов.

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

Свойства можно объявить в абстрактном классе, но можно задать только в конкретных классах, производных от абстрактного класса. Это связано с тем, что для создания элемента объекта для класса вообще требуется открытый конструктор без параметров в классе.

Синтаксис атрибута TypeConverter Enabled

Если вы предоставляете специальный атрибутированный преобразователь типов на уровне класса, применяемое преобразование типов позволяет использовать синтаксис атрибутов для любого свойства, которому нужно создать экземпляр этого типа. Преобразователь типов не позволяет использовать объектный элемент данного типа; только наличие конструктора без параметров для этого типа позволяет использовать объектный элемент. Таким образом, свойства, включенные в преобразователь типов, обычно не используются в синтаксисе свойств, если сам тип не поддерживает синтаксис элемента объекта. Исключением из этого является то, что можно указать синтаксис элемента свойства, но элемент свойства содержит строку. Это использование действительно эквивалентно использованию синтаксиса атрибута, и такое использование не распространено, если не требуется более надежная обработка пробелов значения атрибута. Например, ниже приведено использование элемента свойства, принимающего строку, и эквивалент использования атрибута:

<Button>Hallo!
  <Button.Language>
    de-DE
  </Button.Language>
</Button>
<Button Language="de-DE">Hallo!</Button>

Примеры свойств, где разрешен синтаксис атрибута, но синтаксис элемента свойства, содержащий элемент объекта, запрещен через XAML, являются различными свойствами, которые принимают тип Cursor. Класс Cursor имеет выделенный преобразователь типов CursorConverter, но не предоставляет конструктор без параметров, поэтому свойство Cursor можно задать только с помощью синтаксиса атрибутов, даже если фактический тип Cursor является ссылочным типом.

преобразователи типов Per-Property

Кроме того, само свойство может объявить преобразователь типов на уровне свойств. Это позволяет создать "мини язык," который создает экземпляры объектов типа свойства встраиваемым образом, обрабатывая входящие строковые значения атрибута в качестве входных данных для операции ConvertFrom на основе соответствующего типа. Обычно это делается для предоставления удобного доступа, а не в качестве единственного средства для установки свойства в XAML. Однако можно также использовать преобразователи типов для атрибутов, в которых требуется использовать существующие типы CLR, которые не предоставляют ни конструктора без параметров, ни атрибутированного преобразователя типов. Примеры из API WPF — это некоторые свойства, принимающие тип 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 2009 может поддерживать методы, но использование 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. Требования сериализации не описаны в этом разделе. См. Обзор авторинга и Дерево элементов и сериализация.

См. также