Подробное описание синтаксиса XAML

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

Спецификация языка XAML

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

Для получения дополнительных сведений о спецификации языка XAML можно скачать [MS-XAML] из Центра загрузки Майкрософт.

XAML и CLR

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

Члены типов и наследование классов

Свойства и события, появляющиеся в качестве членов XAML типа WPF, часто наследуются из базовых типов. Рассмотрим следующий пример: <Button Background="Blue" .../>. Свойство Background не является немедленно объявленным свойством класса Button, если просмотреть определение класса, результаты отражения или документацию. Вместо этого, свойство Background наследуется от базового класса Control.

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

Синтаксис объектных элементов

Синтаксис объектных элементов — это синтаксис разметки XAML, который создает экземпляр класса или структуры CLR, объявляя XML-элемент. Этот синтаксис напоминает синтаксис элементов других языков разметки, таких как HTML. Синтаксис объектного элемента начинается с левой угловой скобки (<), за которой сразу же следует имя типа класса или структуры, экземпляр которых создается. После имени типа может следовать ноль или несколько пробелов, кроме того, в объектном элементе могут быть объявлены ноль или несколько атрибутов с одним или несколькими пробелами, разделяющими каждую пару атрибутов name="value". Наконец, должно выполняться одно из указанных ниже условий.

  • Элемент и тег должны быть завершаться косой чертой (/), сразу же за которой должна следовать правая угловая скобка (>).

  • Открывающий тег должен завершаться правой угловой скобкой (>). За открывающим тегом могут следовать другие элементы объекта, элементы свойств или внутренний текст. Само приводимое здесь содержимое, как правило, определяется объектной моделью элемента. Также для элемента объекта должен присутствовать эквивалентный закрывающий тег, на правильном уровне вложения и в балансе с другими парами открывающих и закрывающих тегов.

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

Например, в следующем примере показан синтаксис элемента объекта, создающий новый экземпляр класса Button, а также задается атрибут Name и значение этого атрибута:

<Button Name="CheckoutButton"/>

В следующем примере показан синтаксис элемента объекта, который также содержит синтаксис свойства содержимого XAML. Внутренний текст, содержащийся в нем, будет использоваться для задания свойства содержимого XAML TextBox, Text.

<TextBox>This is a Text Box</TextBox>

Модели содержимого

С точки зрения синтаксиса, класс может поддерживать использование в качестве элемента объекта XAML, но этот элемент будет правильно работать в приложении или на странице, только когда он помещен в ожидаемое положение общей модели содержимого или дерева элементов. Например, обычно MenuItem следует помещать в качестве дочернего элемента класса, производного от MenuBase, такого как Menu. Модели содержимого конкретных элементов документируются как часть примечаний на страницах класса для элементов управления и других классов WPF, которые могут использоваться в качестве элементов XAML.

Свойства элементов объекта

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

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

Синтаксис атрибутов (свойства)

Синтаксис атрибута — это синтаксис разметки XAML, задающий значение свойства путем объявления атрибута в существующем элементе объекта. Имя атрибута должно соответствовать имени члена CLR для свойства класса, базового для соответствующего элемента объекта. За именем атрибута следует оператор присваивания (=). Значение атрибута должно быть строкой, заключенной в кавычки.

Примечание.

Для литеральной кавычки в атрибуте можно использовать чередующиеся кавычки. Например, можно использовать одинарные кавычки в качестве способа объявления строки, содержащей символ двойной кавычки. Независимо от того, используются ли одинарные или двойные кавычки, для открытия и закрытия строки значения атрибута следует использовать совместимую пару. Существуют также Escape-последовательности или другие методы, доступные для обхода ограничений символов, накладываемых любым конкретным синтаксисом XAML. См. раздел Сущности знаков XML и XAML.

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

Для событий XAML в WPF событие, на которое ссылается имя атрибута, должно быть открытым и у него должен быть открытый делегат.

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

Обработка значений атрибутов

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

Значение атрибута заполняется одним из следующих значений, используя указанный порядок обработки:

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

  2. Если свойство объявляется с атрибутом TypeConverter или тип значения этого свойства объявляется с атрибутом TypeConverter, строковое значение атрибута передается преобразователю типов в качестве входных данных преобразования, и преобразователь вернет новый экземпляр объекта.

  3. Если TypeConverter отсутствует, предпринимается попытка прямого преобразования в тип свойства. Этот заключительный уровень — прямое преобразование в собственное значение средства синтаксического анализа между примитивными типами языка XAML или проверка именованных констант в перечислении (затем средство синтаксического анализа обращается к соответствующим значениям).

Значения атрибутов перечисления

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

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

В случае перечислений флагов поведение основано на методе Enum.Parse. Можно указать несколько значений для перечислений флагов, разделив значения запятыми. Но нельзя объединить значения перечисления, которые не являются перечислениями флагов. Например, нельзя использовать синтаксис запятой, чтобы попытаться создать объект Trigger, который действует на несколько условий перечисления, не являющегося перечислением флагов:

<!--This will not compile, because Visibility is not a flagwise enumeration.-->  
...  
<Trigger Property="Visibility" Value="Collapsed,Hidden">  
  <Setter ... />  
</Trigger>  
...  

Перечисления флагов, которые поддерживают атрибуты и которые можно задать в XAML, в WPF встречаются редко. Но одним из таких перечислений является StyleSimulations. Например, можно использовать синтаксис атрибута с разделителями-запятыми, принятый для флагов, чтобы изменить пример, приведенный в примечаниях для класса GlyphsStyleSimulations = "BoldSimulation" может превратиться в StyleSimulations = "BoldSimulation,ItalicSimulation". Другим свойством, в котором можно указать несколько значений перечисления, является KeyBinding.Modifiers. Но это свойство — особый случай, так как перечисление ModifierKeys поддерживает собственный преобразователь типов. Преобразователь типов для модификаторов использует в качестве разделителя знак плюса (+), а не запятую (,). Это преобразование поддерживает более традиционный синтаксис для представления сочетаний клавиш, принятый в программировании Microsoft Windows, таких как CTRL+ALT.

Свойства и ссылки на имена членов событий

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

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

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

В некоторых сценариях имена свойств иногда предоставляются в качестве значения, а не имени атрибута. Такое имя свойства может также содержать квалификаторы, например, свойство, заданное в виде типВладельца.имяСвойстваЗависимости. Этот сценарий типичен при написании стилей или шаблонов в XAML. Правила обработки имен свойств, предоставленных в качестве значения атрибута, отличаются и регулируются типом устанавливаемого свойства или поведением конкретных подсистем WPF. Дополнительные сведения см. в разделе Создание стилей и шаблонов.

Другое использование имен свойств возникает, когда значение атрибута описывает связь между свойствами. Эта возможность, используемая для привязки данных и для целей раскадровки, включается классом PropertyPath и его преобразователем типов. Более полное описание семантики поиска см. в разделе Синтаксис PropertyPath в XAML.

Синтаксис элемента свойства

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

В частности, синтаксис начинается с левой угловой скобки (<), за которой сразу же следует имя типа класса или структуры, внутри которых содержится синтаксис элемента свойства. Сразу за ним следует одна точка (.), затем имя свойства, а затем правая угловая скобка (>). Как и синтаксис атрибута, это свойство должно существовать в объявленных открытых членах указанного типа. Значение, присваиваемое свойству, содержится в элементе свойства. Как правило, значение присваивается в виде одного или нескольких элементов объекта, так как указание объектов в качестве значений — это тот сценарий, для которого предназначен синтаксис элемента свойства. Наконец, должен присутствовать соответствующий закрывающий тег, определяющий ту же комбинацию имяТипаЭлемента.имяСвойства, на правильном уровне вложения и в балансе с другими тегами элементов.

Например, ниже приведен синтаксис элемента свойства ContextMenu для класса Button.

<Button>
  <Button.ContextMenu>
    <ContextMenu>
      <MenuItem Header="1">First item</MenuItem>
      <MenuItem Header="2">Second item</MenuItem>
    </ContextMenu>
  </Button.ContextMenu>
  Right-click me!</Button>

Значение в элементе свойства также может быть задано как внутренний текст, в тех случаях, когда указанный тип свойства является примитивным типом значения, таким как String, или перечислением, в котором указано имя. Эти два варианта используются редко, так как для каждого из них также можно использовать более простой синтаксис атрибута. Один из сценариев заполнения элемента свойства строкой предназначен для свойств, не являющихся свойством содержимого XAML, но по-прежнему используемых для представления текста пользовательского интерфейса, если для отображения в пользовательском интерфейсе этот текст должен содержать определенные символы пробелов, такие как символы перевода строки. Синтаксис атрибута не может содержать символы перевода строки, но синтаксис элемента свойства может, если включено сохранение значащих пробелов (дополнительные сведения см. в разделе Обработка пробелов в XAML). Другой сценарий заключается в том, что к элементу свойства можно применить директиву x:Uid и, таким образом, пометить содержащееся внутри значение как значение, которое должно быть локализовано в языке BAML, выдаваемом WPF, или другим способом.

Элемент свойства не представлен в логическом дереве WPF. Элемент свойства — это просто определенный синтаксис для задания свойства, которое не является элементом, у которого есть экземпляр или за которым стоит объект. (Дополнительные сведения о концепции логического дерева см. в разделе Деревья в WPF.)

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

Синтаксис коллекции

Спецификация XAML требует, чтобы реализации обработчика XAML были способны определить свойства, в которых тип значения является коллекцией. Обычная реализация обработчика XAML в .NET основана на управляемом коде и среде CLR, определяя типы коллекций с помощью одного из следующих типов:

Если тип свойства является коллекцией, то подразумеваемый тип коллекции не нужно определять в разметке как элемент объекта. Вместо этого элементы, которые должны стать элементами коллекции, указываются как один или несколько дочерних элементов для элемента свойства. Каждый такой элемент вычисляется как объект во время загрузки и добавляется в коллекцию с помощью вызова метода Add подразумеваемой коллекции. Например, свойство Triggers класса Style принимает специализированный тип коллекции TriggerCollection, реализующий интерфейс IList. Создание в разметке экземпляра элемента объекта TriggerCollection не требуется. Вместо этого указывается один или несколько элементов Trigger в качестве элементов в элементе свойства Style.Triggers, где Trigger (или производный класс) — это тип, ожидаемый как тип элемента для строго типизированной и неявной коллекции TriggerCollection.

<Style x:Key="SpecialButton" TargetType="{x:Type Button}">
  <Style.Triggers>
    <Trigger Property="Button.IsMouseOver" Value="true">
      <Setter Property = "Background" Value="Red"/>
    </Trigger>
    <Trigger Property="Button.IsPressed" Value="true">
      <Setter Property = "Foreground" Value="Green"/>
    </Trigger>
  </Style.Triggers>
</Style>

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

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

Примечание.

Универсальные интерфейсы списков и словарей (IList<T> и IDictionary<TKey,TValue>) для обнаружения коллекции не поддерживаются. Но можно в качестве базового использовать класс List<T>, так как он напрямую реализует IList, или Dictionary<TKey,TValue>, так как он напрямую реализует IDictionary.

На страницах справочника .NET по типам коллекций этот синтаксис с намеренным отсутствием элемента объекта для коллекции иногда упоминается в разделах синтаксиса XAML как синтаксис неявной коллекции.

За исключением корневого элемента, каждый элемент объекта в XAML-файле, вложенный в качестве дочернего элемента другого элемента, действительно является элементом, соответствующим одному или обоим из следующих случаев: член свойства неявной коллекции соответствующего родительского элемента или элемент, определяющий значение свойства содержимого XAML для родительского элемента (свойства содержимого XAML будут рассмотрены в следующем разделе). Иными словами, связь родительских и дочерних элементов на странице разметки фактически является одним объектом в корне, и каждый элемент объекта ниже корня является либо одним экземпляром, предоставляющим значение свойства родительского элемента, либо одним из элементов в коллекции, которая также является значением свойства типа коллекции в родительском элементе. Эта концепция с одним корнем типична для XML и часто усиливается в поведении интерфейсов API, загружающих XAML, таких как Load.

В следующем примере показан синтаксис с элементом объекта для коллекции (GradientStopCollection), заданной явным образом.

<LinearGradientBrush>  
  <LinearGradientBrush.GradientStops>  
    <GradientStopCollection>  
      <GradientStop Offset="0.0" Color="Red" />  
      <GradientStop Offset="1.0" Color="Blue" />  
    </GradientStopCollection>  
  </LinearGradientBrush.GradientStops>  
</LinearGradientBrush>  

Обратите внимание, что коллекцию не всегда можно объявить явно. Например, попытка явно объявить TriggerCollection в приведенном выше примере Triggers завершится ошибкой. Явное объявление коллекции требует, чтобы класс коллекции поддерживал конструктор без параметров, а у TriggerCollection такой конструктор отсутствует.

Свойства содержимого XAML

Синтаксис содержимого XAML — это синтаксис, разрешенный только для классов, задающих ContentPropertyAttribute как часть объявления класса. Атрибут ContentPropertyAttribute ссылается на имя свойства, являющееся свойством содержимого для этого типа элемента (включая производные классы). При обработке обработчиком XAML все дочерние элементы или внутренний текст, найденные между открывающим и закрывающим тегами элемента объекта, будут назначены в качестве значения свойства содержимого XAML для этого объекта. Для свойства содержимого разрешается указывать явные элементы свойств, но такое использование обычно не показывается в разделах синтаксиса XAML в справочнике по .NET. Эта явная или подробная техника иногда полезна для ясности разметки или стиля разметки, но обычно свойство содержимого призвано упростить разметку, чтобы элементы, интуитивно связанные как родительский и дочерний элементы, можно было вложить напрямую. Теги элементов свойств для других свойств элемента не назначаются как "содержимое" в соответствии со строгим определением языка XAML. В порядке обработки средством синтаксического анализа XAML они обрабатываются ранее и не считаются "содержимым".

Значения свойств содержимого XAML должны задаваться непрерывно

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

<Button>I am a
  <Button.Background>Blue</Button.Background>  
  blue button</Button>  

Она недопустима, поскольку, если бы этот синтаксис был введен явно, используя синтаксис элемента свойства для свойства содержимого, то свойство содержимого было бы задано дважды:

<Button>  
  <Button.Content>I am a </Button.Content>  
  <Button.Background>Blue</Button.Background>  
  <Button.Content> blue button</Button.Content>  
</Button>  

Аналогичный недопустимый пример — когда свойство содержимого является коллекцией, а дочерние элементы чередуются с элементами свойства:

<StackPanel>  
  <Button>This example</Button>  
  <StackPanel.Resources>  
    <SolidColorBrush x:Key="BlueBrush" Color="Blue"/>  
  </StackPanel.Resources>  
  <Button>... is illegal XAML</Button>  
</StackPanel>  

Объединение свойства содержимого и синтаксиса коллекции

Чтобы использовать в качестве содержимого несколько элементов объекта, тип свойства контента должен быть типом коллекции. Как и в случае синтаксиса элемента свойства для типов коллекций, обработчик XAML должен обнаруживать типы, которые являются типами коллекций. Если у элемента есть свойство содержимого XAML, а тип свойства содержимого XAML является коллекцией, то подразумеваемый тип коллекции не нужно указывать в разметке как элемент объекта, а свойство содержимого XAML не нужно определять как элемент свойства. Таким образом, модель видимого содержимого в разметке теперь может содержать несколько дочерних элементов, назначенных в качестве содержимого. Ниже приведен синтаксис содержимого для класса, производного от Panel. Все производные классы Panel устанавливают свойство содержимого XAML равным Children, для чего требуется значение типа UIElementCollection.

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >
  <StackPanel>
    <Button>Button 1</Button>
    <Button>Button 2</Button>
    <Button>Button 3</Button>
  </StackPanel>
</Page>

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

Сочетание элементов свойств и элементов объекта в объекте со свойством содержимого

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

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

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

Пространства имен языка XAML

Ни один из предыдущих примеров синтаксиса не определял пространство имен XAML, отличное от пространства имен XAML по умолчанию. В типичных приложениях WPF пространство имен XAML по умолчанию определяется как пространство имен WPF. Можно указать пространства имен XAML, отличные от пространства имен XAML по умолчанию, и продолжать использовать аналогичный синтаксис. Но в любом месте, где называется класс, недоступный в пространстве имен XAML по умолчанию, имени класса должен предшествовать префикс пространства имен XAML, сопоставленный соответствующему пространству имен CLR. Например, <custom:Example/> —это синтаксис элемента объекта для создания экземпляра класса Example, в котором пространство имен CLR, содержащее этот класс (и, возможно, сведения о внешней сборке, содержащие базовые типы), было ранее сопоставлено префиксу custom.

Дополнительные сведения о пространствах имен XAML см. в разделе Пространства имен XAML и сопоставление пространств имен для XAML в WPF.

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

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

Реализация XAML в .NET использует абстрактный класс MarkupExtension в качестве основы всех расширений разметки, поддерживаемых WPF, а также другими платформами или технологиями. Расширения разметки, конкретно реализуемые WPF, часто служат для предоставления ссылок на другие существующие объекты или для создания отложенных ссылок на объекты, которые будут вычисляться во время выполнения. Например, простая привязка данных WPF выполняется путем задания расширения разметки {Binding} вместо значения, обычно принимаемого конкретным свойством. Многие расширения разметки WPF позволяют использовать синтаксис атрибута для свойств, в которых синтаксис атрибута в противном случае не был бы возможен. Например, объект Style является относительно сложным типом, содержащим вложенную последовательность объектов и свойств. Стили в WPF обычно определяются как ресурс в объекте ResourceDictionary, а затем на них ссылаются с помощью одного из двух расширений разметки WPF, запрашивающих ресурс. Это расширение разметки откладывает вычисление значения свойства до этапа поиска ресурсов и позволяет предоставить значение свойства Style, с типом Style, в синтаксисе атрибута, как показано в следующем примере:

<Button Style="{StaticResource MyStyle}">My button</Button>

Здесь StaticResource определяет класс StaticResourceExtension, обеспечивающий реализацию расширения разметки. Следующая строка MyStyle используется в качестве входных данных для конструктора StaticResourceExtension, не являющегося конструктором по умолчанию, где параметр, извлеченный из строки расширения, объявляет запрошенный ключ ресурса ResourceKey. Ожидается, что MyStyle принимает значение x:Key стиля Style, определяемого как ресурс. Использование расширения разметки StaticResource требует использовать этот ресурс для предоставления значения свойства Style с помощью логики поиска статических ресурсов во время загрузки.

Подробнее о расширениях разметки см. в разделе Расширения разметки и XAML WPF. Справку по расширениям разметки и другим возможностям программирования XAML, включенным в обычную реализацию XAML в .NET, см. в разделе Возможности пространства имен языка XAML (x:). Сведения о расширениях разметки, специфических для WPF, см. в разделе Расширения разметки для WPF.

Присоединенные свойства

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

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

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

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

Присоединенные события

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

Структура корневого элемента XAML

В следующей таблице показан типичный корневой элемент XAML с выделением конкретных атрибутов корневого элемента:

Атрибут Description
<Page Открытие элемента объекта для корневого элемента
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Пространство имен XAML по умолчанию (WPF)
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Пространство имен XAML для языка XAML
x:Class="ExampleNamespace.ExampleCode" Объявление разделяемого класса, подключающее разметку к любому коду программной части, определенному для этого разделяемого класса
> Конец элемента объекта для корневого элемента. Объект еще не закрыт, так как элемент содержит дочерние элементы

Необязательные и нерекомендуемые возможности использования XAML

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

Использование необязательных элементов свойств

Использование необязательных элементов свойств предусматривает явное написание свойств содержимого элемента, которые обработчик XAML неявно считает заданными. Например, при объявлении содержимого меню Menu можно явно объявить коллекцию Items меню Menu как тег элемента свойства <Menu.Items> и поместить каждый пункт меню MenuItem в тег <Menu.Items> вместо того, чтобы использовать неявное поведение обработчика XAML, согласно которому все дочерние элементы меню Menu должны относиться к типу MenuItem и помещаются в коллекцию Items. Иногда такое необязательное использование может помочь визуально уточнить структуру объекта, представленную в разметке. Кроме того, иногда явное использование элемента свойства позволяет избежать разметки, которая технически функциональна, но визуально запутана, такой как вложенные расширения разметки в значении атрибута.

Полные атрибуты имяТипа.имяЧлена

Представление атрибута в виде имяТипа.имяЧлена фактически более универсально, чем просто перенаправленное событие. Но в других ситуациях такое представление является избыточным и его следует избегать, используя только для поддержания стиля разметки и удобочитаемости. В следующем примере все три ссылки на атрибут Background полностью эквивалентны:

<Button Background="Blue">Background</Button>
<Button Button.Background="Blue">Button.Background</Button>
<Button Control.Background="Blue">Control.Background</Button>

Button.Background работает благодаря успешному поиску этого свойства для Button (Background наследуется от Control), а Button является классом элемента объекта или базовым классом. Control.Background работает, так как класс Control фактически определяет Background, а Control является базовым классом для Button.

Но следующий пример представления имяТипа.имяЧлена не работает и поэтому показан закомментированным:

<!--<Button Label.Background="Blue">Does not work</Button> -->

Label является другим классом, производным от Control, и если бы было указано Label.Background внутри элемента объекта Label, этот вариант был бы рабочим. Но, так как Label не является классом или базовым классом Button, заданное поведение процессора XAML заключается в последующей обработке Label.Background как присоединенного свойства. Label.Background не является доступным присоединенным свойством, и такой вариант использования завершается ошибкой.

Элементы свойства имяБазовогоТипа.имяЧлена

Аналогично тому, как представление имяТипа.имяЧлена работает для синтаксиса атрибута, синтаксис имяБазовогоТипа.имяЧлена работает для элемента свойства. Например, работает следующий синтаксис:

<Button>Control.Background PE
  <Control.Background>
    <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
      <GradientStop Color="Yellow" Offset="0.0" />
      <GradientStop Color="LimeGreen" Offset="1.0" />
    </LinearGradientBrush>
    </Control.Background>
</Button>

Здесь элемент свойства был задан как Control.Background, хотя соответствующий элемент свойства содержался в Button.

Но, аналогично представлению имяТипа. имяЧлена для атрибутов, использование в разметке имяБазовогоТипа.имяЧлена является плохим стилем, которого следует избегать.

См. также