Общие сведения о расширениях разметки для XAML
Расширения разметки — это метод XAML для получения значения, которое не является примитивом или определенным типом XAML. Для использования расширений разметки используются известные последовательности символов: открывающая фигурная скобка {
для входа в область расширения разметки и закрывающая фигурная скобка }
для выхода. При использовании служб XAML .NET можно использовать некоторые предопределенные расширения разметки языка XAML из сборки System.Xaml. Вы также можете подкласс из класса MarkupExtension, определенного в System.Xaml, и определить собственные расширения разметки. Или можно использовать расширения разметки, определенные определенной платформой, если вы уже ссылаетесь на эту платформу.
При доступе к расширению разметки модуль записи объектов XAML может предоставлять службы пользовательскому классу MarkupExtension через точку подключения службы в переопределении MarkupExtension.ProvideValue. Службы можно использовать для получения контекста об использовании, конкретных возможностях модуля записи объектов, контексте схемы XAML и т. д.
Расширения разметки, задаваемые в XAML
Несколько расширений разметки реализованы службами XAML .NET для поддержки языка XAML. Эти расширения разметки соответствуют частям спецификации XAML как языка. Обычно они идентифицируются префиксом x:
в синтаксисе, как показано в общем использовании. Реализации служб XAML .NET для этих элементов языка XAML являются производными от базового класса MarkupExtension.
Заметка
Префикс x:
используется для типичного сопоставления пространства имен языка XAML в корневом элементе при создании XAML. Например, шаблоны проектов и страниц Visual Studio для различных платформ инициируют XAML-файл с помощью этого сопоставления x:
. Вы можете выбрать другой маркер префикса в вашем собственном сопоставлении пространства имен XAML, но в этой документации будет предполагается использование сопоставления по умолчанию x:
для идентификации тех сущностей, которые являются частью языка XAML, в отличие от пространства имен XAML по умолчанию, используемого в конкретных фреймворках, или других произвольных пространств имен CLR или XML.
x:Type
x:Type
предоставляет объект Type для именованного типа. Эта функция чаще всего используется в механизмах отсрочки, которые используют базовый тип среды CLR и вывод типов в качестве средства группировки или идентификатора. Стили и шаблоны WPF и их использование свойств TargetType
являются конкретным примером. См. раздел x:Type расширения разметкидля получения дополнительных сведений.
x:Static
x:Static
создает статические значения из сущностей кода типа значений, которые не являются непосредственно типом значения свойства, но могут быть оценены этим типом. Это полезно для указания значений, которые уже существуют как известные константы в определении типа. Дополнительные сведения см. в x:Static Markup Extension.
x:Null
x:Null
указывает null
в качестве значения для элемента XAML. В зависимости от структуры конкретных типов или в более крупных концепциях платформы null
не всегда является значением по умолчанию для свойства или подразумеваемым значением пустого атрибута строки. Дополнительные сведения см. в расширения разметки x:NULL.
x:Array
x:Array
поддерживает создание общих массивов в синтаксисе XAML в случаях, когда поддержка коллекции, предоставляемая базовыми элементами и моделями управления, намеренно не используется. Дополнительные сведения см. в расширении разметки массива . В XAML 2009, в частности, массивы обрабатываются как примитивы языка, а не как расширение. Дополнительные сведения см. в языковых особенностях XAML 2009 .
x:Reference
x:Reference
входит в состав XAML 2009, расширение исходного набора языков (2006).
x:Reference
представляет ссылку на другой существующий объект в графе объектов. Этот объект определяется его x:Name
. Дополнительные сведения см. в разделе x:Reference Markup Extension.
Другие x: конструкции
Другие x:
конструкции для поддержки функций языка XAML существуют, но они не реализуются в виде расширений разметки. Дополнительные сведения см. в разделе языковых возможностей пространства имен XAML (x:).
Базовый класс MarkupExtension
Чтобы определить пользовательское расширение разметки, которое может взаимодействовать с реализациями средств чтения и записи XAML в System.Xaml, создаете класс на основе абстрактного класса MarkupExtension. Этот класс имеет один метод для переопределения, и это ProvideValue. Кроме того, может потребоваться определить дополнительные конструкторы для поддержки аргументов при использовании расширения разметки и сопоставимых устанавливаемых свойств.
Через ProvideValueпользовательское расширение разметки имеет доступ к контексту службы, который информирует о среде, в которой расширение разметки вызывается обработчиком XAML. В пути загрузки обычно это XamlObjectWriter. В пути сохранения обычно это XamlXmlWriter. Каждый отчет описывает контекст службы как внутренний класс контекста поставщика услуг XAML, который реализует шаблон поставщика услуг. Дополнительные сведения о доступных службах и их представлениях см. в разделе Преобразователи типов и расширения разметки для XAML.
Класс расширения разметки должен быть общедоступным; процессоры XAML всегда должны иметь возможность создавать экземпляры поддерживающего класса расширения разметки, чтобы воспользоваться его службами.
Определение типа поддержки для пользовательского расширения разметки
При использовании служб .NET XAML или платформ, которые создаются на основе служб XAML .NET, у вас есть два варианта для имени типа поддержки расширения разметки. Имя типа важно для того, как обработчики объектов XAML пытаются получить доступ к типу поддержки расширения разметки и вызвать его при обнаружении использования расширения разметки в XAML. Используйте одну из следующих стратегий именования:
- Присвойте имя типа, чтобы оно точно совпадало с токеном использования разметки XAML. Например, чтобы поддерживать использование расширения
{Collate ...}
, присвойте типу поддержкиCollate
. - Назовите имя типа как составное из маркера строки использования и суффикса
Extension
. Например, чтобы поддерживать использование расширения{Collate ...}
, присвойте типу поддержкиCollateExtension
.
Порядок поиска: сначала найти имя класса с суффиксом Extension
, затем найти имя класса без суффикса Extension
.
С точки зрения использования разметки, включение суффикса Extension
в использование является допустимым. Однако это происходит так, как если бы Extension
действительно был частью имени класса, и создатели объектов XAML не смогут найти класс поддержки расширения разметки для этого использования, если класс поддержки не имеет суффикс Extension
.
Конструктор без параметров
Для всех типов поддержки расширений разметки следует предоставить открытый конструктор без параметров. Конструктор без параметров необходим для любого случая, когда модуль записи объектов XAML создает экземпляр расширения разметки из использования элемента объекта. Поддержка использования элементов object является справедливым ожиданием для расширения разметки, особенно для сериализации. Однако расширение разметки можно реализовать без общедоступного конструктора, если вы планируете поддерживать использование атрибутов расширения разметки.
Если использование расширения разметки не имеет аргументов, конструктор без параметров необходим для поддержки использования.
Паттерны конструкторов и позиционные аргументы для пользовательского расширения разметки
Для расширения разметки с предполагаемым использованием аргументов общедоступные конструкторы должны соответствовать режимам предполагаемого использования. Другими словами, если расширение разметки предназначено для использования одного позиционного аргумента в качестве допустимого использования, следует поддерживать открытый конструктор с одним входным параметром, принимаюющим позиционный аргумент.
Например, предположим, что расширение разметки Collate
предназначено для поддержки только режима, в котором существует один позиционный аргумент, представляющий его режим, указанный как константа перечисления CollationMode
. В этом случае должен быть конструктор со следующей формой:
public Collate(CollationMode collationMode) {...}
На базовом уровне аргументы, передаваемые расширению разметки, являются строкой, так как они пересылаются из значений атрибутов разметки. Вы можете сделать все аргументы строками и работать с данными на этом уровне. Однако у вас есть доступ к определенной обработке, которая возникает до того, как аргументы расширения разметки передаются в класс поддержки.
Обработка работает концептуально, как если бы расширение разметки рассматривается как объект, который нужно создать, а затем устанавливаются значения его членов. Каждое указанное свойство, которое необходимо задать, вычисляется аналогично тому, как указанный элемент можно задать в созданном объекте при анализе XAML. Существует два важных различия:
- Как отмечалось ранее, тип поддержки расширения разметки не должен иметь конструктор без параметров для создания экземпляра в XAML. Его создание объекта откладывается до тех пор, пока его возможные аргументы в текстовом синтаксисе не будут токенизированы и оценены как позиционные или именованные аргументы, и в этот момент вызывается соответствующий конструктор.
- Расширения разметки могут использоваться вложено. Сначала вычисляется самое внутреннее расширение разметки. Таким образом, вы можете предположить такое использование и объявить один из параметров конструкции как тип, который требует преобразователя значений (например, расширения разметки) для генерации.
В предыдущем примере показана зависимость от такой обработки. Модуль записи объектов XAML служб XAML .NET обрабатывает имена констант перечисления в перечисленные значения на собственном уровне.
Обработка текстового синтаксиса позиционного параметра расширения разметки также может полагаться на преобразователь типов, связанный с типом, который находится в аргументе конструкции.
Аргументы называются позициальными аргументами, так как порядок, в котором встречаются маркеры в использовании, соответствует позициальному порядку параметра конструктора, которому они назначены. Например, рассмотрим следующую подпись конструктора:
public Collate(CollationMode collationMode, object collateThis) {...}
Обработчик XAML ожидает два позиционных аргумента для этого расширения разметки. Если было использование {Collate AlphaUp,{x:Reference circularFile}}
, маркер AlphaUp
отправляется в первый параметр и оценивается как перечисление CollationMode
, названной константой. Результат обработки элемента x:Reference
отправляется во второй параметр и интерпретируется как объект.
В указанных правилах XAML для синтаксиса расширения разметки и обработки запятая является разделителем между аргументами, являются ли эти аргументы позициальными или именованными аргументами.
Дублирование арности позиционных аргументов
Если модуль записи объектов XAML сталкивается с расширением разметки с позиционными аргументами, и есть несколько аргументов конструктора, которые принимают такое же количество аргументов (повторяющаяся арность), это не обязательно является ошибкой. Поведение зависит от настраиваемого параметра контекста схемы XAML, SupportMarkupExtensionsWithDuplicateArity. Если SupportMarkupExtensionsWithDuplicateArity является true
, модуль записи объектов XAML не должен вызывать исключение только из-за повторяющегося числа аргументов. Поведение за пределами этой точки не определено строго. Базовое предположение о проектировании заключается в том, что контекст схемы содержит информацию о типах, доступную для конкретных параметров, и может пытаться выполнить явные приведения, которые соответствуют кандидатам на совпадение, чтобы увидеть, какая сигнатура может быть наилучшим совпадением. Исключение может по-прежнему возникать, если ни одна из подписей не может проходить тесты, налагаемые этим конкретным контекстом схемы, выполняющимся в записывающем объекты XAML.
По умолчанию SupportMarkupExtensionsWithDuplicateArityfalse
в XamlSchemaContext среды CLR для служб XAML .NET. Таким образом, XamlObjectWriter по умолчанию вызывает исключения, если он сталкивается с использованием расширения разметки, где в конструкторах резервного типа есть дубликат arity.
Именованные аргументы для пользовательского расширения разметки
Расширения разметки, указанные XAML, также могут использовать именованную форму аргументов для использования. На первом уровне маркеризации текстовый синтаксис делится на аргументы. Наличие знака равенства (=) в любом аргументе определяет аргумент как именованный аргумент. Такой аргумент также токенизирован в пару "имя-значение". Имя в этом случае обозначает доступное для изменения общедоступное свойство поддерживаемого типа расширения разметки. Если вы планируете поддерживать использование именованных аргументов, необходимо предоставить эти открытые свойства, которые можно установить. Свойства могут быть унаследованы до тех пор, пока они остаются публичными.
Доступ к контексту поставщика услуг через реализацию расширения разметки
Доступные службы одинаковы для любого преобразователя значений. Разница заключается в том, как каждый преобразователь значений получает контекст службы. Доступ к службам и доступные службы документированы в теме Преобразователи типов и расширения разметки для XAML.
Использование элемента свойства в расширении разметки
Сценарии использования расширений разметки часто ориентированы на применение расширений разметки в атрибутах. Однако также можно определить резервный класс для поддержки использования элементов свойства.
Чтобы поддерживать использование свойств в вашей расширяемой разметке, определите публичный конструктор без параметров. Это должен быть конструктор экземпляра, а не статический конструктор. Это необходимо, так как обработчик XAML обычно должен вызывать конструктор без параметров для любого элемента объекта, который он обрабатывает из разметки, и это включает классы расширения разметки в качестве элементов объекта. Для расширенных сценариев можно определить нестандартные пути построения для классов. (Дополнительные сведения см. в разделе директивы x:FactoryMethod.) Однако эти шаблоны не следует использовать для расширения разметки, так как это делает обнаружение шаблона использования гораздо сложнее, как для конструкторов, так и для пользователей необработанной разметки.
Присвоение атрибутов пользовательскому расширению разметки
Для поддержки как сред разработки, так и определенных сценариев записи объектов XAML, следует присвоить типу, поддерживающему расширения разметки, несколько атрибутов CLR. Эти атрибуты сообщают о предполагаемом использовании расширения разметки.
MarkupExtensionReturnTypeAttribute сообщает сведения о Type для типа объекта, который возвращает ProvideValue. Согласно своей чистой сигнатуре, ProvideValue возвращает Object. Но различные потребители могут потребовать более точных сведений о типе возврата. К ним относятся:
- Конструкторы и интегрированные среды разработки, которые могут предоставить поддержку использования расширений разметки.
- Расширенные реализации обработчиков
SetMarkupExtension
в целевых классах, которые могут полагаться на рефлексию, чтобы определить тип возвращаемого расширения разметки вместо ветвления по определенным известным MarkupExtension реализациям по имени.
Сериализация использования расширений разметки
Когда модуль записи объектов XAML обрабатывает использование расширения разметки и вызывает ProvideValue, контекст для него, ранее являющийся расширением разметки, сохраняется в потоке узлов XAML, но не в графе объектов. В графе объектов сохраняется только значение. Если у вас есть сценарии разработки или другие причины сохранения исходного расширения разметки в сериализованных выходных данных, необходимо разработать собственную инфраструктуру для отслеживания использования расширения разметки из потока узлов XAML пути загрузки. Вы можете реализовать логику, чтобы воссоздать элементы потока узла из пути загрузки и передать их средствам записи XAML для сериализации в пути сохранения, заменяя значение в соответствующей позиции потока узла.
Расширения разметки в потоке узлов XAML
Если вы работаете с потоком узлов XAML на этапе загрузки, использование расширения разметки отображается в потоке узлов в качестве объекта.
Если использование расширения разметки использует позиционные аргументы, оно представлено как начальный объект со значением инициализации. В качестве грубого текстового представления поток узла выглядит следующим образом:
StartObject
(XamlType — это тип определения расширения разметки, а не его возвращаемый тип)
StartMember
(имя XamlMember равно _InitializationText
)
Value
(значение — позиционные аргументы в виде строки, включая промежуточные разделители)
EndMember
EndObject
Использование расширения разметки с именованными аргументами представляется как объект с членами соответствующих имен, каждый из которых содержит значения в виде текстовых строк.
Фактический вызов реализации ProvideValue
расширения разметки требует контекста схемы XAML, поскольку для этого необходимо сопоставление типов и создание экземпляра типа поддержки расширения разметки. Это одна из причин, почему использование расширений разметки сохраняется таким образом в потоках узлов служб XAML по умолчанию .NET. Часть чтения пути загрузки часто не имеет необходимого контекста схемы XAML.
Если вы работаете с потоком узлов XAML в контексте сохранения, в представлении графа объектов обычно нет ничего, что могло бы указать вам, что объект для сериализации изначально был представлен через использование расширения разметки с результатом ProvideValue
. Сценарии, которые должны сохранять использование расширения разметки для обеспечения обратимости и одновременно фиксировать другие изменения в графе объектов, должны разработать собственные методы для сохранения информации о применении расширения разметки из исходных данных XAML. Например, чтобы восстановить использование расширения разметки, может понадобиться работа с потоком узлов на пути сохранения или выполнение некоторого типа слияния между исходным XAML и возвратно преобразованным XAML. Некоторые платформы, реализующие XAML, такие как WPF, используют промежуточные типы (выражения), чтобы помочь представлять случаи, в которых значения предоставляются расширениями разметки.
См. также
- MarkupExtension
- преобразователи типов и расширения разметки для XAML
- расширения разметки и XAML WPF
.NET Desktop feedback