Partilhar via


Esquema de Nomes XAML do WPF

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 gerido do WPF são criados ao carregar as raízes individuais de página XAML para uma aplicação XAML. Os namescopes XAML como o objeto de programação são definidos pela interface INameScope e também são implementados pela classe prática NameScope.

Namescopes em aplicações XAML carregadas

Em um contexto mais amplo de programação ou ciência da computação, os conceitos de programação geralmente incluem o princípio de um identificador exclusivo ou nome que pode ser usado para acessar um objeto. Para sistemas que usam identificadores ou nomes, o namescope define os limites dentro dos quais um processo ou técnica pesquisará se um objeto desse nome for solicitado, ou os limites em que a exclusividade dos nomes de identificação é imposta. Esses princípios gerais são verdadeiros para namescopes XAML. No WPF, 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, a partir da raiz da página, é adicionado a um namescope 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 na marcação, um processador XAML adiciona implicitamente uma raiz Page para que o Page possa fornecer um namescope XAML funcional.

Observação

As ações de compilação do WPF criam um namescope XAML para uma produção XAML, mesmo que nenhum atributo 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 WPF XAML que tem code-behind e faz parte de um aplicativo compilado, a exceção é gerada em 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 de marcação inicial. Para XAML que não é compilado como marcação por nenhuma 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 antecipar problemas de namescope XAML na fase de design.

Adicionando objetos a árvores de objetos de tempo de execução

O momento em que o XAML é analisado representa o momento no tempo em que um namescope WPF XAML é criado e definido. Se você adicionar um objeto a uma árvore de objetos em um ponto no tempo depois que o XAML que produziu essa árvore foi 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 WPF XAML depois que o XAML for 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 por 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 direcionam objetos para animações. Para obter mais informações, consulte Storyboards Visão Geral.

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 chamador está mantido, como se você tivesse chamado RegisterName no objeto definidor 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 de namescope XAML são os mesmos mesmo para um uso de código puro, porque 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 poder localizar objetos por nome dentro de uma árvore de objetos que normalmente é definida parcial ou totalmente 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 FrameworkElement ou FrameworkContentElement, para 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 pretenda registrar nomes posteriormente. Para criar um namescope XAML, chame o método SetNameScope estático. Especifique o objeto que o possuirá como o parâmetro dependencyObject e uma nova chamada do construtor NameScope como o parâmetro value.

Se o objeto fornecido como dependencyObject para SetNameScope não for uma implementação INameScope, FrameworkElement ou FrameworkContentElement, chamar RegisterName em qualquer elemento filho não terá efeito. Se você não conseguir 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 conteúdo de forma 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 onde o estilo ou modelo é 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 nomes no namescope XAML. Cada instanciação do modelo tem seu próprio namescope XAML, portanto, neste exemplo, cada namescope XAML do 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 permitem comportamentos específicos de controle que terão como destino elementos desse nome, mesmo que o modelo tenha sido redefinido como parte da personalização do controle.

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

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

Se estiveres a trabalhar a partir de um modelo e precisares de chegar ao espaço de nomes XAML onde o modelo é aplicado, obtém o valor de TemplatedParente, depois, chama FindName nesse local. Um exemplo de trabalho dentro do modelo seria se você estiver escrevendo a implementação do manipulador de eventos onde o evento será gerado a partir de um elemento em um modelo aplicado.

FrameworkElement tem FindName, RegisterName e UnregisterName métodos. Se o objeto no qual você chamar esses métodos possuir um namescope XAML, os métodos chamarão os métodos do namescope XAML relevante. Caso contrário, o elemento pai é verificado para ver se possui um namescope XAML, e esse processo continua recursivamente até que um namescope XAML seja encontrado (devido ao comportamento do processador XAML, é garantido que haja um namescope XAML na raiz). FrameworkContentElement tem comportamentos análogos, com a exceção de que nenhum FrameworkContentElement possuirá um namescope XAML. Os métodos existem em FrameworkContentElement para que as chamadas possam ser eventualmente encaminhadas para um 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 geralmente 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; ele usa chaves em vez disso, porque é uma implementação de dicionário. A única razão pela qual ResourceDictionary implementa INameScope é para que ele possa 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 namescopes XAML não sejam aplicados a um ResourceDictionary por elementos pai.

FrameworkTemplate e Style implementam INameScope por meio de definições explícitas de interface. 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 pelos processos internos do WPF. Mas as definições explícitas de interface não fazem parte da superfície de API convencional de FrameworkTemplate e Style, porque 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 à sua implementação de namescope XAML por meio da propriedade anexada NameScope.NameScope:

Ver também