Partilhar via


namescopes de XAML

Um namescope XAML armazena relações entre os nomes de objetos definidos por XAML e seus equivalentes de instância. Este conceito é semelhante ao significado mais amplo do termo namescope em outras linguagens e tecnologias de programação.

Como os namescopes XAML são definidos

Os nomes em namescopes XAML permitem que o código do usuário faça referência aos objetos que foram inicialmente declarados em XAML. O resultado interno da análise de XAML é que o tempo de execução cria um conjunto de objetos que retêm algumas ou todas as relações que esses objetos tinham nas declarações XAML. Essas relações são mantidas como propriedades de objeto específicas dos objetos criados ou são expostas a métodos de utilidade nas APIs do modelo de programação.

O uso mais típico de um nome em um namescope XAML é como uma referência direta a uma instância de objeto, que é habilitada pelo passo de compilação de marcação como uma ação de compilação de projeto, combinado com um método InitializeComponent gerado nos modelos de classe parcial.

Você também pode usar o método utilitário FindName em tempo de execução para retornar uma referência a objetos que foram definidos com um nome na marcação XAML.

Mais sobre ações de compilação e XAML

O que acontece tecnicamente é que o próprio XAML passa por uma passagem de compilador de marcação ao mesmo tempo em que o XAML e a classe parcial que ele define para code-behind são compilados juntos. Cada elemento de objeto com um atributo Name ou x:Name definido na marcação gera um campo interno com um nome que corresponde ao nome XAML. Este campo está inicialmente vazio. Em seguida, a classe gera um método InitializeComponent que é chamado somente depois que todo o XAML é carregado. Dentro da lógica InitializeComponent, cada campo interno é preenchido com o valor de retorno FindName para a string de nome equivalente. Você pode observar essa infraestrutura por si mesmo observando os arquivos ".g" (gerados) criados para cada página XAML na subpasta /obj de um projeto de aplicativo do Tempo de Execução do Windows após a compilação. Você também pode ver os campos e o método InitializeComponent como membros dos seus assemblies resultantes se os analisar através de reflexão ou examinar de outra forma o conteúdo da linguagem da interface.

Observação

Especificamente para aplicativos de extensões de componente Visual C++ (C++/CX), um campo de apoio para uma referência x:Name não é criado para o elemento raiz de um arquivo XAML. Se precisar referenciar o objeto raiz a partir do code-behind C++/CX, utilize outras APIs ou percorra a árvore. Por exemplo, você pode chamar FindName para um elemento filho nomeado conhecido e, em seguida, chamar Parent.

Criando objetos em tempo de execução com XamlReader.Load

O XAML também pode ser usado como a entrada de cadeia de caracteres para o método XamlReader.Load , que atua de forma análoga à operação inicial de análise de origem XAML. XamlReader.Load cria uma nova árvore desconectada de objetos em tempo de execução. A árvore desconectada pode então ser anexada a algum ponto na árvore de objetos principal. Você deve conectar explicitamente sua árvore de objetos criada, adicionando-a a uma coleção de propriedades de conteúdo, como Children, ou definindo alguma outra propriedade que tenha um valor de objeto (por exemplo, carregando um novo ImageBrush para um valor de propriedade Fill ).

Implicações do namescope do XamlReader.Load em XAML

O namescope XAML preliminar definido pela nova árvore de objetos criada por XamlReader.Load avalia quaisquer nomes definidos no XAML fornecido quanto à exclusividade. Se os nomes no XAML fornecido não forem internamente exclusivos neste momento, XamlReader.Load lançará uma exceção. A árvore de objetos desconectada não tenta mesclar seu namescope XAML com o namescope XAML do aplicativo principal, se ou quando estiver conectada à árvore de objetos do aplicativo principal. Depois de conectar as árvores, seu aplicativo tem uma árvore de objetos unificada, mas essa árvore tem namescopes XAML discretos dentro dela. As divisões ocorrem nos pontos de conexão entre objetos, onde você define alguma propriedade como sendo o valor retornado de uma chamada XamlReader.Load .

A complicação de ter namescopes XAML discretos e desconectados é que as chamadas para o método FindName , bem como referências diretas de objeto gerenciado, não operam mais em um namescope XAML unificado. Em vez disso, o objeto específico no qual FindName é chamado implica o escopo, com o escopo sendo o namescope XAML no qual o objeto chamador está dentro. No caso de referência de objeto gerenciado direto, o escopo é implícito pela classe onde o código existe. Normalmente, o código subjacente para interação em tempo de execução de uma "página" de conteúdo da aplicação existe na classe parcial que sustenta a "página" raiz e, portanto, o escopo de nomes XAML é o escopo de nomes XAML raiz.

Se você chamar FindName para obter um objeto nomeado no namescope XAML raiz, o método não localizará os objetos de um namescope XAML discreto criado por XamlReader.Load. Por outro lado, se você chamar FindName de um objeto obtido fora do namescope XAML discreto, o método não encontrará objetos nomeados no namescope XAML raiz.

Este problema de namescope XAML discreto afeta somente a localização de objetos por nome em namescopes XAML ao usar a chamada FindName.

Para obter referências a objetos definidos em um namescope XAML diferente, você pode usar várias técnicas:

  • Percorra toda a árvore em etapas discretas com as propriedades Pai e/ou coleção que são conhecidas por existirem na estrutura da árvore de objetos (como a coleção retornada por Panel.Children).
  • Se você estiver chamando de um namescope XAML discreto e quiser o namescope XAML raiz, é sempre fácil obter uma referência à janela principal exibida no momento. Você pode obter a raiz visual (o elemento XAML raiz, também conhecido como fonte de conteúdo) da janela atual do aplicativo em uma linha de código com a chamada Window.Current.Content. Em seguida, você pode converter para FrameworkElement e chamar FindName a partir desse escopo.
  • Se estiver a fazer uma chamada a partir do namescope XAML raiz e quiser um objeto dentro de um namescope XAML discreto, a melhor coisa a fazer é planear com antecedência o seu código e reter uma referência ao objeto que é retornado por XamlReader.Load e depois adicionado à árvore de objetos principal. Este objeto agora é um objeto válido para chamar FindName dentro do namescope XAML discreto. Você pode manter esse objeto disponível como uma variável global ou passá-lo usando parâmetros de método.
  • Você pode evitar completamente os nomes e as considerações de namescope XAML examinando a árvore visual. A VisualTreeHelper API permite percorrer a árvore visual em termos de objetos pai e coleções filho, com base puramente na posição e no índice.

Namescopes XAML em modelos

Os modelos em XAML fornecem a capacidade de reutilizar e reaplicar conteúdo de forma simples, mas os modelos também podem incluir elementos com nomes definidos no nível do modelo. Esse mesmo modelo pode ser usado várias vezes em uma página. Por esse motivo, os modelos definem seus próprios namescopes XAML, independentemente da página que contém onde o estilo ou modelo é aplicado. Considere este 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="MyTemplate">
      ....
      <TextBlock x:Name="MyTextBlock" />
    </ControlTemplate>
  </Page.Resources>
  <StackPanel>
    <SomeControl Template="{StaticResource MyTemplate}" />
    <SomeControl Template="{StaticResource MyTemplate}" />
  </StackPanel>
</Page>

Aqui, o mesmo modelo é aplicado a dois controles diferentes. Se os modelos não tivessem namescopes XAML discretos, o nome "MyTextBlock" usado no modelo causaria uma colisão de nomes. Cada instanciação do modelo tem seu próprio namescope XAML, portanto, neste exemplo, cada namescope XAML do modelo instanciado conteria exatamente um nome. No entanto, o namescope XAML raiz não contém o nome de nenhum dos templates.

Devido aos namescopes XAML separados, localizar elementos nomeados dentro de um modelo a partir do escopo da página onde o modelo é aplicado requer uma técnica diferente. Em vez de chamar FindName em algum objeto na árvore de objetos, você primeiro obtém o objeto que tem o modelo aplicado e, em seguida, chama GetTemplateChild. 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, apenas o autor do controle tem acesso a ele. Além disso, há convenções que os autores de controle devem seguir para nomear partes e partes de modelo e relatá-las como valores de atributo aplicados à classe de controle. Essa técnica torna os nomes de partes importantes detetáveis para controlar os usuários que desejam aplicar um modelo diferente, que precisaria substituir as partes nomeadas para manter a funcionalidade de controle.