Partilhar via


Visão geral das extensões de marcação para XAML

As extensões de marcação são uma técnica XAML para obter um valor que não é um tipo XAML primitivo ou específico. Para o uso de atributos, as extensões de marcação utilizam a sequência de caracteres conhecida, com uma chave de abertura { para entrar no escopo da extensão de marcação e uma chave de fechamento } para sair. Ao usar os Serviços XAML .NET, você pode usar algumas das extensões de marcação de linguagem XAML predefinidas do assembly System.Xaml. Você também pode subclassificar a partir da classe MarkupExtension, definida em System.Xaml, e definir as suas próprias extensões de marcação. Ou você pode usar extensões de marcação definidas por uma estrutura específica se já estiver fazendo referência a essa estrutura.

Quando uma utilização de extensão de marcação é acedida, o gravador de objeto XAML pode fornecer serviços a uma classe personalizada de MarkupExtension através de um ponto de conexão de serviço na substituição de MarkupExtension.ProvideValue. Os serviços podem ser usados para obter contexto sobre o uso, recursos específicos do gravador de objetos, contexto de esquema XAML e assim por diante.

Extensões de marcação definidas por XAML

Várias extensões de marcação são implementadas pelos Serviços .NET XAML para suporte à linguagem XAML. Essas extensões de marcação correspondem a partes da especificação de XAML como uma linguagem. Estes são normalmente identificáveis pelo prefixo x: na sintaxe, como visto no uso comum. Todas as implementações dos Serviços XAML .NET para esses elementos de linguagem XAML derivam da classe base MarkupExtension.

Observação

O prefixo x: é utilizado para o mapeamento típico do namespace da linguagem XAML no elemento raiz de uma produção XAML. Por exemplo, o projeto do Visual Studio e os modelos de página para várias estruturas específicas iniciam um arquivo XAML usando esse mapeamento de x:. Você pode escolher um token de prefixo diferente no seu próprio mapeamento de namespace XAML, contudo, esta documentação assumirá o mapeamento padrão x: como um meio de identificar as entidades definidas do namespace da linguagem XAML, em oposição ao namespace XAML padrão de uma estrutura específica ou quaisquer outros namespaces CLR ou XML arbitrários.

x:Tipo

x:Type fornece o objeto Type para o tipo nomeado. Essa funcionalidade é usada com mais freqüência em mecanismos de adiamento que usam o tipo CLR subjacente e a derivação de tipo como um moniker ou identificador de agrupamento. Estilos e modelos do WPF, e seu uso de propriedades TargetType, são um exemplo específico. Para obter mais informações, consulte x:Type Markup Extension.

x:Estático

x:Static produz valores estáticos a partir de entidades de código de tipo valor que não são diretamente o tipo de valor de uma propriedade, mas podem ser avaliadas para esse tipo. Isso é útil para especificar valores que já existem como constantes conhecidas em uma definição de tipo. Para obter mais informações, consulte x:Static Markup Extension.

x:Nulo

x:Null especifica null como um valor para um membro XAML. Dependendo do design de tipos específicos ou de conceitos de estrutura maiores, nem sempre null é um valor padrão para uma propriedade ou o valor implícito de um atributo de cadeia de caracteres vazio. Para obter mais informações, consulte x:Null Markup Extension.

x:Array

x:Array oferece suporte à criação de matrizes gerais na sintaxe XAML nos casos em que o suporte à coleção fornecido por elementos base e modelos de controle não é usado deliberadamente. Para obter mais informações, consulte x:Array Markup Extension. No XAML 2009 especificamente, as matrizes são acessadas como primitivas de linguagem em vez de como uma extensão. Para obter mais informações, consulte Recursos de linguagem XAML 2009.

x:Referência

x:Reference faz parte do XAML 2009, uma extensão do conjunto de idiomas original (2006). x:Reference representa uma referência a outro objeto existente em um gráfico de objeto. Esse objeto é identificado pelo seu x:Name. Para obter mais informações, consulte x:Reference Markup Extension.

Outros x: Estruturas

Existem outras construções x: para dar suporte a recursos de linguagem XAML, mas elas não são implementadas como extensões de marcação. Para obter mais informações, consulte Recursos de linguagem do namespace XAML (x:).

A Classe Base de MarkupExtension

Para definir uma extensão de marcação personalizada que possa interagir com as implementações padrão de leitores XAML e gravadores XAML em System.Xaml, derive uma classe da classe MarkupExtension abstrata. Essa classe tem um método para substituir, que é ProvideValue. Também pode ser necessário definir construtores adicionais para dar suporte a argumentos para o uso da extensão de marcação e propriedades configuráveis correspondentes.

Por meio ProvideValue, uma extensão de marcação personalizada tem acesso a um contexto de serviço que relata o ambiente onde a extensão de marcação é invocada por um processador XAML. No caminho de carga, normalmente é um XamlObjectWriter. No caminho de salvamento, normalmente é um XamlXmlWriter. Cada relatório descreve o contexto de serviço como uma classe interna de contexto de provedor de serviços XAML que implementa um padrão de provedor de serviços. Para obter mais informações sobre os serviços disponíveis e o que eles representam, consulte Conversores de tipos e extensões de marcação para XAML.

Sua classe de extensão de marcação deve usar um nível de acesso público; Os processadores XAML sempre devem ser capazes de instanciar a classe de suporte da extensão de marcação para usar seus serviços.

Definindo o tipo de suporte para uma extensão de marcação personalizada

Quando utilizas Serviços XAML .NET ou frameworks que se baseiam nos Serviços XAML .NET, tens duas opções para nomear o tipo de suporte de extensão de marcação. O nome do tipo é relevante para como os criadores de objetos XAML tentam aceder e invocar um tipo de suporte para extensão de marcação quando encontram uma utilização da extensão de marcação em XAML. Use uma das seguintes estratégias de nomenclatura:

  • Dê ao tipo um nome que corresponda exatamente ao token de uso da marcação XAML. Por exemplo, para dar suporte a um uso de extensão {Collate ...}, nomeie o tipo de suporte Collate.
  • Nomeie o nome do tipo como sendo o token de cadeia de caracteres de uso mais o sufixo Extension. Por exemplo, para dar suporte a um uso de extensão {Collate ...}, nomeie o tipo de suporte CollateExtension.

A ordem de pesquisa é, primeiro, procurar pelo nome da classe com o sufixo Extensione, em seguida, procurar pelo nome da classe sem o sufixo Extension.

Do ponto de vista da utilização da marcação, é válido incluir o sufixo Extension como parte dessa utilização. No entanto, isso se comporta como se Extension fosse realmente parte do nome da classe, e os gravadores de objeto XAML não conseguiriam resolver uma classe de suporte de extensão de marcação para esse uso se a classe de suporte não tivesse o sufixo Extension.

O construtor sem parâmetros

Para todos os tipos de suporte de extensão de marcação, você deve expor um construtor sem parâmetros público. Um construtor sem parâmetros é necessário para qualquer caso em que um gravador de objeto XAML instancie a extensão de marcação a partir do uso de um elemento de objeto. O suporte ao uso de elementos de objeto é uma expectativa justa para uma extensão de marcação, particularmente para serialização. No entanto, você pode implementar uma extensão de marcação sem um construtor público se você só pretende dar suporte a usos de atributos da extensão de marcação.

Se o uso da extensão de marcação não tiver argumentos, o construtor sem parâmetros será necessário para suportar esta utilização.

Padrões de construtor e argumentos posicionais para uma extensão de marcação personalizada

Para uma extensão de marcação com uso de argumento pretendido, os construtores públicos devem corresponder às formas de utilização desejadas. Em outras palavras, se sua extensão de marcação foi projetada para exigir um argumento posicional como um uso válido, você deve dar suporte a um construtor público com um parâmetro de entrada que usa o argumento posicional.

Por exemplo, suponha que a extensão de marcação Collate se destina a suportar apenas um modo onde há um argumento posicional que representa seu modo, especificado como uma constante de enumeração CollationMode. Neste caso, deve haver um construtor com a seguinte forma:

public Collate(CollationMode collationMode) {...}

Em um nível básico, os argumentos passados para uma extensão de marcação são uma cadeia de caracteres porque estão sendo transferidos a partir dos valores de atributo da marcação. Você pode criar todos os seus argumentos em formato de cadeias de caracteres e trabalhar com a entrada a esse nível. No entanto, você tem acesso a determinado processamento que ocorre antes que os argumentos de extensão de marcação sejam passados para a classe de suporte.

O processamento funciona conceitualmente como se a extensão de marcação fosse um objeto a ser criado e, em seguida, seus valores de membro são definidos. Cada propriedade especificada a ser definida é avaliada de forma semelhante a como um membro especificado pode ser definido em um objeto criado quando o XAML é analisado. Existem duas diferenças importantes:

  • Como observado anteriormente, não é necessário que um tipo de suporte para extensão de marcação tenha um construtor sem parâmetros para ser instanciado em XAML. Sua construção de objeto é adiada até que seus possíveis argumentos na sintaxe do texto sejam tokenizados e avaliados como argumentos posicionais ou nomeados, e o construtor apropriado seja chamado naquele momento.
  • As extensões de marcação podem ser usadas de forma aninhada. A extensão de marcação mais interna é avaliada primeiro. Portanto, você pode assumir esse uso e declarar um dos parâmetros de construção como um tipo que requer um conversor de valor (como uma extensão de marcação) para produzir.

A confiança nesse processamento foi demonstrada no exemplo anterior. O gravador de objetos XAML dos Serviços XAML .NET processa nomes constantes de enumeração em valores enumerados a nível nativo.

O processamento da sintaxe de texto de um parâmetro posicional numa extensão de marcação também pode contar com um conversor de tipo associado ao tipo que está no argumento de construção.

Os argumentos são chamados de argumentos posicionais porque a ordem na qual os tokens no uso são encontrados corresponde à ordem posicional do parâmetro do construtor ao qual eles são atribuídos. Por exemplo, considere a seguinte assinatura do construtor:

public Collate(CollationMode collationMode, object collateThis) {...}

Um processador XAML espera dois argumentos posicionais para essa extensão de marcação. Se existiu um {Collate AlphaUp,{x:Reference circularFile}}de uso, o token AlphaUp é enviado para o primeiro parâmetro e avaliado como uma enumeração CollationMode denominada constante. O resultado do x:Reference interno é enviado para o segundo parâmetro e avaliado como um objeto.

Nas regras especificadas do XAML para sintaxe e processamento de extensão de marcação, a vírgula é o delimitador entre argumentos, sejam eles argumentos posicionais ou nomeados.

Duplicação de argumentos posicionais

Se um gravador de objeto XAML encontrar um uso de extensão de marcação com argumentos posicionais, e houver vários argumentos de construtor que usam esse número de argumentos (uma aridade duplicada), isso não é necessariamente um erro. O comportamento depende de uma configuração de contexto de esquema XAML personalizável, SupportMarkupExtensionsWithDuplicateArity. Se SupportMarkupExtensionsWithDuplicateArity for true, um escritor de objetos XAML não deve lançar uma exceção apenas por motivos de aridade duplicada. O comportamento além desse ponto não é estritamente definido. A suposição básica de design é que o contexto do esquema tem informações de tipo disponíveis para os parâmetros específicos e pode tentar conversões explícitas que se adequem aos candidatos duplicados para ver qual assinatura pode ser a melhor combinação. Uma exceção ainda pode ser lançada se nenhuma assinatura conseguir passar nos testes definidos pelo contexto de esquema específico que está a ser executado num escritor de objetos XAML.

Por padrão, SupportMarkupExtensionsWithDuplicateArity é false no XamlSchemaContext baseado em CLR para os Serviços XAML do .NET. Assim, o XamlObjectWriter padrão lança exceções se encontrar um uso de extensão de marcação onde há aridade duplicada nos construtores do tipo subjacente.

Argumentos nomeados para uma extensão de marcação personalizada

As extensões de marcação, conforme especificado pelo XAML, também podem usar um formulário de argumentos nomeados para uso. No primeiro nível de tokenização, a sintaxe do texto é dividida em argumentos. A presença de um sinal de igual (=) dentro de qualquer argumento identifica um argumento como um argumento nomeado. Tal argumento também é tokenizado em um par nome/valor. O nome, neste caso, designa uma propriedade pública configurável do tipo de suporte à extensão de marcação. Se pretendes dar suporte ao uso de argumentos nomeados, deves fornecer essas propriedades públicas configuráveis. As propriedades podem ser propriedades herdadas desde que permaneçam públicas.

Aceder ao contexto do provedor de serviços a partir de uma implementação de extensão de marcação

Os serviços disponíveis são os mesmos para qualquer conversor de valor. A diferença está em como cada conversor de valor recebe o contexto de serviço. Como aceder aos serviços, bem como os serviços disponíveis, está documentado no tópico Conversores de Tipo e Extensões de Marcação para XAML.

Uso de elemento de propriedade numa extensão de marcação

Os cenários para a utilização de extensões de marcação são frequentemente concebidos com foco no seu uso em atributos. No entanto, também é eventualmente possível definir a classe de apoio para dar suporte ao uso do elemento de propriedade.

Para dar suporte ao uso do elemento de propriedade de sua extensão de marcação, defina um construtor sem parâmetros público. Este deve ser um construtor de instância e não um construtor estático. Isso é necessário porque um processador XAML geralmente deve invocar o construtor sem parâmetros em qualquer elemento de objeto que processa a partir da marcação, e isso inclui classes de extensão de marcação como elementos de objeto. Para cenários avançados, você pode definir caminhos de construção não padrão para classes. (Para obter mais informações, consulte Diretiva x:FactoryMethod.) No entanto, você não deve usar esses padrões para fins de extensão de marcação, pois isso torna a descoberta do padrão de uso muito mais difícil, tanto para designers quanto para usuários de marcação bruta.

Atribuição para uma extensão de marcação personalizada

Para oferecer suporte a ambientes de design e determinados cenários de gravador de objetos XAML, você deve atribuir um tipo de suporte de extensão de marcação com vários atributos CLR. Esses atributos relatam o uso pretendido da extensão de marcação.

MarkupExtensionReturnTypeAttribute reporta a informação de Type para o tipo de objeto que ProvideValue retorna. Por sua assinatura pura, ProvideValue retorna Object. Mas vários consumidores podem querer informações mais precisas sobre o tipo de devolução. Isto inclui:

  • Designers e IDEs, que podem fornecer suporte com reconhecimento de tipo para usos de extensão de marcação.
  • Implementações avançadas de manipuladores de SetMarkupExtension em classes de destino, que podem recorrer à reflexão para determinar o tipo de retorno de uma extensão de marcação em vez de dividir-se em implementações específicas de MarkupExtension conhecidas pelo nome.

Serialização dos usos da extensão de marcação

Quando um escritor de objetos XAML processa um uso de extensão de marcação e chama ProvideValue, o contexto de ter sido um uso de extensão de marcação persiste no fluxo de nós XAML, mas não no grafo de objetos. No gráfico de objetos, apenas o valor é preservado. Se tiver cenários de design ou outros motivos para manter o uso da extensão de marcação original no resultado serializado, deverá projetar a sua própria infraestrutura para controlar os usos da extensão de marcação do fluxo de nós XAML no caminho de carregamento. Você pode implementar um comportamento que recria os elementos do fluxo de nós a partir do caminho de carregamento e transmiti-los aos gravadores XAML para serialização no processo de salvamento, substituindo o valor na posição apropriada do fluxo de nó.

Extensões de marcação no fluxo de nós XAML

Se estiveres a trabalhar com um fluxo de nó XAML durante o processo de carregamento, a utilização de uma extensão de marcação aparecerá no fluxo de nó como um objeto.

Se a utilização da extensão de marcação empregar argumentos posicionais, é representada como um objeto de início com um valor inicial. Como representação aproximada de texto, o fluxo de nós é semelhante ao seguinte:

StartObject (XamlType é o tipo de definição da extensão de marcação, não seu tipo de retorno)

StartMember (nome do XamlMember é _InitializationText)

Value (valor são os argumentos posicionais como uma cadeia de caracteres, incluindo os delimitadores intervenientes)

EndMember

EndObject

Um uso de extensão de marcação com argumentos nomeados é representado como um objeto com membros cujos nomes são relevantes, cada um configurado com valores de cadeia de caracteres.

De facto, invocar a implementação de ProvideValue de uma extensão de marcação requer o contexto do esquema XAML, pois requer o mapeamento de tipo e a criação de uma instância de tipo de suporte para a extensão de marcação. Essa é uma das razões pelas quais os usos das extensões de marcação são preservados dessa maneira nos fluxos de nós padrão dos Serviços XAML do .NET - a componente leitora de um caminho de carregamento geralmente não tem o contexto de esquema XAML necessário disponível.

Se você estiver trabalhando com um fluxo de nó XAML no caminho de salvamento, geralmente não há nada presente em uma representação de gráfico de objeto que possa informá-lo de que o objeto a serializar foi originalmente fornecido por um uso de extensão de marcação e um resultado ProvideValue. Os cenários que precisam persistir os usos de extensão de marcação para ida e volta e, ao mesmo tempo, capturar outras alterações no gráfico de objetos devem desenvolver suas próprias técnicas para preservar o conhecimento do uso de uma extensão de marcação da entrada XAML original. Por exemplo, para restaurar os usos da extensão de marcação, pode ser necessário trabalhar com o fluxo de nós no caminho de gravação, ou realizar algum tipo de mesclagem entre o XAML original e o XAML de retorno. Algumas estruturas de implementação de XAML, como WPF, usam tipos intermediários (expressões) para ajudar a representar casos em que os usos de extensão de marcação forneceram os valores.

Ver também