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 XAML e gravadores XAML poderão descobrir as características XAML do seu tipo e fornecer uma 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 instanciado como um elemento de objeto no 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 de namespace de classe ambígua e interfere em outros recursos XAML, como propriedades anexadas. Se um objeto puder ser instanciado como um elemento de objeto, o objeto criado poderá preencher a forma de elemento de propriedade de quaisquer propriedades que levem 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 valor. 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. Esse construtor inicializa todos os valores de propriedade para seus padrões.

Em alguns casos, o comportamento de construção padrão de uma estrutura não é desejável. Isso pode ocorrer porque a estrutura destina-se a preencher valores e funcionar conceitualmente como uma união. Como união, os valores contidos podem ter interpretações mutuamente exclusivas e, portanto, nenhuma de suas propriedades é configurável. Um exemplo dessa estrutura no vocabulário do WPF é GridLength. Essas estruturas devem implementar um conversor de tipo para que os valores possam ser expressos no formulário 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 um 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 tipos XAML verifica a lista atribuível e espera que o objeto fornecido como o 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 dê 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 a diretiva x:FactoryMethod.

Enumerações

As enumerações têm comportamento de conversão de tipo nativo XAML. Os nomes de constante 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 dá suporte a um uso no estilo sinalizadores para enumerações com FlagsAttribute aplicadas. Para obter mais informações, consulte a sintaxe XAML em detalhes. (A sintaxe XAML em detalhes é gravada para o público-alvo do WPF, mas a maioria das informações nesse tópico é relevante para XAML que não é específica para uma estrutura de implementação específica.)

Definições de membro

Os tipos podem definir membros para uso XAML. É possível que os tipos definam membros que podem ser usados por XAML, mesmo que esse tipo específico não seja utilizável em XAML. Isso é possível devido à herança CLR. Desde que algum tipo que herda o membro dê suporte ao uso de XAML como um tipo e o membro dê suporte ao uso de XAML para seu tipo subjacente ou tiver 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 e set acessador get típicos e palavras-chave apropriadas ao idioma, o sistema de tipos XAML poderá relatar a propriedade como um membro com as informações apropriadas fornecidas para XamlMember propriedades, como IsReadPublic e IsWritePublic.

Propriedades específicas podem habilitar uma sintaxe de texto aplicando TypeConverterAttribute. 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 indireção, como um uso de extensão de marcação, o tipo de uma propriedade (TargetType no sistema de tipos 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 o XAML 2009, a Extensão de Marcação x:Reference poderá ser usada 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 tipos 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 é uma funcionalidade XAML padrão. Na maioria dos casos, você não faz referência diretamente aos membros do método do XAML e a função dos métodos no XAML é apenas fornecer suporte para padrões XAML específicos. A diretiva x:FactoryMethod é uma exceção.

Campos

As diretrizes de design clr desencorajam campos não estáticos. Para campos estáticos, você pode acessar valores de campo estáticos somente por meio da Extensão de Marcação Estática x:; nesse caso, você não está fazendo nada especial na definição clr para expor um campo para usos x:estáticos .

Membros anexáveis

Os membros anexáveis são expostos ao XAML por meio de um padrão de método de acessador em um tipo definidor. O tipo definidor 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 para nenhuma outra função, como uma representação de interface do usuário. Para as seções a seguir, o espaço reservado PropertyName representa o nome do membro anexável. Esse nome deve ser válido na Gramática XamlName.

Tenha cuidado com as colisões de nome 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 fosse 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 do membro anexável; os usos fora do escopo pretendido gerarão exceções de conversão inválidas que, em seguida, sã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 dar suporte a uma TypeConverter sintaxe de texto habilitada para uso de atributo do membro anexável, aplique-se TypeConverterAttribute ao GetPropertyName acessador. Aplicar ao get invés do set pode parecer não intuitivo; no entanto, essa convenção pode dar suporte ao 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, conforme descrito 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 para esse método é a entrada proveniente do uso de XAML, normalmente no formulário de atributo. No formulário de atributo, deve haver suporte ao conversor de valor para uma sintaxe de texto e você atribuir no GetPropertyNameacessador s.

Repositórios de membros anexáveis

Os métodos do acessador normalmente não são suficientes para fornecer um meio de colocar valores de membro anexáveis em um grafo de objeto ou para recuperar valores do grafo do objeto e serializá-los corretamente. Para fornecer essa funcionalidade, os target objetos nas assinaturas do acessador anterior devem ser capazes de armazenar valores. O mecanismo de armazenamento deve ser consistente com o princípio de membro anexável ao qual o membro é anexado a destinos em que 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 repositórios de membros anexáveis por meio das APIs IAttachedPropertyStore e AttachablePropertyServices. IAttachedPropertyStore é usado pelos gravadores XAML para descobrir a implementação do repositório e deve ser implementado no tipo que é dos target acessadores. As APIs estáticas AttachablePropertyServices são usadas dentro do corpo dos acessadores e referem-se ao membro anexável por sua AttachableMemberIdentifier.

Atribuir corretamente seus tipos, membros e assemblies é importante para relatar informações do sistema de tipo XAML aos Serviços XAML do .NET. Relatar informações do sistema de tipo XAML será relevante 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 do .NET XAML Services e gravadores XAML.
  • Você define ou usa uma estrutura que utiliza XAML com base nesses leitores XAML e gravadores XAML.

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

Uso

O uso de tipos personalizados exige 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 internal nível de acesso. Essa funcionalidade é fornecida 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 de usuário.

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

Para um aplicativo que carrega XAML em confiança total e usa XamlObjectWriter, carregar classes com internal nível de acesso é 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 modelo do WPF) devem ser capazes de propagar quaisquer permissões de nível de acesso e preservá-las para as avaliações de tempo de execução eventuais; 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 para AssemblyAccessTo o assembly que é a origem 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 deliberadamente omite 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 GeneratedInternalTypeHelper, 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