Расширения разметки и XAML WPF

В этом разделе рассматриваются концепции расширений разметки для XAML, включая их синтаксические правила, назначение и формирующую их объектную модель классов. Расширения разметки — это общая функция языка XAML и реализация служб XAML платформы .NET. В этом разделе рассматриваются расширения разметки для использования в XAML WPF.

Обработчики XAML и расширения разметки

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

Базовый синтаксис расширения разметки

Расширение разметки можно реализовать для предоставления значений атрибутов при использовании атрибутов, свойств при использовании элементов свойств или и того и другого.

При использовании расширения разметки для предоставления значения атрибута синтаксис, который позволяет обработчику XAML различать последовательность расширения разметки, подразумевает наличие открывающей и закрывающей фигурных скобок ({ и }). Затем тип расширения разметки определяется по токену строки, следующему сразу после открывающей фигурной скобки.

При использовании в синтаксисе элемента свойства расширение разметки визуально не отличается от любого другого элемента, используемого для предоставления значения элемента свойства: объявление элемента XAML, которое ссылается на класс расширения разметки как на элемент, заключается в угловые скобки (<>).

Расширения разметки, определенные в XAML

Существует несколько расширений разметки, которые не относятся к реализации WPF XAML, но являются реализациями внутренних параметров или функций XAML как языка. Эти расширения разметки реализуются в сборке System.Xaml как часть общих служб .NET Framework XAML и находятся в пространстве имен XAML языка XAML. В обычном синтаксисе разметки эти расширения разметки обычно распознаются по префиксу x:. Базовый класс MarkupExtension (также определенный в сборке System.Xaml) содержит шаблон, который должны использовать все расширения разметки для их поддержки в средствах чтения и записи XAML, включенных в XAML WPF.

  • x:Type предоставляет объект Type для именованного типа. Это средство наиболее часто используется в стилях и шаблонах. Дополнительные сведения см. в разделе Расширение разметки x:Type.

  • x:Static создает статические значения. Эти значения поступают из сущностей кода типа значения, которые непосредственно не принадлежат к типу значения целевого свойства, но могут быть приведены к нему. Дополнительные сведения см. в разделе Расширение разметки x:Static.

  • x:Null задает null как значение для свойства и может использоваться для атрибутов или значений элементов свойств. Дополнительные сведения см. в разделе Расширение разметки x:NULL.

  • x:Array обеспечивает поддержку создания общих массивов в синтаксисе XAML для случаев, когда поддержка коллекций, предоставляемая базовыми элементами и моделями элементов управления WPF, намеренно не используется. Дополнительные сведения см. в разделе Расширение разметки x:Array.

Примечание.

Префикс x: используется для обычного сопоставления пространства имен XAML внутренних параметров языка XAML в корневом элементе файла или рабочей среды XAML. Например, шаблоны Visual Studio для приложений WPF инициируют файл XAML с помощью этого сопоставления x:. Можно выбрать другой маркер префикса в собственном сопоставлении пространства имен XAML, но в этой документации предполагается использование сопоставления по умолчанию x: как средства идентификации этих сущностей, которые являются определенной частью пространства имен XAML для языка XAML, в отличие от пространства имен WPF по умолчанию или других пространств имен XAML, не связанных с конкретной платформой.

Расширения разметки для WPF

Наиболее распространенными расширениями разметки, используемыми в программировании WPF, являются те, которые поддерживают ссылки на ресурсы (StaticResource и DynamicResource) и те, которые поддерживают привязку данных (Binding).

  • StaticResource обеспечивает значение для свойства, заменяя значение уже заданного ресурса. Оценка StaticResource в конечном счете выполняется во время загрузки XAML и не имеет доступа к графу объектов во время выполнения. Дополнительные сведения см. в разделе Расширение разметки StaticResource.

  • DynamicResource обеспечивает значение для свойства в виде ссылки на ресурс, отложенной до времени выполнения. Ссылка на динамический ресурс вызывает новый поиск каждый раз, когда осуществляется доступ к такому ресурсу, и имеет доступ к графу объектов во время выполнения. Для получения такого доступа концепция DynamicResource поддерживается свойствами зависимостей в системе свойств WPF и вычисляемыми выражениями. Таким образом, вы можете использовать DynamicResource только для целевого свойства зависимостей. Дополнительные сведения см. в разделе Расширение разметки DynamicResource.

  • Binding предоставляет для свойства привязанное к данным значение, используя контекст данных, который применяется к родительскому объекту во время выполнения. Это расширение разметки довольно сложное, поскольку разрешает использовать важный встроенный синтаксис для указания привязки данных. Дополнительные сведения см. в разделе Привязка расширения разметки.

  • RelativeSource предоставляет исходные сведения для класса Binding, который может перемещаться по нескольким возможным связям в дереве объектов времени выполнения. Это обеспечивает специализированные источники для привязок, которые создаются в шаблонах для многократного использования или в коде без полных сведений об окружающем дереве объектов. Дополнительные сведения см. в разделе Расширение разметки RelativeSource.

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

  • ColorConvertedBitmap поддерживает сравнительно сложный сценарий создания образов. Дополнительные сведения см. в разделе Расширение разметки ColorConvertedBitmap.

  • ComponentResourceKey и ThemeDictionary поддерживают аспекты поиска ресурсов, в честности ресурсов и тем, упакованных в пользовательские элементы управления. Дополнительные сведения см. в разделе Расширение разметки ComponentResourceKey, Расширение разметки ThemeDictionary или Общие сведения о разработке элементов управления.

Классы *Extension

Как для общих расширений разметки языка XAML, так и для расширений разметки для WPF поведение каждого расширения разметки распознается обработчиком XAML с помощью класса *Extension, который является производным от MarkupExtension и обеспечивает реализацию метода ProvideValue. Этот метод в каждом расширении предоставляет объект, который возвращается при вычислении расширения разметки. Возвращаемый объект обычно вычисляется, исходя из различных токенов строк, передаваемых в расширение разметки.

Например, класс StaticResourceExtension предоставляет поверхностную реализацию фактического поиска ресурса, чтобы его реализация ProvideValue возвращала запрашиваемый объект, с входными данными этой конкретной реализации в виде строки, которая используется для поиска ресурса по его x:Key. Большая часть подробностей этой реализации не важна при использовании существующего расширения разметки.

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

Шаблон именования *Extension принят для удобства и согласованности. Он не требуется, чтобы обработчик XAML определил этот класс как поддерживающий расширение разметки. Пока база кода включает сборку System.Xaml и использует реализации служб XAML .NET Framework, для распознавания расширения разметки XAML достаточно наследования от MarkupExtension и поддержки синтаксиса создания объектов. WPF определяет классы, обеспечивающие поддержку расширений разметки, которые не соответствуют шаблону именования *Extension, например Binding. Обычно причина заключается в том, что класс поддерживает не только расширение разметки, но и сценарии. В случае Binding этот класс поддерживает доступ к методам и свойствам объекта во время выполнения для сценариев, которые не имеют ничего общего с XAML.

Интерпретация текста инициализации класса Extension

Токены строки, следующие именем расширения разметки и по-прежнему заключенные в фигурные скобки, интерпретируются обработчиком XAML одним из следующих способов.

  • Запятая всегда представляет разделитель отдельных токенов.

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

    Примечание.

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

  • Если отдельные разделенные токены содержат знаки равенства, то обработчик XAML сначала вызывает конструктор без параметров для расширения разметки. Затем каждая пара «имя = значение» интерпретируется как имя свойства, которое существует в расширении разметки, и значение для присвоения этому свойству.

  • Если имеется параллельный результат между поведением конструктора и поведением установки свойства в расширении разметки, не имеет значения, какое поведение использовать. Чаще всего пары свойство=значение используются для расширений разметки, которые имеют несколько устанавливаемых свойств, поскольку это делает разметку более продуманной и уменьшает вероятность случайно перепутать параметры конструктора. (При указании пар property=value эти свойства могут находиться в любом порядке.) Кроме того, не гарантируется, что расширение разметки предоставляет параметр конструктора, который задает каждое из его свойств набора. Например, Binding является расширением разметки со множеством свойств, устанавливаемых в форме свойство=значение, но Binding поддерживает только два конструктора: конструктор без параметров и конструктор, задающий начальный путь.

  • Литерал запятой нельзя передать в расширение разметки без escape-последовательности.

Escape-последовательности и расширения разметки

При обработке атрибутов в обработчик XAML фигурные скобки расцениваются как указание на последовательность расширения разметки. При необходимости можно также получить литеральный символ фигурной скобки, введя escape-последовательность с помощью пары пустых фигурных скобок, за которыми следует литерал фигурной скобки. См. раздел {} Escape последовательность — расширение разметки.

Вложенные расширения разметки при использовании XAML

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

<Setter Property="Background"  
  Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />  

При таком использовании инструкция x:Static оценивается первой и возвращает строку. Затем эта строка используется в качестве аргумента для DynamicResource.

Расширения разметки и синтаксис элементов свойств

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

Большинство расширений разметки, если они используются в синтаксисе объектных элементов для заполнения элемента свойства, не имеют содержимого или какого-либо другого синтаксиса элементов свойств внутри. Таким образом, будет закрыт тег объектного элемента и не будут предоставлены дочерние элементы. Всякий раз, когда обработчик XAML обнаруживает объектный элемент, вызывается конструктор для этого класса, который создает экземпляр объекта, созданного из проанализированного элемента. Класс расширения разметки не отличается: если вы хотите, чтобы расширение разметки использовалось в синтаксисе элемента объекта, то должны предоставить конструктор без параметров. Некоторые существующие расширения разметки имеют по крайней мере одно обязательное значение свойства, которое должно быть указано для эффективной инициализации. В таком случае это значение свойства обычно передается в объектный элемент объекта качестве атрибута свойства. В справочных страницах Возможности пространства имен языка XAML (x:) и Расширения XAML WPF будут отмечены расширения разметки, которые имеют обязательные свойства (и имена обязательных свойств). В справочных страницах также будут отмечено, если для определенных расширений разметки запрещен синтаксис атрибутов или синтаксис объектных элементов. Важным случаем является расширение разметки x:Array, которое не поддерживает синтаксис атрибутов, так как содержимое этого массива должно быть указано в тегах как содержимое. Содержимое массива обрабатывается как общие объекты, поэтому нецелесообразно применение для атрибута преобразователей типа по умолчанию. Кроме того, расширение разметки x:Array требует параметр type.

См. также