Обзор XAML (WPF .NET)

В этой статье описаны возможности языка XAML и показано, как использовать язык XAML для написания приложений Windows Presentation Foundation (WPF). В частности, в этой статье описывается реализация XAML в WPF. Сам язык XAML — это более широкое понятие языка, чем WPF.

Что такое язык XAML

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

При представлении в виде текста файлы XAML являются XML-файлами, которые обычно имеют расширение .xaml. Файлы можно сохранять в любой кодировке, поддерживаемой XML, но обычно используется кодировка UTF-8.

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

<StackPanel>
    <Button Content="Click Me"/>
</StackPanel>

Краткое описание синтаксиса XAML

Далее приводятся описание основных форм синтаксиса XAML и короткий пример разметки. В этих разделах не приводится полная информация о каждой форме синтаксиса (например, представление в системе резервных типов). Дополнительные сведения об особенностях синтаксиса XAML см. в статье Подробное описание синтаксиса XAML.

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

Объектные элементы XAML

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

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

<StackPanel>
    <Button Content="Click Me"/>
</StackPanel>

В нем указано два объектных элемента: <StackPanel> (с содержимым и последующим закрывающим тегом) и <Button .../> (самозакрывающаяся форма с несколькими атрибутами). Объектные элементы StackPanel и Button сопоставлены с именем класса, определенным WPF и являющимся частью сборок WPF. При задании тега объектного элемента создается инструкция обработки XAML для создания нового экземпляра базового типа. Каждый экземпляр создается путем вызова конструктора без параметров для базового типа при синтаксическом анализе и загрузке XAML-кода.

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

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

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

<Button Background="Blue" Foreground="Red" Content="This is a button"/>

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

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

Для открывающего тега элемента свойства используется следующий синтаксис: <TypeName.PropertyName>. В большинстве случаев содержимое тега является объектным элементом типа, к которому может принадлежать значение этого свойства. После указания содержимого необходимо завершить элемент свойства закрывающим тегом. Для закрывающего тега используется следующий синтаксис: </TypeName.PropertyName>.

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

<Button>
    <Button.Background>
        <SolidColorBrush Color="Blue"/>
    </Button.Background>
    <Button.Foreground>
        <SolidColorBrush Color="Red"/>
    </Button.Foreground>
    <Button.Content>
        This is a button
    </Button.Content>
</Button>

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

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

В следующем примере показан синтаксис коллекции для задания значений свойства GradientStops.

<LinearGradientBrush>
    <LinearGradientBrush.GradientStops>
        <!-- no explicit new GradientStopCollection, parser knows how to find or create -->
        <GradientStop Offset="0.0" Color="Red" />
        <GradientStop Offset="1.0" Color="Blue" />
    </LinearGradientBrush.GradientStops>
</LinearGradientBrush>

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

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

Например, Border задает свойство содержимого для Child. Следующие два элемента Borderобрабатываются одинаково. Первый элемент использует преимущества синтаксиса свойства содержимого и опускает элемент свойства Border.Child. Второй пример явно показывает Border.Child.

<Border>
    <TextBox Width="300"/>
</Border>
<!--explicit equivalent-->
<Border>
    <Border.Child>
        <TextBox Width="300"/>
    </Border.Child>
</Border>

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

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

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

Текстовое содержимое

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

  • Класс должен объявить свойство содержимого, а это свойство содержимого должно принадлежать типу, присваиваемому строке (тип может быть Object). Например, любой класс ContentControl использует Content в качестве свойства содержимого и имеет тип Object, и при этом поддерживается следующее использование в классе ContentControl, таком как Button: <Button>Hello</Button>.

  • Тип должен объявлять преобразователь типов, при этом текстовое содержимое используется как текст инициализации для данного преобразователя типов. Например, <Brush>Blue</Brush> преобразует значение содержимого Blue в кисть. Этот случай менее распространен.

  • Тип должен быть известным типом-примитивом языка XAML.

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

Рассмотрим следующий пример.

<StackPanel>
    <Button>First Button</Button>
    <Button>Second Button</Button>
</StackPanel>

Здесь каждый Button является дочерним элементом StackPanel. Это рациональная и интуитивно понятная разметка, в которой опущены два тега по двум разным причинам.

  • Опущен элемент свойства StackPanel.Children. StackPanel наследует от Panel. Panel определяет Panel.Children как свое свойство содержимого XAML.

  • Опущен объектный элемент UIElementCollection: свойство Panel.Children получает тип UIElementCollection, который реализует IList. Тег элемента коллекции можно опустить на основании правил XAML, касающихся обработки коллекций, например IList. (В этом случае UIElementCollection фактически не может быть создан, поскольку он не предоставляет конструктор по умолчанию, и поэтому объектный элемент UIElementCollection показан закомментированным.)

<StackPanel>
    <StackPanel.Children>
        <!--<UIElementCollection>-->
        <Button>First Button</Button>
        <Button>Second Button</Button>
        <!--</UIElementCollection>-->
    </StackPanel.Children>
</StackPanel>

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

Синтаксис атрибутов также можно использовать для элементов, которые являются событиями, а не свойствами. В этом случае имя атрибута является именем события. События для языка XAML реализованы в WPF таким образом, что значение атрибута является именем обработчика, реализующего делегат этого события. Например, в следующей разметке назначается обработчик для события Click класса Button, созданного в разметке:

<Button Click="Button_Click" >Click Me!</Button>

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

Чувствительность к регистру и пробелам в XAML

Как правило, в языке XAML учитывается регистр. При разрешении резервных типов в WPF XAML регистр учитывается по тем же правилам, что и для CLR. Объектные элементы, элементы свойств и имена атрибутов должны быть указаны с учетом регистра символов при сравнении по имени с базовым типом в сборке или с элементом типа. В ключевых словах и примитивах в языке XAML также учитывается регистр. В значениях регистр учитывается не всегда. Учет регистра для значений будет зависеть от поведения преобразователя типов, связанного со свойством, которое принимает значение, или с соответствующим типом значения свойства. Например, свойства, принимающие тип Boolean, могут принимать значения true или True как эквивалентные, но только потому, что синтаксический анализатор WPF XAML для преобразования из строкового формата в тип Boolean уже рассматривает их как эквиваленты.

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

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

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

Наиболее распространенными расширениями разметки, используемыми при программирования приложений WPF, являются Binding, используемое для выражений привязки данных, и ссылки на ресурсы StaticResource и DynamicResource. Используя расширения разметки, можно использовать синтаксис атрибутов для указания значений свойств, которые в целом не поддерживают синтаксис атрибутов. В расширениях разметки часто используются промежуточные типы выражений для включения функций, которые присутствуют только во время выполнения, например откладывание значений или ссылка на другие объекты.

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

<Window x:Class="index.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="100" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
        <Style TargetType="Border" x:Key="PageBackground">
            <Setter Property="BorderBrush" Value="Blue"/>
            <Setter Property="BorderThickness" Value="5" />
        </Style>
    </Window.Resources>
    <Border Style="{StaticResource PageBackground}">
        <StackPanel>
            <TextBlock Text="Hello" />
        </StackPanel>
    </Border>
</Window>

Список всех расширений разметки для XAML, реализованных в WPF, см. в разделе Расширения XAML WPF. Справочный список расширений разметки, определенных System.Xaml и более широко доступных для реализаций XAML .NET, см. в разделе "Возможности языка пространства имен XAML ( x:). Дополнительные сведения о понятиях расширений разметки см. в разделе Расширения разметки и XAML WPF.

Преобразователи типов

В разделе Краткое описание синтаксиса XAML было отмечено, что необходимо обеспечить возможность задать значение атрибута строкой. Основная собственная обработка преобразования строк в другие типы объектов или простые значения основана на самом типе String, а также на некоторой собственной обработке для определенных типов, например DateTime или Uri. Но многие типы WPF или члены этих типов расширяют основное поведение обработки атрибутов строк таким образом, что экземпляры более сложных типов объектов могут быть заданы как строки и атрибуты.

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

<Button Margin="10,20,10,30" Content="Click me"/>

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

<Button Content="Click me">
    <Button.Margin>
        <Thickness Left="10" Top="20" Right="10" Bottom="30"/>
    </Button.Margin>
</Button>

Примечание.

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

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

Корневые элементы и пространства имен

Файл XAML должен иметь только один корневой элемент, чтобы одновременно быть файлом XML с правильным форматом и допустимым файлом XAML. Обычно при работе с WPF используется корневой элемент, наиболее значимый в модели приложения WPF (например, Window или Page для страницы, ResourceDictionary для внешнего словаря или Application для определения приложения). В следующем примере показан корневой элемент обычного файла XAML для страницы XAML с корневым элементом Page.

<Page x:Class="index.Page1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Page1">

</Page>

Корневой элемент также содержит атрибуты xmlns и xmlns:x. Эти атрибуты указывают обработчику XAML, какие пространства имен XAML содержат определения типов для резервных типов, на которые будет ссылаться разметка в качестве элементов. Атрибут xmlns специально указывает пространство имен XAML по умолчанию. В пределах пространства имен XAML по умолчанию объектные элементы в разметке можно указывать без префикса. Для большинства сценариев приложения WPF и практически для всех примеров, приведенных в разделах WPF пакета SDK, пространство имен XAML по умолчанию сопоставлено с пространством имен WPF http://schemas.microsoft.com/winfx/2006/xaml/presentation. Атрибут xmlns:x указывает дополнительное пространство имен XAML, которое сопоставлено пространству имен языка http://schemas.microsoft.com/winfx/2006/xaml.

Это использование атрибутов xmlns для определения области использования и сопоставления области видимости имен согласовано со спецификацией XML 1.0. Области видимости имен XAML отличаются от областей видимости имен XML только тем, что область видимости имен XAML также подразумевает некоторую информацию о резервном копировании элементов области видимости имен по типам в случае разрешения типов и анализа XAML.

Атрибуты xmlns строго необходимы только для корневого элемента каждого XAML-файла. Определения xmlns будут применяться ко всем элементам-потомкам корневого элемента (такое поведение также соответствует спецификации XML 1.0 для xmlns.). Атрибуты xmlns также разрешены для других элементов ниже корня и будут применены ко всем элементам-потомкам определяющего элемента. Впрочем, частое определение или повторное определение пространств имен XAML может сделать стиль разметки XAML трудным для человеческого восприятия.

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

Префикс "x:"

В предыдущем примере корневого элемента префикс x: использовался для сопоставления пространства имен XAML http://schemas.microsoft.com/winfx/2006/xaml, которое является выделенным пространством имен XAML, поддерживающим конструкции языка XAML. Префикс x: используется для сопоставления данного пространства имен XAML в шаблонах для проектов, в примерах и в документации по всему этому пакету SDK. Пространство имен XAML для языка XAML содержит несколько конструкций программирования, которые часто используются в XAML. Ниже приведен список наиболее распространенных конструкций программирования с префиксом x:, которые могут вам потребоваться.

  • x:Key. Задает уникальный ключ для каждого ресурса в словаре ResourceDictionary (или аналогичных словарях на других платформах). x:Key применяется примерно в 90 % случаев использования сопоставления x: в разметке типичных приложений WPF.

  • x:Class: задает пространство имен и имя класса СРЕДЫ CLR для класса, который предоставляет код для страницы XAML. Согласно модели программирования WPF, такой класс должен присутствовать для поддержки кода программной части, поэтому сопоставление x: присутствует почти всегда, даже если ресурсы отсутствуют.

  • x:Name. Задает имя объекта во время выполнения для экземпляра, который существует в коде во время выполнения после обработки объектного элемента. Как правило, на практике часто используется свойство, определенное в WPF, эквивалентное x:Name. Такие свойства сопоставляются непосредственно с резервным свойством CLR, а потому более удобны для программирования приложений, где код времени выполнения часто используется для поиска именованных элементов из инициализированного XAML-кода. Наиболее распространенным свойством является FrameworkElement.Name. Можно по-прежнему использовать x:Name, когда эквивалентное свойство Name уровня WPF не поддерживается в определенном типе. Это происходит в некоторых сценариях анимации.

  • x:Static. Позволяет использовать ссылку, возвращающую статическое значение, в остальном не являющееся свойством, совместимым с XAML.

  • x:Type. Создает ссылку Type на основе имени типа. Это используется для задания атрибутов, которые принимают тип Type, например Style.TargetType, хотя часто свойство имеет встроенное преобразование из строки в тип Type и использовать расширение разметки x:Type необязательно.

Существуют дополнительные конструкции программирования в пространстве имен XAML/с префиксом x:, которые применяются не так часто. Дополнительные сведения см. в разделе Возможности пространства имен языка XAML (x:).

Пользовательские префиксы и пользовательские типы

При использовании собственных пользовательских сборок или сборок PresentationCore, PresentationFramework и WindowsBase за пределами ядра WPF можно указать сборку как часть пользовательского сопоставления xmlns. Затем можно указывать в пользовательском XAML-коде ссылки на типы из этой сборки, если в типе правильно реализована поддержка требуемых средств XAML.

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

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:NumericUpDownCustomControl;assembly=CustomLibrary"
    >
  <StackPanel Name="LayoutRoot">
    <custom:NumericUpDown Name="numericCtrl1" Width="100" Height="60"/>
...
  </StackPanel>
</Page>

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

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

События и код программной части XAML

В большинстве приложений WPF содержатся как разметка XAML, так и код программной части. В проекте XAML-код записывается как файл с расширением .xaml, а для записи файла с кодом программной части используется язык CLR, например Microsoft Visual Basic или C#. При компиляции разметки в файле XAML (в составе модели приложений и программирования WPF) расположение файла кода программной части для файла XAML определяется путем указания пространства имен и класса как атрибута x:Class корневого элемента XAML-кода.

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

<Page x:Class="ExampleNamespace.ExamplePage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <Button Click="Button_Click">Click me</Button>
    </StackPanel>
</Page>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ExampleNamespace;

public partial class ExamplePage : Page
{
    public ExamplePage() =>
        InitializeComponent();

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        var buttonControl = (Button)e.Source;
        buttonControl.Foreground = Brushes.Red;
    }
}
Class ExamplePage
    Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
        Dim buttonControl = DirectCast(e.Source, Button)
        buttonControl.Foreground = Brushes.Red
    End Sub
End Class

Обратите внимание, что в файле кода программной части используется пространство ExampleNamespace имен CLR (пространство имен не отображается в Visual Basic) и объявляется ExamplePage как частичный класс в этом пространстве имен. Это параллельно значению x:Class атрибута ExampleNamespace.ExamplePage, указанному в корневой разметке. Компилятор разметки WPF создает разделяемый класс для любого скомпилированного файла XAML путем создания класса, производного от типа корневого элемента. При предоставлении кода программной части, который также определяет тот же разделяемый класс, результирующий код объединяется внутри того же пространства имен и класса скомпилированного приложения.

Внимание

В Visual Basic корневое пространство имен подразумевается как для XAML, так и для кода. Видимы только вложенные пространства имен. В этой статье демонстрируется XAML проекта C#.

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

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

Перенаправленные события

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

Именованные элементы

По умолчанию экземпляр объекта, созданный в графе объекта путем обработки объектного элемента XAML, не имеет уникального идентификатора или ссылки на объект. Напротив, если конструктор вызывается в коде, результат конструктора почти всегда используется для указания созданного экземпляра в качестве значения переменной, чтобы на него можно было ссылаться в дальнейшем коде. Для предоставления стандартизованного доступа к объектам, созданным с помощью определения разметки, в языке XAML определяется атрибут x:Name. Можно задать значение атрибута x:Name для любого объектного элемента. В коде программной части выбранный идентификатор эквивалентен переменной экземпляра, которая ссылается на созданный экземпляр. Именованные элементы во всех отношениях действуют так, как если бы они были экземплярами объекта (имя только ссылается на этот экземпляр), и в приложении код программной части может ссылаться на именованные элементы для обработки взаимодействий времени выполнения. Эта связь между экземплярами и переменными обеспечивается компилятором разметки WPF XAML и в частности использует такие функции, как InitializeComponent, подробно не рассматриваемые в этой статье.

Элементы XAML уровня среды WPF наследуют свойство Name, которое эквивалентно определенному в XAML атрибуту x:Name. Некоторые другие классы также предоставляют эквиваленты уровня свойств для x:Name, который также обычно определяется как свойство Name. Вообще говоря, если не удается найти свойство Name в таблице членов для выбранного элемента или типа, вместо него следует использовать атрибут x:Name. Значения x:Name обеспечивают идентификатор элемента XAML, который может использоваться во время выполнения (либо определенными подсистемами, либо вспомогательными методами, такими как FindName).

В следующем примере задается Name для элемента StackPanel. Затем обработчик в Button в этом StackPanel ссылается на StackPanel через ссылку на экземпляр buttonContainer, как задано с помощью Name.

<StackPanel Name="buttonContainer">
    <Button Click="RemoveThis_Click">Click to remove this button</Button>
</StackPanel>
private void RemoveThis_Click(object sender, RoutedEventArgs e)
{
    var element = (FrameworkElement)e.Source;
    
    if (buttonContainer.Children.Contains(element))
        buttonContainer.Children.Remove(element);
}
Private Sub RemoveThis_Click(sender As Object, e As RoutedEventArgs)
    Dim element = DirectCast(e.Source, FrameworkElement)

    If buttonContainer.Children.Contains(element) Then
        buttonContainer.Children.Remove(element)
    End If
End Sub

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

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

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

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

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

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

В следующем примере показано присоединенное свойство DockPanel.Dock. Класс DockPanel определяет методы доступа для DockPanel.Dock и владеет присоединенным свойством. Класс DockPanel также включает логику, которая выполняет итерацию по дочерним элементам и, в частности, проверяет каждый элемент на заданное значение DockPanel.Dock. Если значение обнаруживается, это значение используется в процессе разметки для расположения дочерних элементов. Использование присоединенного свойства DockPanel.Dock и возможности размещения на самом деле являются мотивирующим сценарием для класса DockPanel.

<DockPanel>
    <Button DockPanel.Dock="Left" Width="100" Height="20">I am on the left</Button>
    <Button DockPanel.Dock="Right" Width="100" Height="20">I am on the right</Button>
</DockPanel>

В WPF большинство присоединенных свойств (или все такие свойства) также реализуются как свойства зависимости. Дополнительные сведения см. в разделе Общие сведения о вложенных свойствах.

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

Базовые типы

Базовая платформа XAML WPF и его пространство имен XAML представляют собой коллекцию типов, которые соответствуют объектам CLR, а также элементы разметки для XAML. Однако не все классы могут сопоставляться с элементами. Абстрактные классы, такие как ButtonBase, и некоторые неабстрактные базовые классы используются для наследования в модели объектов CLR. Базовые классы, включая абстрактные, по-прежнему важны для разработки XAML, так как каждый из элементов XAML наследует члены некоторого базового класса в своей иерархии. Часто эти члены включают в себя свойства, которые можно задать в качестве атрибутов в элементе или событий, которые могут быть обработаны. FrameworkElement — конкретный базовый класс пользовательского интерфейса WPF на уровне платформы WPF. При разработке пользовательского интерфейса следует использовать различные классы фигур, панелей, декораторов или элементов управления, которые являются производными от FrameworkElement. Связанный базовый класс FrameworkContentElement поддерживает элементы, ориентированные на документы, которые подходят для представления макета потока, используя API, намеренно дублирующие API в FrameworkElement. Сочетание атрибутов на уровне элементов и объектной модели CLR обеспечивает набор общих свойств, которые можно задать для большинства конкретных элементов XAML, независимо от типа элемента XAML и его базового типа.

Безопасность

XAML — это язык разметки, непосредственно представляющий создание и выполнение объекта. Именно поэтому элементы, созданные в XAML, имеют такие же возможности взаимодействия с системными ресурсами (например, сетевой доступ и операции ввода-вывода файловой системы), как и код приложения. XAML имеет такой же доступ к системным ресурсам, как ведущее приложение.

Управление доступом для кода (CAS) в WPF

В отличие от .NET Framework WPF для .NET не поддерживает CAS. Дополнительные сведения см. в разделе Управление доступом для кода.

Загрузка XAML из кода

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

  • включение частичной настройки;
  • локальное хранение сведений о пользовательском интерфейсе;
  • моделирование бизнес-объекта.

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

См. также