Compartilhar via


XAML e classes personalizadas para WPF

O XAML, conforme implementado em estruturas CLR (Common Language Runtime), dá suporte à capacidade de definir uma classe ou estrutura personalizada em qualquer linguagem CLR (Common Language Runtime) e, em seguida, acessar essa classe usando marcação XAML. Você pode usar uma mistura de tipos definidos pelo WPF (Windows Presentation Foundation) e seus tipos personalizados no mesmo arquivo de marcação, normalmente mapeando os tipos personalizados para um prefixo de namespace XAML. Este tópico discute os requisitos que uma classe personalizada deve atender para ser utilizável como um elemento XAML.

Classes personalizadas em aplicativos ou assemblies

Classes personalizadas usadas em XAML podem ser definidas de duas maneiras distintas: dentro do código subjacente ou de outro código que produz o aplicativo WPF (Windows Presentation Foundation) primário, ou como uma classe em um assembly separado (ou conjunto de componentes), como um executável ou uma DLL utilizada como biblioteca de classes. Cada uma dessas abordagens tem vantagens e desvantagens particulares.

  • A vantagem de criar uma biblioteca de classes é que essas classes personalizadas podem ser compartilhadas em vários aplicativos possíveis diferentes. Uma biblioteca separada também facilita o controle de problemas de controle de versão de aplicativos e simplifica a criação de uma classe em que o uso de classe pretendido é como um elemento raiz em uma página XAML.

  • A vantagem de definir as classes personalizadas no aplicativo é que essa técnica é relativamente leve e minimiza os problemas de implantação e teste encontrados ao introduzir assemblies separados além do executável do aplicativo principal.

  • Seja definido no mesmo assembly ou em um assembly diferente, as classes personalizadas precisam ser mapeadas entre o namespace CLR e o namespace XML para serem usadas em XAML como elementos. Consulte namespaces XAML e mapeamento de namespace para WPF XAML.

Requisitos para uma classe personalizada como um elemento XAML

Para poder ser instanciado como um elemento de objeto, sua classe deve atender aos seguintes requisitos:

  • Sua classe personalizada deve ser pública e dar suporte a um construtor público padrão (sem parâmetros). (Consulte a seção a seguir para obter notas sobre estruturas.)

  • Sua classe personalizada não deve ser uma classe aninhada. As classes aninhadas e o "ponto" em sua sintaxe geral de uso no CLR interferem em outros recursos do WPF e/ou XAML, como propriedades anexadas.

Além de habilitar a sintaxe do elemento de objeto, sua definição de objeto também habilita a sintaxe do elemento de propriedade para quaisquer outras propriedades públicas que levem esse objeto como o tipo de valor. Isso ocorre porque o objeto agora pode ser instanciado como um elemento do tipo objeto e pode preencher o valor de um elemento de propriedade dessa propriedade.

Estruturas

Estruturas que você define como tipos personalizados sempre podem ser construídas em XAML no WPF. Isso ocorre porque os compiladores CLR criam implicitamente um construtor sem parâmetros para uma estrutura que inicializa todos os valores de propriedade para seus padrões. Em alguns casos, o comportamento de construção padrão e/ou o uso de elemento de objeto para uma estrutura não são desejáveis. Isso pode ocorrer porque a estrutura destina-se a preencher valores e funcionar conceitualmente como uma união, em que os valores contidos podem ter interpretações mutuamente exclusivas e, portanto, nenhuma de suas propriedades é configurável. Um exemplo do WPF de tal estrutura é GridLength. Em geral, essas estruturas devem implementar um conversor de tipo de modo 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.

Requisitos para propriedades de uma classe personalizada como atributos XAML

As propriedades devem referenciar um tipo por valor (como um primitivo) ou usar uma classe para o tipo que tenha um construtor sem parâmetros ou um conversor de tipo dedicado que um processador XAML possa acessar. Na implementação do CLR XAML, os processadores XAML encontram esses conversores por meio do suporte nativo aos primitivos de linguagem ou por meio da aplicação de TypeConverterAttribute a um tipo ou membro nas definições de tipo de suporte.

Como alternativa, a propriedade pode referenciar um tipo de classe abstrato ou uma interface. Para classes ou interfaces abstratas, a expectativa para a análise XAML é que o valor da propriedade deve ser preenchido com instâncias de classe concretas que implementam a interface ou instâncias de tipos que derivam da classe abstrata.

As propriedades podem ser declaradas em uma classe abstrata, mas só podem ser definidas em classes concretas que derivam da classe abstrata. Isso ocorre porque a criação do elemento de objeto para a classe exige um construtor público sem parâmetros na classe.

Sintaxe de atributo de habilitação para TypeConverter

Se você fornecer um conversor de tipo atribuído dedicado no nível da classe, a conversão de tipo aplicado habilitará a sintaxe de atributo para qualquer propriedade que precise instanciar esse tipo. Um conversor de tipo não habilita o uso do elemento de objeto do tipo; apenas a presença de um construtor sem parâmetros para esse tipo habilita o uso do elemento de objeto. Portanto, as propriedades equipadas com conversor de tipo geralmente não são utilizáveis na sintaxe de propriedade, a menos que o tipo em si também dê suporte à sintaxe do elemento de objeto. A exceção a isso é que você pode especificar uma sintaxe de elemento de propriedade, mas fazer com que o elemento de propriedade contenha uma cadeia de caracteres. Esse uso é essencialmente equivalente a um uso de sintaxe de atributo e esse uso não é comum, a menos que haja a necessidade de uma manipulação de espaço em branco mais robusta do valor do atributo. Por exemplo, o seguinte é um uso de elemento de propriedade que usa uma cadeia de caracteres e o equivalente de uso do atributo:

<Button>Hallo!
  <Button.Language>
    de-DE
  </Button.Language>
</Button>
<Button Language="de-DE">Hallo!</Button>

Exemplos de propriedades em que a sintaxe de atributo é permitida, mas a sintaxe do elemento de propriedade que contém um elemento de objeto não é permitida por meio de XAML incluem diversas propriedades de tipo Cursor. A Cursor classe tem um conversor CursorConverterde tipo dedicado, mas não expõe um construtor sem parâmetros, portanto, a Cursor propriedade só pode ser definida por meio da sintaxe de atributo, mesmo que o tipo real Cursor seja um tipo de referência.

Conversores de tipo Per-Property

Como alternativa, a propriedade em si pode declarar um conversor de tipo no nível da propriedade. Isso permite uma "mini linguagem" que cria instâncias de objetos do tipo da propriedade diretamente, processando valores de strings de entrada do atributo como entrada para uma operação ConvertFrom com base no tipo apropriado. Normalmente, isso é feito para fornecer um acessador de conveniência e não como o único meio para habilitar a configuração de uma propriedade no XAML. No entanto, também é possível usar conversores de tipo para atributos em que você deseja usar tipos CLR existentes que não fornecem um construtor sem parâmetros ou um conversor de tipo atribuído. Exemplos da API do WPF são certas propriedades do tipo CultureInfo. Nesse caso, o WPF usou o tipo existente do Microsoft .NET Framework CultureInfo para lidar melhor com cenários de compatibilidade e migração que eram usados em versões anteriores de estruturas, mas o CultureInfo tipo não suportava os construtores necessários ou conversão de tipo no nível do tipo para ser utilizável como um valor de propriedade XAML diretamente.

Sempre que você expõe uma propriedade que tem um uso XAML, especialmente se você for um autor de controle, considere fortemente o backup dessa propriedade com uma propriedade de dependência. Isso é particularmente verdadeiro se você usar a implementação existente do Windows Presentation Foundation (WPF) do processador XAML, pois você pode melhorar o desempenho usando DependencyProperty apoio. Uma propriedade de dependência irá expor as funcionalidades do sistema de propriedades que os usuários esperam de uma propriedade acessível pelo XAML. Isso inclui recursos como animação, associação de dados e suporte a estilo. Para obter mais informações, consulte propriedades de dependência personalizadas e propriedades de carregamento e dependência XAML.

Gravando e atribuindo um conversor de tipo

Ocasionalmente, você precisará escrever uma classe derivada personalizada TypeConverter para fornecer conversão de tipo para o tipo de propriedade. Para obter instruções sobre como derivar e criar um conversor de tipo que possa dar suporte a usos XAML e como aplicar o TypeConverterAttribute, consulte TypeConverters e XAML.

Requisitos para sintaxe do atributo de manipulador de eventos XAML para eventos de uma classe personalizada

Para ser utilizável como um evento CLR, o evento deve ser exposto como um evento público em uma classe que dá suporte a um construtor sem parâmetros ou em uma classe abstrata em que o evento pode ser acessado em classes derivadas. Para ser usado convenientemente como um evento roteado, o evento CLR deve implementar métodos explícitos add e remove, que adicionam e removem manipuladores para a assinatura de evento CLR e encaminham esses manipuladores para os métodos AddHandler e RemoveHandler. Esses métodos adicionam ou removem os manipuladores ao repositório do manipulador de eventos roteado na instância à qual o evento está anexado.

Observação

É possível registrar manipuladores diretamente para eventos roteados usando AddHandlere não definir deliberadamente um evento CLR que expõe o evento roteado. Isso geralmente não é recomendado porque o evento não habilitará a sintaxe de atributo XAML para anexar manipuladores e sua classe resultante oferecerá uma exibição XAML menos transparente dos recursos desse tipo.

Gravando propriedades da coleção

As propriedades que levam um tipo de coleção têm uma sintaxe XAML que permite especificar objetos que são adicionados à coleção. Essa sintaxe tem dois recursos notáveis.

  • O objeto que é o objeto de coleção não precisa ser especificado na sintaxe do elemento de objeto. A presença desse tipo de coleção é implícita sempre que você especifica uma propriedade em XAML que usa um tipo de coleção.

  • Os elementos filho da propriedade de coleção no código são processados para se tornarem membros da coleção. Normalmente, o acesso de código aos membros de uma coleção é executado por meio de métodos de lista/dicionário, como Add, ou por meio de um indexador. Mas a sintaxe XAML não dá suporte a métodos ou indexadores (exceção: XAML 2009 pode dar suporte a métodos, mas o uso do XAML 2009 restringe os possíveis usos do WPF; consulte recursos de linguagem XAML 2009). As coleções são, obviamente, um requisito muito comum para a criação de uma árvore de elementos e você precisa de alguma maneira para preencher essas coleções em XAML declarativo. Portanto, os elementos filho de uma propriedade de coleção são processados adicionando-os à coleção que é o valor do tipo de propriedade da coleção.

A implementação dos Serviços XAML do .NET Framework e, portanto, o processador XAML do WPF usa a definição a seguir para o que constitui uma propriedade de coleção. O tipo de propriedade da propriedade deve implementar um dos seguintes:

Cada um desses tipos no CLR tem um Add método, que é usado pelo processador XAML para adicionar itens à coleção subjacente ao criar o grafo de objeto.

Observação

Não há suporte para as interfaces genéricas List e Dictionary (IList<T> e IDictionary<TKey,TValue>) para detecção de coleção pelo processador XAML do WPF. No entanto, você pode usar a List<T> classe como uma classe base, pois ela implementa IList diretamente ou Dictionary<TKey,TValue> como uma classe base, porque ela implementa IDictionary diretamente.

Ao declarar uma propriedade que usa uma coleção, tenha cuidado com a forma como o valor dessa propriedade é inicializado em novas instâncias do tipo. Se você não estiver implementando a propriedade como uma propriedade de dependência, fazer com que a propriedade use um campo de backup que chama o construtor de tipo de coleção é adequado. Se sua propriedade for uma propriedade de dependência, talvez seja necessário inicializar a propriedade de coleção como parte do construtor de tipo padrão. Isso ocorre porque uma propriedade de dependência usa seu valor padrão de metadados e você normalmente não deseja que o valor inicial de uma propriedade de coleção seja uma coleção estática e compartilhada. Deve haver uma instância de coleção por cada instância de tipo que contenha. Para obter mais informações, consulte Propriedades de Dependência Personalizadas.

Você pode criar um tipo de coleção personalizado para a sua propriedade de coleção. Devido ao tratamento de propriedade de coleção implícita, o tipo de coleção personalizada não precisa fornecer um construtor sem parâmetros para ser usado implicitamente no XAML. No entanto, opcionalmente, você pode fornecer um construtor sem parâmetros para o tipo de coleção. Essa pode ser uma prática que vale a pena. A menos que você forneça um construtor sem parâmetros, você não pode declarar explicitamente a coleção como um elemento de objeto. Alguns autores de marcação podem preferir ver a coleção explícita como uma questão de estilo de marcação. Além disso, um construtor sem parâmetros pode simplificar os requisitos de inicialização ao criar novos objetos que usam seu tipo de coleção como um valor de propriedade.

Declarando propriedades de conteúdo XAML

A linguagem XAML define o conceito de uma propriedade de conteúdo XAML. Cada classe utilizável na sintaxe do objeto pode ter exatamente uma propriedade de conteúdo XAML. Para declarar uma propriedade como sendo a propriedade de conteúdo XAML para sua classe, aplique-a ContentPropertyAttribute como parte da definição de classe. Especifique o nome da propriedade de conteúdo XAML pretendida no atributo Name. A propriedade é especificada como uma cadeia de caracteres por nome, não como um constructo de reflexão, como PropertyInfo.

Você pode especificar uma propriedade de coleção para ser a propriedade de conteúdo XAML. Isso resulta em um uso para essa propriedade em que o elemento de objeto pode ter um ou mais elementos filho, sem quaisquer elementos de objeto de coleção ou marcas de elementos de propriedade. Esses elementos são tratados como o valor da propriedade de conteúdo XAML e adicionados à instância da coleção de backup.

Algumas propriedades de conteúdo XAML existentes usam o tipo de propriedade .Object Isso permite uma propriedade de conteúdo XAML que pode levar valores primitivos, como um String , bem como obter um único valor de objeto de referência. Se você seguir esse modelo, seu tipo será responsável pela determinação do tipo, bem como pela manipulação de tipos possíveis. O motivo típico para um Object tipo de conteúdo é dar suporte a um meio simples de adicionar conteúdo de objeto como uma cadeia de caracteres (que recebe um tratamento de apresentação padrão) ou a um meio avançado de adicionar conteúdo de objeto que especifica uma apresentação não padrão ou dados adicionais.

Serializando XAML

Para determinados cenários, como se você for um autor de controle, também convém garantir que qualquer representação de objeto que possa ser instanciada no XAML também possa ser serializada de volta à marcação XAML equivalente. Os requisitos de serialização não são descritos neste tópico. Consulte Visão geral de criação de controle e árvore de elementos e serialização.

Consulte também