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 escopos de nomes XAML no código gerenciado do WPF são criados quando as raízes de páginas XAML individuais de um aplicativo XAML são carregadas. Os namescopes XAML como o objeto de programação são definidos pela INameScope interface 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 na página XAML que começa na raiz da página é adicionado a um namescope XAML pertinente.

No WPF XAML, os elementos que são elementos raiz comuns (como Page, e Window) sempre controlam um namescope XAML. Se um elemento como FrameworkElement ou FrameworkContentElement for o elemento raiz da página na marcação, um processador XAML adicionará um elemento Page raiz implicitamente para que Page possa fornecer um namescope XAML funcional.

Observação

As ações de build do WPF criam uma namescope XAML para uma produção XAML, mesmo que nenhum dos atributos Name ou x:Name esteja 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 momento da compilação por ações de build 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 de XAML também podem prever problemas de escopo de nomes XAML durante o tempo de projeto.

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 Name ou x:Name valor no novo objeto não atualizará automaticamente as informações em um namescope XAML. Para adicionar um nome a um objeto em um escopo de nomes XAML do WPF após o XAML ser carregado, você deve chamar a implementação apropriada de RegisterName no objeto que define o escopo de nomes 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 FindName, e 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 têm como objetivo objetos destinados a animações. Para obter mais informações, consulte Visão geral do Storyboards.

Se você chamar RegisterName 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 o 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 INameScope, ou ser uma classe derivada de FrameworkElement ou FrameworkContentElement, a fim de dar suporte à criação de um namescope XAML em suas 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 dependencyObject parâmetro e uma nova NameScope chamada de construtor como o value parâmetro.

Se o objeto fornecido como dependencyObject para SetNameScope não for uma implementação de INameScope, FrameworkElement ou FrameworkContentElement, invocar RegisterName em quaisquer 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 TheBorder nome 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 modelo é mais desafiador do que encontrar um elemento nomeado não modelado em uma página. Primeiro, você precisa determinar o modelo aplicado, obtendo o Template valor da propriedade do controle em que o modelo é aplicado. Em seguida, você chama a versão do modelo de FindName, passando como segundo parâmetro o controle onde o modelo foi aplicado.

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 seu código de implementação de controle. O GetTemplateChild método é protegido, portanto, somente o autor do controle tem acesso a ele.

Se você estiver trabalhando de dentro de um modelo e precisar acessar o namescope XAML em que o modelo é aplicado, obtenha o valor de TemplatedParente, em seguida, chame-o FindName . 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 FindNamee RegisterNameUnregisterName métodos. Se o objeto no qual você chama esses métodos possui um namescope XAML, então os métodos invocam os métodos do namescope 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 nenhum FrameworkContentElement nunca terá um namescope XAML. Os métodos existem em FrameworkContentElement para que as chamadas possam ser encaminhadas para um elemento pai FrameworkElement eventualmente.

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 no 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 é para poder gerar exceções ao código do usuário que ajudem a esclarecer a distinção entre um namescope XAML verdadeiro e como um ResourceDictionary lida com chaves, assegurando também que os namescopes XAML não sejam aplicados a um ResourceDictionary por elementos pai.

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

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

Consulte também