Области видимости имен XAML в WPF

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

Области имен в загруженных приложениях XAML

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

В XAML WPF, элементы, являющиеся общими корневыми элементами (например, Page и Window) всегда управляют областью имен XAML. Если такой элемент, как FrameworkElement или FrameworkContentElement, является корневым элементом страницы в разметке, процессор XAML неявно добавляет корень Page, чтобы страница Page могла предоставить работающую область имен XAML.

Примечание.

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

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

Добавление объектов в деревья объектов среды выполнения

Область имен XAML системы WPF создается и определяется в момент синтаксического анализа кода XAML. Если объект добавляется в дерево объектов уже после того, как выполнен синтаксический анализ кода XAML, сформировавшего это дерево, значение Name или x:Name нового объекта не приводит к автоматическому обновлению сведений из области имен XAML. Чтобы добавить имя объекта в область имен XAML системы WPF после загрузки кода XAML, необходимо вызвать соответствующую реализацию метода RegisterName для объекта, определяющего область имен XAML (как правило, это корневая страница XAML). Если имя не зарегистрировано, на добавленный объект нельзя ссылаться по имени с помощью таких методов, как FindName. Кроме того, такое имя нельзя использовать при подготовке для анимации.

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

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

Области имен XAML в коде

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

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

Также для любого элемента, не загруженного и не обработанного обработчиком XAML, область имен XAML для объекта не создается и не инициализируется по умолчанию. Необходимо явным образом создать область имен XAML для любого объекта, в котором впоследствии требуется регистрировать имена. Чтобы создать область имен XAML, вызывается статический метод SetNameScope. Укажите объект, являющийся его владельцем, в качестве параметра, dependencyObject и новый конструктор NameScope в качестве параметра value.

Если объект, предоставленный как dependencyObject не SetNameScope является реализацией INameScope, FrameworkElement или FrameworkContentElement, то вызов RegisterName любых дочерних элементов не будет иметь никакого эффекта. Если при создании области имен XAML произойдет сбой, то вызовы метода RegisterNameинициируют исключение.

Пример использования интерфейсов API для области имен XAML в коде см. в разделе Определение пространства имен.

Области имен XAML в стилях и шаблонах

Стили и шаблоны WPF позволяют повторно использовать и применять содержимое простым и понятным способом. Однако стили и шаблоны могут также включать элементы с именами 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="MyButtonTemplate" TargetType="{x:Type Button}">
      <Border BorderBrush="Red" Name="TheBorder" BorderThickness="2">
        <ContentPresenter/>
      </Border>      
    </ControlTemplate>
  </Page.Resources>
  <StackPanel>
    <Button Template="{StaticResource MyButtonTemplate}">My first button</Button>
    <Button Template="{StaticResource MyButtonTemplate}">My second button</Button>
  </StackPanel>
</Page>

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

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

Из-за независимых областей имен XAML поиск именованных элементов в шаблоне является более затратной задачей, чем поиск нешаблонного именованного элемента на странице. Сначала необходимо определить применяемый шаблон, получив значение свойства Template элемента управления, в котором применяется шаблон. Затем вызывается версия шаблона FindName, передающая элемент управления, в котором шаблон применяется в качестве второго параметра.

Если вы являетесь автором элемента управления и создаете соглашение, в котором конкретный именованный элемент в примененном шаблоне является целью для расширения функциональности, заданной самим элементом управления, то вы можете использовать метод GetTemplateChild из кода реализации элемента управления. Метод GetTemplateChild защищен, поэтому доступ к нему имеет только разработчик элемента управления.

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

У FrameworkElement есть методы FindName, RegisterName и UnregisterName. Если объект, для которого вызываются эти методы, имеет собственную область имен XAML, то методы вызывают методы соответствующей области имен XAML. В противном случае выполняется проверка того, владеет ли родительский элемент областью имен XAML и этот процесс продолжается рекурсивно до тех пор, пока область имен XAML не будет найдена (из-за того что поведение обработчика XAML гарантирует наличие области имен XAML в корне). FrameworkContentElement ведет себя аналогичным образом, за исключением того, что у FrameworkContentElement нет своей области имен XAML. Эти методы существуют для FrameworkContentElement, что позволяет в итоге переадресовать вызовы родительскому элементу FrameworkElement.

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

Реализации области имен XAML

Следующие классы напрямую реализуют интерфейс INameScope:

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

FrameworkTemplate и Style реализуют INameScope, используя явные определения интерфейса. Явные реализации позволяют этим областям имен XAML вести себя традиционным образом при доступе к ним через интерфейс INameScope, определяющий, как области имен XAML передаются внутренними процессами WPF. Но явные определения интерфейсов не являются частью традиционной поверхностью API для FrameworkTemplate и Style, так как непосредственно вызывать методы INameScope классов FrameworkTemplate и Style нужно редко, и вместо этого используются другие API-интерфейсы, например GetTemplateChild.

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

См. также