Compartilhar via


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ós 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 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 obter notas 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 todas as 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 valor. Para obter mais informações, consulte Conversores de Tipo e Extensões de Marcação paraXAML.

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 de 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 diretiva x:FactoryMethod.

Enumerações

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 aplicado. Para obter mais informações, consulte sintaxe XAML em detalhes. (sintaxe XAML em detalhes é escrita para o público do WPF, mas a maioria das informações nesse tópico é relevante para XAML que não é específico 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 são utilizáveis em 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 XAML para seu tipo subjacente ou tenha uma sintaxe XAML nativa disponível, esse membro é utilizável em XAML.

Propriedades

Se você definir propriedades como uma propriedade CLR pública usando os padrões típicos de get clr e set acessador e palavras-chave apropriadas ao idioma, o sistema de tipos XAML poderá relatar a propriedade como um membro com informações apropriadas fornecidas para propriedades XamlMember, 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 paraXAML.

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 para um gravador de objeto XAML tratando o tipo de destino como um tipo CLR.

Se estiver usando o XAML 2009, Extensão de Marcação x:Reference 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 é uma funcionalidade XAML padrão. Na maioria dos casos, você não faz referência diretamente aos membros do método XAML e a função dos métodos no XAML é fornecer suporte apenas para padrões XAML específicos. da 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 de de extensão de marcação x:Static; nesse caso, você não está fazendo nada especial na definição clr para expor um campo para usos de x:Static.

Membros anexáveis

Os membros anexáveis são expostos ao XAML por meio de um padrão de método acessador em um tipo de definição. 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 membro anexável. Esse nome deve ser válido no gramática XamlName .

Tenha cuidado com 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 acessador GetPropertyName deve ser:

public static object GetPropertyName(object target)

  • O objeto target pode ser especificado como um tipo mais específico em sua implementação. Você pode usá-lo 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 do parâmetro target 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 em sua implementação.

Para dar suporte a uma sintaxe de texto TypeConverter habilitada para uso de atributo do membro anexável, aplique TypeConverterAttribute ao acessador GetPropertyName. Aplicar ao get em vez 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 acessador SetPropertyName deve ser:

public static void SetPropertyName(object target, object value)

  • O objeto target 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 em sua implementação.

Lembre-se de que o valor desse 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 acessador do GetPropertyName.

Repositórios de membros anexáveis

Os métodos acessadores normalmente não são suficientes para fornecer um meio de colocar valores de membro anexáveis em um grafo de objeto ou recuperar valores do grafo de objeto e serializá-los corretamente. Para fornecer essa funcionalidade, os objetos target 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 de que o membro é anexável 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 é o target dos acessadores. As APIs de AttachablePropertyServices estáticas são usadas dentro do corpo dos acessadores e referem-se ao membro anexável por seu 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 dos Serviços XAML do .NET e gravadores XAML.
  • Você define ou usa uma estrutura que utiliza XAML baseada 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 XAML-Related atributos CLR 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 acesso internal. 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 do usuário.

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

Para um aplicativo que carrega XAML em total confiança e usa XamlObjectWriter, carregar 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 de nível de acesso usando a API XamlAccessLevel. 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 informações de XamlAccessLevel.

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 a AssemblyAccessTo para 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, uma 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 classe GeneratedInternalTypeHelper para o assembly. Se quiser evitar GeneratedInternalTypeHelper, você deve usar public nível de acesso ou deve fatorar a classe relevante em um assembly separado e tornar esse assembly dependente.

Consulte também