Definir tipos personalizados para uso com os Serviços XAML do .NET

Quando você define tipos personalizados que são objetos de negócios ou são tipos que não têm uma dependência de estruturas específicas, há certas práticas recomendadas para XAML que você pode seguir. Se você seguir essas práticas, os Serviços XAML do .NET e seus leitores e gravadores XAML poderão descobrir as características XAML do seu tipo e dar-lhe a representação apropriada em um fluxo de nó XAML usando o sistema de tipos XAML. Este tópico descreve as práticas recomendadas para definições de tipo, definições de membro e atribuição CLR de tipos ou membros.

Padrões de construtor e definições de tipo para XAML

Para ser instanciada como um elemento de objeto em XAML, uma classe personalizada deve atender aos seguintes requisitos:

  • A classe personalizada deve ser pública e deve expor um construtor público sem parâmetros. (Consulte a seção a seguir para ver as observações sobre estruturas.)

  • A classe personalizada não deve ser uma classe aninhada. O "ponto" extra no caminho de nome completo torna a divisão class-namespace ambígua e interfere em outros recursos XAML, como propriedades anexadas. Se um objeto pode ser instanciado como um elemento de objeto, o objeto criado pode preencher o formulário de elemento de propriedade de quaisquer propriedades que tomam o objeto como seu tipo subjacente.

Você ainda pode fornecer valores de objeto para tipos que não atendem a esses critérios, se você habilitar um conversor de valores. Para obter mais informações, consulte Conversores de tipo e extensões de marcação para XAML.

Estruturas

As estruturas sempre podem ser construídas em XAML, por definição CLR. Isso ocorre porque um compilador CLR cria implicitamente um construtor sem parâmetros para uma estrutura. Este construtor inicializa todos os valores de propriedade para seus padrões.

Em alguns casos, o comportamento de construção padrão para uma estrutura não é desejável. Isso pode ser porque a estrutura se destina a preencher valores e funcionar conceitualmente como uma união. Como uma união, os valores contidos podem ter interpretações mutuamente exclusivas e, portanto, nenhuma de suas propriedades é configurável. Um exemplo de tal estrutura no vocabulário do WPF é GridLength. Tais estruturas devem implementar um conversor de tipos para que os valores possam ser expressos em forma de atributo, usando convenções de cadeia de caracteres que criam as diferentes interpretações ou modos dos valores da estrutura. A estrutura também deve expor comportamento semelhante para a construção de código por meio de um construtor sem parâmetros.

Interfaces

As interfaces podem ser usadas como tipos subjacentes de membros. O sistema de tipo XAML verifica a lista atribuível e espera que o objeto fornecido como valor possa ser atribuído à interface. Não há nenhum conceito de como a interface deve ser apresentada como um tipo XAML, desde que um tipo atribuível relevante ofereça suporte aos requisitos de construção XAML.

Métodos de Fábrica

Os métodos de fábrica são um recurso XAML 2009. Eles modificam o princípio XAML de que os objetos devem ter construtores sem parâmetros. Os métodos de fábrica não estão documentados neste artigo. Consulte x:FactoryMethod Directive.

Enumerações

As enumerações têm comportamento de conversão de tipo nativo XAML. Os nomes de constantes de enumeração especificados em XAML são resolvidos em relação ao tipo de enumeração subjacente e retornam o valor de enumeração para um gravador de objeto XAML.

O XAML oferece suporte a um uso no estilo de sinalizadores para enumerações com FlagsAttribute aplicado. Para obter mais informações, consulte Sintaxe XAML em detalhes. (A sintaxe XAML em detalhes foi escrita para o público do WPF, mas a maioria das informações nesse tópico é relevante para XAML que não é específico de uma estrutura de implementação específica.)

Definições de membros

Os tipos podem definir membros para uso de XAML. É possível que os tipos definam membros que são utilizáveis por XAML, mesmo que esse tipo específico não seja utilizável por XAML. Isso é possível devido à herança CLR. Contanto que algum tipo que herda o membro ofereça suporte ao uso de XAML como um tipo e o membro ofereça suporte ao uso de XAML para seu tipo subjacente ou tenha uma sintaxe XAML nativa disponível, esse membro poderá ser usado por XAML.

Propriedades

Se você definir propriedades como uma propriedade CLR pública usando os padrões CLR get e set acessador típicos e palavras-chave apropriadas à linguagem, o sistema de tipos XAML poderá relatar a propriedade como um membro com informações apropriadas fornecidas para XamlMember propriedades, como IsReadPublic e IsWritePublic.

Propriedades específicas podem habilitar uma sintaxe de texto aplicando TypeConverterAttributeo . Para obter mais informações, consulte Conversores de tipo e extensões de marcação para XAML.

Na ausência de uma sintaxe de texto ou conversão XAML nativa e na ausência de mais direcionamento, como um uso de extensão de marcação, o tipo de uma propriedade (TargetType no sistema de tipo XAML) deve ser capaz de retornar uma instância a um gravador de objeto XAML tratando o tipo de destino como um tipo CLR.

Se estiver usando XAML 2009, x:Reference Markup Extension poderá ser usado para fornecer valores se as considerações anteriores não forem atendidas, no entanto, isso é mais um problema de uso do que um problema de definição de tipo.

Eventos

Se você definir eventos como um evento CLR público, o sistema de tipo XAML poderá relatar o evento como um membro com IsEvent como true. A conexão dos manipuladores de eventos não está dentro do escopo dos recursos dos Serviços XAML do .NET; A fiação é deixada para estruturas e implementações específicas.

Métodos

O código embutido para métodos não é um recurso XAML padrão. Na maioria dos casos, você não faz referência direta a membros de método de XAML, e a função dos métodos em XAML é apenas fornecer suporte para padrões XAML específicos. x:FactoryMethod Directive é uma exceção.

Campos

As diretrizes de design do CLR desencorajam campos não estáticos. Para campos estáticos, você pode acessar valores de campo estático somente por meio de x:Static Markup Extension, neste caso você não está fazendo nada especial na definição CLR para expor um campo para usos x:Static .

Membros Anexáveis

Membros amovíveis são expostos ao XAML por meio de um padrão de método de acessador em um tipo definidor. O tipo de definição em si não precisa ser utilizável em XAML como um objeto. Na verdade, um padrão comum é declarar uma classe de serviço cuja função é possuir o membro anexável e implementar os comportamentos relacionados, mas não servir a nenhuma outra função, como uma representação da interface do usuário. Para as seções a seguir, o espaço reservado PropertyName representa o nome do seu membro anexável. Esse nome deve ser válido na gramática XamlName.

Tenha cuidado com colisões de nomes entre esses padrões e outros métodos de um tipo. Se existir um membro que corresponda a um dos padrões, ele poderá ser interpretado como um caminho de uso de membro anexável por um processador XAML, mesmo que essa não tenha sido sua intenção.

O acessador GetPropertyName

A assinatura do GetPropertyName acessador deve ser:

public static object GetPropertyName(object target)

  • O objeto target pode ser especificado como um tipo mais específico na sua implementação. Você pode usar isso para definir o escopo do uso de seu membro anexável; usos fora do escopo pretendido lançarão exceções de transmissão inválidas que serão exibidas por um erro de análise XAML. O nome target do parâmetro não é um requisito, mas é nomeado target por convenção na maioria das implementações.

  • O valor retornado pode ser especificado como um tipo mais específico na sua implementação.

Para oferecer suporte a uma TypeConverter sintaxe de texto habilitada para uso de atributo do membro anexável, aplique TypeConverterAttribute ao GetPropertyName acessador. Aplicar ao em vez do set pode parecer não intuitivo, no entanto, essa convenção pode oferecer suporte ao get conceito de membros anexáveis somente leitura que são serializáveis, o que é útil em cenários de designer.

O acessador SetPropertyName

A assinatura do SetPropertyName acessador deve ser:

public static void SetPropertyName(object target, object value)

  • O target objeto pode ser especificado como um tipo mais específico em sua implementação, com a mesma lógica e consequências descritas na seção anterior.

  • O objeto value pode ser especificado como um tipo mais específico na sua implementação.

Lembre-se de que o valor desse método é a entrada proveniente do uso de XAML, normalmente em forma de atributo. No formulário de atributo, deve haver suporte ao conversor de valor para uma sintaxe de texto e você atribui no GetPropertyNameacessador s.

Lojas de Membros Anexáveis

Os métodos de acessador normalmente não são suficientes para fornecer um meio de colocar valores de membro anexáveis em um gráfico de objeto ou para recuperar valores do gráfico de objeto e serializá-los corretamente. Para fornecer essa funcionalidade, os target objetos nas assinaturas de acessador anteriores devem ser capazes de armazenar valores. O mecanismo de armazenamento deve ser coerente com o princípio de membro anexável de que o membro é anexável a alvos onde o membro anexável não está na lista de membros. Os Serviços XAML do .NET fornecem uma técnica de implementação para armazenamentos de membros anexáveis por meio das APIs IAttachedPropertyStore e AttachablePropertyServicesdo . IAttachedPropertyStore é usado pelos gravadores XAML para descobrir a implementação do repositório e deve ser implementado no tipo que é o target dos acessadores. As APIs estáticas AttachablePropertyServices são usadas no corpo dos acessadores e se referem ao membro anexável por seu AttachableMemberIdentifier.

Atribuir corretamente seus tipos, membros e assemblies é importante para relatar informações do sistema do tipo XAML aos Serviços XAML do .NET. As informações do sistema do tipo XAML de relatório serão relevantes se uma das seguintes situações se aplicar:

  • Você pretende que seus tipos sejam usados com sistemas XAML que são diretamente baseados em leitores XAML dos Serviços XAML .NET e gravadores XAML.
  • Você define ou usa uma estrutura que utiliza XAML baseada nesses leitores e gravadores XAML.

Para obter uma lista de cada atributo relacionado a XAML relevante para o suporte a XAML de seus tipos personalizados, consulte Atributos CLR relacionados a XAML para tipos e bibliotecas personalizados.

Uso

O uso de tipos personalizados requer que o autor da marcação mapeie um prefixo para o assembly e o namespace CLR que contêm o tipo personalizado. Este procedimento não está documentado neste tópico.

Nível de acesso

O XAML fornece um meio de carregar e instanciar tipos que têm um nível de internal acesso. Esse recurso é fornecido para que o código do usuário possa definir seus próprios tipos e, em seguida, instanciar essas classes da marcação que também faz parte do mesmo escopo de código do usuário.

Um exemplo do WPF é sempre que o código do usuário define um que se destina a refatorar um UserControl comportamento de interface do usuário, mas não como parte de qualquer mecanismo de extensão possível que possa ser implícito declarando a classe de suporte com public nível de acesso. UserControl Isso pode ser declarado com internal acesso se o código de suporte for compilado no mesmo assembly a partir do qual ele é referenciado como um tipo XAML.

Para um aplicativo que carrega XAML sob confiança total e usa XamlObjectWriter, o carregamento de classes com internal nível de acesso está sempre habilitado.

Para um aplicativo que carrega XAML sob confiança parcial, você pode controlar as características do nível de acesso usando a XamlAccessLevel API. Além disso, os mecanismos de adiamento (como o sistema de modelos do WPF) devem ser capazes de propagar quaisquer permissões de nível de acesso e preservá-las para as eventuais avaliações de tempo de execução; Isso é tratado internamente passando as XamlAccessLevel informações.

Implementação do WPF

O WPF XAML usa um modelo de acesso de confiança parcial em que, se o BAML for carregado sob confiança parcial, o acesso será restrito ao AssemblyAccessTo assembly que é a origem do BAML. Para adiamento, o WPF usa IXamlObjectWriterFactory.GetParentSettings como um mecanismo para passar as informações de nível de acesso.

Na terminologia XAML do WPF, um tipo interno é um tipo definido pelo mesmo assembly que também inclui o XAML de referência. Esse tipo pode ser mapeado por meio de um namespace XAML que omite deliberadamente a parte assembly= de um mapeamento, por exemplo, xmlns:local="clr-namespace:WPFApplication1". Se o BAML fizer referência a um tipo interno e esse tipo tiver internal nível de acesso, isso gerará uma GeneratedInternalTypeHelper classe para o assembly. Se você quiser evitar GeneratedInternalTypeHelpero , você deve usar public o nível de acesso ou deve fatorar a classe relevante em um assembly separado e tornar esse assembly dependente.

Confira também