Compartilhar via


WPF XAML Namescopes

Os namescopes XAML são um conceito que identifica objetos definidos em XAML. Os nomes em um namescope XAML podem ser usados para estabelecer relações entre os nomes de objetos definidos por XAML e seus equivalentes de instância em uma árvore de objetos. Normalmente, os namescopes XAML no código gerenciado do WPF são criados ao carregar as raízes de página XAML individuais para um aplicativo XAML. Os namescopes XAML como objeto de programação são definidos pela interface INameScope e também são implementados pela classe prática NameScope.

Namescopes em aplicativos XAML carregados

Em um contexto de programação ou ciência da computação mais amplo, os conceitos de programação geralmente incluem o princípio de um identificador ou nome exclusivo que pode ser usado para acessar um objeto. Para sistemas que usam identificadores ou nomes, o namescope define os limites nos quais um processo ou técnica pesquisará se um objeto desse nome for solicitado ou os limites nos quais a exclusividade da identificação de nomes é imposta. Esses princípios gerais são verdadeiros para namescopes XAML. No WPF, os namescopes XAML são criados no elemento raiz de uma página XAML quando a página é carregada. Cada nome especificado dentro da página XAML a partir da raiz da página é adicionado a um escopo de nomes XAML pertinente.

No WPF XAML, os elementos que são elementos raiz comuns (como Pagee Window) sempre controlam um namescope XAML. Se um elemento como FrameworkElement ou FrameworkContentElement for o elemento raiz da página no código, um processador XAML adicionará implicitamente uma raiz Page para que o Page possa fornecer um namescope XAML funcional.

Nota

As ações de build do WPF criam um namescope XAML para uma produção XAML, mesmo que nenhum atributo de Name ou x:Name seja definido em nenhum elemento na marcação XAML.

Se você tentar usar o mesmo nome duas vezes em qualquer namescope XAML, uma exceção será gerada. Para o WPF XAML que tem code-behind e faz parte de um aplicativo compilado, a exceção é gerada no tempo de compilação por ações de compilação do WPF, ao criar a classe gerada para a página durante a compilação inicial da marcação. Para XAML que não é compilado por qualquer ação de build, exceções relacionadas a problemas de namescope XAML podem ser geradas quando o XAML é carregado. Os designers XAML também podem antecipar problemas de escopo de nomes XAML durante a fase de design.

Adicionando objetos a árvores de objetos de runtime

O momento em que o XAML é analisado representa o momento em que um namescope XAML do WPF é criado e definido. Se você adicionar um objeto a uma árvore de objetos em um ponto no tempo após o XAML que produziu essa árvore ter sido analisado, um valor Name ou x:Name no novo objeto não atualizará automaticamente as informações em um namescope XAML. Para adicionar um nome para um objeto em um namescope XAML do WPF após o XAML ser carregado, você deve chamar a implementação apropriada de RegisterName no objeto que define o namescope XAML, que normalmente é a raiz da página XAML. Se o nome não estiver registrado, o objeto adicionado não poderá ser referenciado pelo nome por meio de métodos como FindNamee você não poderá usar esse nome para direcionamento de animação.

O cenário mais comum para desenvolvedores de aplicativos é que você usará RegisterName para registrar nomes no namescope XAML na raiz atual da página. RegisterName faz parte de um cenário importante para storyboards que direcionam objetos para animações. Para obter mais informações, consulte Visão Geral de Storyboards.

Se você chamar RegisterName em um objeto diferente do objeto que define o namescope XAML, o nome ainda será registrado no namescope XAML no qual o objeto de chamada é mantido, como se você tivesse chamado RegisterName no objeto de definição do namescope XAML.

Namescopes XAML no código

Você pode criar e usar namescopes XAML no código. As APIs e os conceitos envolvidos na criação do namescope XAML são iguais até mesmo para um uso de código puro, pois o processador XAML para WPF usa essas APIs e conceitos quando processa o próprio XAML. Os conceitos e a API existem principalmente com a finalidade de localizar objetos por nome dentro de uma árvore de objetos que normalmente é definida parcial ou inteiramente em XAML.

Para aplicativos criados programaticamente e não a partir de XAML carregado, o objeto que define um namescope XAML deve implementar INameScopeou ser uma classe derivada de FrameworkElement ou FrameworkContentElement, para suportar a criação de um namescope XAML em instâncias.

Além disso, para qualquer elemento que não seja carregado e processado por um processador XAML, o namescope XAML para o objeto não é criado ou inicializado por padrão. Você deve criar explicitamente um novo namescope XAML para qualquer objeto no qual pretende registrar nomes posteriormente. Para criar um namescope XAML, você chama o método estático SetNameScope. Especifique o objeto que o possuirá como o parâmetro dependencyObject e uma nova chamada de construtor NameScope como o parâmetro value.

Se o objeto fornecido como dependencyObject para SetNameScope não for uma implementação de INameScope, FrameworkElement ou FrameworkContentElement, a chamada de RegisterName em qualquer um dos elementos filho não terá efeito. Se você não criar o novo namescope XAML explicitamente, as chamadas para RegisterName gerarão uma exceção.

Para obter um exemplo de como usar APIs de namescope XAML no código, consulte Definir um escopo de nome.

Namescopes XAML em estilos e modelos

Estilos e modelos no WPF fornecem a capacidade de reutilizar e reaplicar o conteúdo de maneira simples. No entanto, estilos e modelos também podem incluir elementos com nomes XAML definidos no nível do modelo. Esse mesmo modelo pode ser usado várias vezes em uma página. Por esse motivo, estilos e modelos definem seus próprios namescopes XAML, independentemente de qualquer local em uma árvore de objetos em que o estilo ou modelo seja aplicado.

Considere o seguinte exemplo:

<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>

Aqui, o mesmo modelo é aplicado a dois botões diferentes. Se os modelos não tivessem namescopes XAML discretos, o nome TheBorder usado no modelo causaria uma colisão de nome no namescope XAML. Cada instanciação do modelo tem seu próprio namescope XAML, portanto, neste exemplo, cada namescope XAML de cada modelo instanciado conteria exatamente um nome.

Os estilos também definem seu próprio namescope XAML, principalmente para que partes de storyboards possam ter nomes específicos atribuídos. Esses nomes habilitam comportamentos específicos de controle que serão direcionados a elementos desse nome, mesmo que o modelo tenha sido redefinido como parte da personalização do controle.

Devido aos NameScopes XAML separados, encontrar elementos nomeados em um template é mais desafiador do que encontrar um elemento nomeado que não faz parte de um template em uma página. Primeiro, você precisa determinar o modelo aplicado, obtendo o valor da propriedade Template do controle em que o modelo é aplicado. Em seguida, você chama a versão do template de FindName, passando o controle para onde o template foi aplicado como o segundo parâmetro.

Se você for um autor de controle e estiver gerando uma convenção em que um determinado elemento nomeado em um modelo aplicado é o destino de um comportamento definido pelo próprio controle, você pode usar o método GetTemplateChild do código de implementação de controle. O método GetTemplateChild é protegido, portanto, somente o autor do controle tem acesso a ele.

Se você estiver trabalhando em um modelo e precisar acessar o namescope XAML em que o modelo é aplicado, obtenha o valor de TemplatedParente chame FindName lá. Um exemplo de trabalho dentro do modelo seria se você estivesse escrevendo a implementação do manipulador de eventos em que o evento será gerado de um elemento em um modelo aplicado.

FrameworkElement tem métodos FindName, RegisterName e UnregisterName. Se o objeto no qual você chama esses métodos possui uma identidade de nomes XAML, eles invocarão os métodos da identidade de nomes XAML relevante. Caso contrário, o elemento pai será verificado para ver se ele possui um namescope XAML e esse processo continua recursivamente até que um namescope XAML seja encontrado (devido ao comportamento do processador XAML, há garantia de que haja um namescope XAML na raiz). FrameworkContentElement tem comportamentos análogos, com a exceção de que nenhuma FrameworkContentElement terá um namescope XAML. Os métodos existem em FrameworkContentElement para que as chamadas possam ser encaminhadas posteriormente para o elemento pai FrameworkElement.

SetNameScope é usado para mapear um novo namescope XAML para um objeto existente. Você pode chamar SetNameScope mais de uma vez para redefinir ou limpar o namescope XAML, mas esse não é um uso comum. Além disso, GetNameScope normalmente não é usado em código.

Implementações de namescope XAML

As seguintes classes implementam INameScope diretamente:

ResourceDictionary não usa nomes XAML ou namescopes; em vez disso, ele usa chaves, porque é uma implementação de dicionário. O único motivo pelo qual ResourceDictionary implementa INameScope é que ele pode gerar exceções ao código do usuário que ajudam a esclarecer a distinção entre um namescope XAML verdadeiro e como um ResourceDictionary manipula chaves e também para garantir que os namescopes XAML não sejam aplicados a um ResourceDictionary por elementos pai.

FrameworkTemplate e Style implementam INameScope através de definições de interface explícitas. As implementações explícitas permitem que esses namescopes XAML se comportem convencionalmente quando são acessados por meio da interface INameScope, que é como os namescopes XAML são comunicados por processos internos do WPF. Mas as definições de interface explícitas não fazem parte da superfície convencional da API de FrameworkTemplate e Style, pois você raramente precisa chamar os métodos INameScope em FrameworkTemplate e Style diretamente e, em vez disso, usaria outra API, como GetTemplateChild.

As classes a seguir definem seu próprio namescope XAML usando a classe auxiliar System.Windows.NameScope e conectando-se à implementação do namescope XAML por meio da propriedade NameScope.NameScope anexada:

Consulte também