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 uso de atributo, as extensões de marcação usam a sequência de caracteres conhecida de 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 do .NET, você pode usar algumas das extensões de marcação de linguagem XAML predefinidas do assembly System.Xaml. Você também pode subclasse da MarkupExtension classe, definida em System.Xaml, e definir 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 um uso de extensão de marcação é acessado, o gravador de objeto XAML pode fornecer serviços para uma classe personalizada MarkupExtension por meio de um ponto de conexão de serviço na MarkupExtension.ProvideValue substituição. Os serviços podem ser usados para obter contexto sobre o uso, recursos específicos do gravador de objetos, contexto do 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 XAML do .NET para suporte à linguagem XAML. Essas extensões de marcação correspondem a partes da especificação de XAML como uma linguagem. Estes são tipicamente identificáveis pelo prefixo x: na sintaxe, como visto no uso comum. As implementações dos Serviços XAML do .NET para esses elementos de linguagem XAML derivam da MarkupExtension classe base.

Observação

O x: prefixo é usado para o mapeamento de namespace XAML típico do namespace de 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 x: mapeamento. Você pode escolher um token de prefixo diferente em seu próprio mapeamento de namespace XAML, mas esta documentação assumirá o mapeamento padrão como um meio de identificar as entidades que são uma parte definida do namespace XAML da linguagem XAML, em oposição ao namespace XAML padrão x: de uma estrutura específica ou a outros namespaces CLR ou XML arbitrários.

x:Type

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

x:Static

x:Static Produz valores estáticos de entidades de código de tipo de 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:Null

x:Null especifica null como um valor para um membro XAML. Dependendo do design de tipos específicos ou de conceitos de estrutura maiores, null nem sempre é 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:Reference

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 por seu x:Name. Para obter mais informações, consulte x:Reference Markup Extension.

Outros x: Construções

Existem outras x: construções 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, confira Recursos de linguagem do Namespace XAML (x:).

A classe base 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 abstrata MarkupExtension . Essa classe tem um método para substituir, que é ProvideValue. Talvez você também precise definir construtores adicionais para oferecer suporte a argumentos para o uso da extensão de marcação e propriedades configuráveis correspondentes.

Por meio ProvideValuedo , uma extensão de marcação personalizada tem acesso a um contexto de serviço que relata o ambiente em que a extensão de marcação é invocada por um processador XAML. No caminho de carregamento, isso normalmente é um XamlObjectWriterarquivo . No caminho de salvamento, isso normalmente é um XamlXmlWriterarquivo . Cada relatório relata o contexto de serviço como uma classe de contexto de provedor de serviços XAML interna 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 tipo 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

Ao usar os Serviços XAML do .NET ou estruturas que se baseiam nos Serviços XAML do .NET, você tem duas opções de como nomear o tipo de suporte da extensão de marcação. O nome do tipo é relevante para como os escritores de objeto XAML tentam acessar e invocar um tipo de suporte de extensão de marcação quando encontram um uso de extensão de marcação em XAML. Use uma das seguintes estratégias de nomenclatura:

  • Nomeie o nome do tipo para ser uma correspondência exata com o token de uso de marcação XAML. Por exemplo, para dar suporte a um uso de {Collate ...} extensão, nomeie o tipo Collatede suporte .
  • Nomeie o nome do tipo para ser o token de cadeia de caracteres de uso mais o sufixo Extension. Por exemplo, para dar suporte a um uso de {Collate ...} extensão, nomeie o tipo CollateExtensionde suporte .

A ordem de pesquisa é procurar o nome da classe -suffixed primeiro e, em seguida, procurar o nome da classe sem o Extensionsufixo Extension .

Do ponto de vista do uso da marcação, incluir o sufixo Extension como parte do uso é válido. No entanto, isso se comporta como se realmente fizesse 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 Extension a classe de suporte não tivesse o Extension sufixo.

O construtor sem parâmetros

Para todos os tipos de suporte de extensão de marcação, você deve expor um construtor público sem parâmetros. Um construtor sem parâmetros é necessário para qualquer caso em que um gravador de objeto XAML instancia a extensão de marcação a partir do uso de um elemento de objeto. O suporte ao uso do elemento objeto é uma expectativa justa para uma extensão de marcação, especialmente para serialização. No entanto, você pode implementar uma extensão de marcação sem um construtor público se você pretende apenas oferecer suporte a usos de atributo 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 oferecer suporte ao uso.

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 aos modos do uso pretendido. Em outras palavras, se sua extensão de marcação for projetada para exigir um argumento posicional como um uso válido, você deverá oferecer 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 se destina a oferecer suporte apenas a Collate um modo em que há um argumento posicional que representa seu modo, especificado como uma CollationMode constante de enumeração. Nesse 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 encaminhados a partir dos valores de atributo da marcação. Você pode criar todas as cadeias de caracteres de seus argumentos e trabalhar com entrada nesse 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. Há duas diferenças importantes:

  • Como observado anteriormente, um tipo de suporte de extensão de marcação não precisa ter 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 nesse momento.
  • Os usos de extensões de marcação podem ser aninhados. 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.

Uma dependência desse processamento foi mostrada no exemplo anterior. O gravador de objetos XAML dos Serviços XAML do .NET processa nomes constantes de enumeração em valores enumerados em um nível nativo.

O processamento da sintaxe de texto de um parâmetro posicional de extensão de marcação também pode depender de 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 houve um uso {Collate AlphaUp,{x:Reference circularFile}}, o token é enviado para o AlphaUp primeiro parâmetro e avaliado como uma CollationMode enumeração chamada constante. O resultado do interno x:Reference é enviado para o segundo parâmetro e avaliado como um objeto.

Nas regras especificadas em XAML para sintaxe e processamento de extensão de marcação, a vírgula é o delimitador entre argumentos, sejam esses argumentos posicionais ou argumentos 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 gravador de objeto 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 do design é que o contexto do esquema tem informações de tipo disponíveis para os parâmetros específicos e pode tentar casts explícitos que correspondam aos candidatos duplicados para ver qual assinatura pode ser a melhor correspondência. Uma exceção ainda pode ser lançada se nenhuma assinatura puder passar nos testes impostos por esse contexto de esquema específico que está sendo executado em um gravador de objeto XAML.

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

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, nesse caso, nomeia uma propriedade settable pública do tipo de suporte da extensão de marcação. Se você pretende oferecer suporte ao uso de argumento nomeado, você deve fornecer essas propriedades públicas configuráveis. As propriedades podem ser herdadas, desde que permaneçam públicas.

Acessando o 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. O acesso aos serviços e aos serviços disponíveis está documentado no tópico Conversores de tipo e extensões de marcação para XAML.

Uso do elemento de propriedade de uma extensão de marcação

Os cenários para usos de extensão de marcação geralmente são projetados em torno do uso da extensão de marcação no uso de atributo. No entanto, também é potencialmente possível definir a classe de suporte 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 público sem parâmetros. 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 ele 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 x:FactoryMethod Directive.) No entanto, você não deve usar esses padrões para fins de extensão de marcação porque isso torna a descoberta do padrão de uso muito mais difícil, tanto para designers quanto para usuários de marcação bruta.

Atribuindo uma extensão de marcação personalizada

Para dar 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 da extensão de marcação pretendida.

MarkupExtensionReturnTypeAttribute Relata as Type informações 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. Isso 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 em classes de destino, que podem depender da reflexão para determinar o tipo de retorno de uma extensão de marcação em vez de SetMarkupExtension ramificar em implementações conhecidas MarkupExtension específicas por nome.

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

Quando um gravador de objeto XAML processa um uso de extensão de marcação e chama ProvideValue, o contexto para que ele seja anteriormente um uso de extensão de marcação persiste no fluxo de nó XAML, mas não no gráfico de objeto. No gráfico de objeto, somente o valor é preservado. Se você tiver cenários de design ou outros motivos para persistir o uso da extensão de marcação original na saída serializada, deverá projetar sua própria infraestrutura para controlar os usos da extensão de marcação a partir do fluxo de nó XAML do caminho de carga. Você pode implementar o comportamento para recriar os elementos do fluxo de nó a partir do caminho de carregamento e reproduzi-los para gravadores XAML para serialização no caminho de salvamento, substituindo o valor na posição apropriada do fluxo de nó.

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

Se você estiver trabalhando com um fluxo de nó XAML no caminho de carregamento, um uso de extensão de marcação aparecerá no fluxo de nó como um objeto.

Se o uso da extensão de marcação usar argumentos posicionais, ele será representado como um objeto inicial com um valor de inicialização. Como uma representação de texto aproximada, o fluxo de nó é semelhante ao seguinte:

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

StartMember (nome do XamlMember é _InitializationText)

Value (value 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 dos nomes relevantes, cada conjunto com valores de cadeia de caracteres de texto.

Na verdade, invocar a ProvideValue implementação de uma extensão de marcação requer o contexto do esquema XAML porque isso requer mapeamento de tipo e criação de uma instância de tipo de suporte de extensão de marcação. Essa é uma razão pela qual os usos da extensão de marcação são preservados dessa maneira nos fluxos de nó padrão dos Serviços XAML do .NET - a parte do leitor 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 ser serializado foi originalmente fornecido por um uso de extensão de marcação e um ProvideValue resultado. Os cenários que precisam persistir os usos da extensão de marcação para ida e volta e, ao mesmo tempo, capturar outras alterações no gráfico de objeto devem criar 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, talvez seja necessário trabalhar com o fluxo de nó no caminho de salvamento para restaurar os usos da extensão de marcação ou executar algum tipo de mesclagem entre o XAML original e o XAML redondo. 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 forneciam os valores.

Confira também