Пространства имен XAML

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

Метод определения областей имен в языке XAML

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

Наиболее распространенным примером использования имени в области имен XAML является прямая ссылка на экземпляр объекта, который включается на этапе компиляции разметки как действие при сборке проекта в сочетании с созданным методом InitializeComponent в шаблонах разделяемых классов.

Можно также использовать вспомогательный метод FindName в среде выполнения для возвращения ссылки на объекты, для которых были определены имена в разметке XAML.

Подробнее о действиях при сборке и языке XAML

С технической точки зрения происходит следующее: XAML проходит через компилятор разметки одновременно с компилированием XAML и разделяемого класса, который он определяет для программной части кода. Каждый элемент объекта с Name или атрибутом x:Name, определенным в разметке, создает внутреннее поле с именем, которое соответствует имени XAML. Изначально это поле пустое. Затем класс создает метод InitializeComponent, который вызывается только после загрузки всего XAML. В логике InitializeComponent каждое внутреннее поле затем заполняется возвращаемым значением FindName для эквивалентной строки имени. Вы можете наблюдать эту инфраструктуру на примере файлов .g (generated), которые после компиляции создаются для каждой страницы XAML во вложенной папке /obj проекта приложения среды выполнения Windows. Кроме того, вы можете увидеть эти поля и метод InitializeComponent в результирующих сборках, если приглядитесь к ним или как-то иначе изучите содержимое, соответствующее их языку интерфейса.

Примечание Специально для приложений расширений компонентов Visual C++ (C++/CX) резервное поле для ссылки x:Name не создается для корневого элемента XAML-файла. Если возникает необходимость создать ссылку на корневой объект в коде программной части C++/CX, используйте другие API или просмотр дерева. Например, можно вызвать FindName для дочернего элемента с известным именем, а затем вызвать Parent.

Создание объектов во время выполнения с использованием XamlReader.Load

XAML можно также использовать для ввода строк при использовании метода XamlReader.Load, что дает результаты, аналогичные операции анализа исходного источника XAML. XamlReader.Load создает новое обособленное дерево объектов в среде выполнения. Обособленное дерево затем можно подключить к некоторой точке основного дерева объектов. Необходимо явным образом подключить созданное дерево объектов либо путем добавления его в коллекцию свойств содержимого (например, Children), либо путем определения какого-либо другого свойства, использующего значение объекта (например, загрузка нового ImageBrush в качестве значения свойства Fill).

Последствия XamlReader.Load для области имен XAML

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

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

Если вызвать FindName для получения именованного объекта в корневой области имен XAML, то этот метод не позволит найти объекты в дискретной области имен XAML, созданной XamlReader.Load. И наоборот, при вызове FindName из объекта, полученного из дискретной области имен XAML, этот метод не позволит найти именованные объекты в корневой области имен XAML.

Эта проблема с дискретными областями имен XAML влияет только на поиск объектов по имени в области имен XAML при вызове FindName.

Чтобы получить ссылки на объекты, определенные в другой области имен XAML, можно использовать несколько способов:

  • Пройдите по всему дереву дискретными шагами с Parent или свойствами коллекций, которые заведомо существуют в структуре вашего дерева объектов (например, коллекция, возвращаемая Panel.Children).
  • Если вы обращаетесь из дискретной области имен XAML и хотите использовать корневую область имен XAML, можно легко получить ссылку на главное окно, которое отображается в данный момент. Вы можете получить визуальный корень (корневой элемент XAML, известный также под названием "источник содержимого") из текущего окна приложения в одной строке кода с вызовом Window.Current.Content. После этого можно выполнить приведение к типу FrameworkElement и вызвать FindName из этой области.
  • Если вызов осуществляется из корневой области имен XAML и требуется объект в дискретной области имен XAML, то рекомендуется спланировать это заранее в коде и сохранить ссылку на объект, возвращенную XamlReader.Load и затем добавленную в главное дерево объектов. Теперь этот объект стал действительным объектом для вызова FindName в дискретной области имен XAML. Вы можете сохранять этот объект доступным в качестве глобальной переменной или пройти его, используя параметры метода.
  • Можно избежать проблем с именами и областями имен XAML, если изучить визуальное дерево. API VisualTreeHelper позволяет просматривать визуальное дерево на предмет родительских объектов и коллекций дочерних объектов только по признакам положения и индекса.

Пространства имен XAML в шаблонах

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

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  >
  <Page.Resources>
    <ControlTemplate x:Key="MyTemplate">
      ....
      <TextBlock x:Name="MyTextBlock" />
    </ControlTemplate>
  </Page.Resources>
  <StackPanel>
    <SomeControl Template="{StaticResource MyTemplate}" />
    <SomeControl Template="{StaticResource MyTemplate}" />
  </StackPanel>
</Page>

Здесь один шаблон применяется к двум разным элементам управления. Если у шаблонов не было дискретных областей имен XAML, то имя MyTextBlock, использованное в шаблоне, приведет к конфликту имен. Каждый экземпляр шаблона имеет свою собственную область имен XAML, поэтому в данном примере каждая область имен XAML экземпляра шаблона будет содержать ровно одно имя. Однако корневая область имен XAML не содержит имен из этих шаблонов.

Из-за наличия раздельных областей имен XAML для поиска именованных элементов в шаблоне из области страницы, на которой применен шаблон, требуется иной способ. Вместо вызова FindName для какого-либо объекта в дереве объектов следует вначале получить объект с примененным шаблоном, после чего вызвать GetTemplateChild. Если вы являетесь разработчиком элементов управления и создаете соглашение, в котором конкретный именованный элемент в примененном шаблоне является получателем поведения, определяемого самим элементом управления, можно использовать метод GetTemplateChild из кода реализации элемента управления. Метод GetTemplateChild защищен, поэтому доступ к нему имеет только разработчик элементов управления. Кроме того, разработчики элементов управления должны следовать определенным соглашениям, чтобы именовать блоки и блоки шаблонов, и сообщать о них как о значениях атрибутов, примененных к классу элементов управления. Такой способ позволяет обнаруживать имена важных блоков пользователям элемента управления, которым захочется применить другой шаблон, что потребует замены именованных блоков для сохранения функций элемента управления.