Condividi tramite


Classi XAML e personalizzate per WPF

XAML implementato in framework CLR (Common Language Runtime) supporta la possibilità di definire una classe o una struttura personalizzata in qualsiasi linguaggio CLR (Common Language Runtime) e quindi accedere a tale classe usando markup XAML. Puoi usare una combinazione di tipi definiti da Windows Presentation Foundation (WPF) e i tipi personalizzati all'interno dello stesso file di markup, in genere eseguendo il mapping dei tipi personalizzati a un prefisso dello spazio dei nomi XAML. Questo argomento illustra i requisiti che una classe personalizzata deve soddisfare per essere utilizzabile come elemento XAML.

Classi personalizzate in applicazioni o assembly

Le classi personalizzate usate in XAML possono essere definite in due modi distinti: all'interno del code-behind o di un altro codice che produce l'applicazione Windows Presentation Foundation (WPF) primaria o come classe in un assembly separato, ad esempio un eseguibile o una DLL usata come libreria di classi. Ognuno di questi approcci presenta particolari vantaggi e svantaggi.

  • Il vantaggio della creazione di una libreria di classi è che qualsiasi classe personalizzata può essere condivisa in molte applicazioni diverse. Una libreria separata semplifica anche il controllo delle versioni delle applicazioni e semplifica la creazione di una classe in cui l'utilizzo previsto della classe è come elemento radice in una pagina XAML.

  • Il vantaggio di definire le classi personalizzate nell'applicazione è che questa tecnica è relativamente leggera e riduce al minimo i problemi di distribuzione e test riscontrati quando si introducono assembly separati oltre il file eseguibile principale dell'applicazione.

  • Indipendentemente dal fatto che sia definito nello stesso assembly o in un assembly diverso, è necessario eseguire il mapping delle classi personalizzate tra lo spazio dei nomi CLR e lo spazio dei nomi XML per poter essere usato in XAML come elementi. Vedere spazi dei nomi XAML e mapping dello spazio dei nomi per XAML WPF.

Requisiti per una classe personalizzata come elemento XAML

Per essere istanziata come elemento oggetto, la classe deve soddisfare i seguenti requisiti:

  • La classe personalizzata deve essere pubblica e supportare un costruttore pubblico predefinito (senza parametri). Per le note relative alle strutture, vedere la sezione seguente.

  • La classe personalizzata non deve essere una classe annidata. Le classi annidate e il "punto" nella sintassi generale di utilizzo CLR interferiscono con altre funzionalità WPF e/o XAML, come le proprietà allegate.

Oltre ad abilitare la sintassi degli elementi oggetto, la definizione dell'oggetto abilita anche la sintassi degli elementi proprietà per qualsiasi altra proprietà pubblica che accetta tale oggetto come tipo valore. Ciò è dovuto al fatto che l'oggetto può ora essere creato come elemento oggetto e può riempire il valore dell'elemento della proprietà di tale proprietà.

Strutture

Le strutture definite come tipi personalizzati sono sempre in grado di essere costruite in XAML in WPF. Il motivo è che i compilatori CLR creano in modo implicito un costruttore senza parametri per una struttura che inizializza tutti i valori delle proprietà nei valori predefiniti. In alcuni casi, il comportamento di costruzione predefinito e/o l'utilizzo degli elementi oggetto per una struttura non è auspicabile. Ciò potrebbe essere dovuto al fatto che la struttura è destinata a riempire i valori e a funzionare concettualmente come unione, in cui i valori contenuti potrebbero avere interpretazioni che si escludono a vicenda e pertanto nessuna delle relative proprietà è impostabile. Un esempio WPF di tale struttura è GridLength. In genere, tali strutture devono implementare un convertitore di tipi in modo che i valori possano essere espressi in forma di attributo, usando convenzioni di stringa che creano le diverse interpretazioni o modalità dei valori della struttura. La struttura deve esporre anche un comportamento simile per la costruzione del codice tramite un costruttore senza parametri.

Requisiti per le proprietà di una classe personalizzata come attributi XAML

Le proprietà devono fare riferimento a un tipo per valore (come un tipo primitivo) oppure utilizzare una classe per il tipo che abbia un costruttore senza parametri o un convertitore di tipo specifico accessibile da un processore XAML. Nell'implementazione XAML CLR, i processori XAML trovano tali convertitori tramite il supporto nativo per le primitive del linguaggio o tramite l'applicazione di TypeConverterAttribute a un tipo o a un membro nelle definizioni dei tipi di base.

In alternativa, la proprietà può fare riferimento a un tipo di classe astratta o a un'interfaccia. Per le classi o le interfacce astratte, l'aspettativa per l'analisi XAML è che il valore della proprietà deve essere riempito con istanze di classe concrete che implementano l'interfaccia o istanze di tipi che derivano dalla classe astratta.

Le proprietà possono essere dichiarate in una classe astratta, ma possono essere impostate solo su classi concrete che derivano dalla classe astratta. Ciò è dovuto al fatto che la creazione dell'elemento oggetto per la classe richiede un costruttore pubblico senza parametri nella classe .

Sintassi dell'attributo TypeConverter Abilitato

Se si fornisce un convertitore di tipi specifico riconosciuto tramite attributi a livello di classe, la conversione dei tipi applicata consente la sintassi degli attributi per qualsiasi proprietà che debba istanziare tale tipo. Un convertitore di tipi non abilita l'utilizzo degli elementi oggetto del tipo; solo la presenza di un costruttore senza parametri per tale tipo abilita l'utilizzo degli elementi oggetto. Pertanto, le proprietà abilitate per il convertitore di tipi non sono in genere utilizzabili nella sintassi delle proprietà, a meno che il tipo stesso non supporti anche la sintassi degli elementi oggetto. L'eccezione è che è possibile specificare una sintassi dell'elemento proprietà, ma l'elemento proprietà deve contenere una stringa. Questo utilizzo è essenzialmente equivalente a un utilizzo della sintassi degli attributi e tale utilizzo non è comune a meno che non vi sia bisogno di una gestione più affidabile dello spazio vuoto del valore dell'attributo. Ad esempio, di seguito è riportato l'utilizzo di un elemento di proprietà che accetta una stringa e l'equivalente di un uso dell'attributo:

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

Esempi di proprietà in cui è consentita la sintassi degli attributi, ma la sintassi degli elementi proprietà che contiene un elemento oggetto non è consentita tramite XAML sono varie proprietà che accettano il tipo Cursor. La classe Cursor dispone di un convertitore di tipi dedicato CursorConverter, ma non espone un costruttore senza parametri, pertanto la proprietà Cursor può essere impostata solo tramite la sintassi degli attributi anche se il tipo Cursor effettivo è un tipo riferimento.

convertitori di tipi Per-Property

In alternativa, la proprietà stessa può dichiarare un convertitore di tipi a livello di proprietà. In questo modo viene attivata una "mini lingua" che crea un'istanza di oggetti del tipo della proprietà inline, elaborando i valori stringa in ingresso dell'attributo come input per un'operazione di ConvertFrom in base al tipo appropriato. In genere questa operazione viene eseguita per fornire una funzione di accesso pratico e non come unico mezzo per abilitare l'impostazione di una proprietà in XAML. Tuttavia, è anche possibile usare convertitori di tipi per gli attributi in cui si desidera utilizzare i tipi CLR esistenti che non forniscono un costruttore senza parametri o un convertitore di tipi con attributi. Esempi dell'API WPF sono certe proprietà che utilizzano il tipo CultureInfo. In questo caso, WPF usava il tipo Microsoft .NET Framework CultureInfo esistente per risolvere meglio gli scenari di compatibilità e migrazione usati nelle versioni precedenti dei framework, ma il tipo CultureInfo non supportava i costruttori o la conversione dei tipi a livello di tipo necessari per poter essere utilizzato direttamente come valore di proprietà XAML.

Ogni volta che si espone una proprietà che ha un utilizzo XAML, in particolare se si è uno sviluppatore di controlli, è altamente consigliabile supportare quella proprietà con una proprietà di dipendenza. Ciò è particolarmente vero se utilizzi l'implementazione esistente di Windows Presentation Foundation (WPF) del processore XAML, perché puoi migliorare le prestazioni usando il supporto DependencyProperty. Una proprietà di dipendenza esporrà le funzionalità del sistema di proprietà di cui gli utenti si aspettano la presenza per una proprietà accessibile da XAML. Sono incluse funzionalità come animazione, data binding e supporto dello stile. Per ulteriori informazioni, consultare Proprietà di Dipendenza Personalizzate e Caricamento XAML e Proprietà di Dipendenza.

Scrittura e attributi di un convertitore di tipi

In alcuni casi sarà necessario scrivere una classe derivata TypeConverter personalizzata per fornire la conversione dei tipi per il tipo di proprietà. Per istruzioni su come derivare da e creare un convertitore di tipi in grado di supportare gli utilizzi XAML e su come applicare il TypeConverterAttribute, vedi TypeConverters e XAML.

Requisiti per la sintassi dell'attributo del gestore eventi XAML per gli eventi di una classe personalizzata

Per essere utilizzabile come evento CLR, l'evento deve essere esposto come evento pubblico in una classe che supporta un costruttore senza parametri o in una classe astratta a cui è possibile accedere all'evento sulle classi derivate. Per poter essere utilizzato comodamente come evento indirizzato, l'evento CLR dovrebbe implementare metodi espliciti add e remove, che aggiungono e rimuovono gestori per la firma dell'evento CLR e inoltrano tali gestori ai metodi AddHandler e RemoveHandler. Questi metodi aggiungono o rimuovono i gestori dall'archivio dei gestori di eventi indirizzati nell'istanza a cui è associato l'evento.

Nota

È possibile registrare i gestori direttamente per gli eventi indirizzati usando AddHandlere per non definire deliberatamente un evento CLR che espone l'evento indirizzato. Questo non è generalmente consigliato perché l'evento non abiliterà la sintassi degli attributi XAML per l'associazione dei gestori, e la classe risultante fornirà una visione XAML meno chiara delle funzionalità di quel tipo.

Scrittura delle proprietà dell'insieme

Le proprietà che accettano un tipo di raccolta hanno una sintassi XAML che consente di specificare gli oggetti aggiunti alla raccolta. Questa sintassi presenta due funzionalità rilevanti.

  • L'oggetto della raccolta non deve essere specificato nella sintassi dell'elemento oggetto. La presenza di tale tipo di raccolta è implicita ogni volta che specifichi una proprietà in XAML che accetta un tipo di raccolta.

  • Gli elementi figlio dell'attributo della collezione nel markup sono modificati per diventare membri della collezione. In genere, l'accesso del codice ai membri di una raccolta viene eseguito tramite metodi di elenco/dizionario, ad esempio Addo tramite un indicizzatore. Ma la sintassi XAML non supporta metodi o indicizzatori (eccezione: XAML 2009 può supportare metodi, ma l'uso di XAML 2009 limita i possibili utilizzi wpf; vedi XAML 2009 Language Features). Le raccolte sono ovviamente un requisito molto comune per la creazione di un albero di elementi ed è necessario un modo per popolare queste raccolte in XAML dichiarativo. Pertanto, gli elementi figlio di una proprietà di raccolta vengono elaborati aggiungendoli alla collezione che rappresenta il valore del tipo di proprietà di raccolta.

L'implementazione dei servizi XAML di .NET Framework e quindi il processore XAML WPF usa la definizione seguente per ciò che costituisce una proprietà di raccolta. Il tipo di proprietà della proprietà deve implementare uno dei seguenti elementi:

Ognuno di questi tipi in CLR ha un metodo Add, che viene usato dal processore XAML per aggiungere elementi alla raccolta sottostante durante la creazione dell'oggetto grafico.

Nota

Le interfacce List e Dictionary generiche (IList<T> e IDictionary<TKey,TValue>) non sono supportate per il rilevamento delle raccolte dal processore XAML WPF. Tuttavia, è possibile usare la classe List<T> come classe base, perché implementa direttamente IList o Dictionary<TKey,TValue> come classe base, perché implementa direttamente IDictionary.

Quando si dichiara una proprietà che accetta una raccolta, prestare attenzione al modo in cui il valore della proprietà viene inizializzato nelle nuove istanze del tipo. Se non si implementa la proprietà come proprietà di dipendenza, avere la proprietà che utilizza un campo di supporto, il quale richiama il costruttore del tipo di raccolta, è sufficiente. Se la tua proprietà è una proprietà di dipendenza, potrebbe essere necessario inizializzare la proprietà di una raccolta come parte del costruttore del tipo predefinito. Ciò avviene perché una proprietà di dipendenza accetta il valore predefinito dai metadati e in genere non si vuole che il valore iniziale di una proprietà della raccolta sia una raccolta statica e condivisa. Deve essere presente un'istanza di raccolta per ogni istanza di tipo contenitore. Per altre informazioni, vedere Proprietà di Dipendenza Personalizzate.

È possibile implementare un tipo di raccolta personalizzato per la proprietà della tua raccolta. A causa del trattamento implicito delle proprietà di raccolta, il tipo di raccolta personalizzata non deve fornire un costruttore senza parametri per poter essere usato in modo implicito in XAML. È tuttavia possibile specificare facoltativamente un costruttore senza parametri per il tipo di raccolta. Questo può essere una pratica utile. A meno che non si fornisca un costruttore senza parametri, non è possibile dichiarare in modo esplicito la raccolta come elemento oggetto. Alcuni autori di markup potrebbero preferire la visualizzazione della raccolta esplicita come una questione di stile di markup. Inoltre, un costruttore senza parametri può semplificare i requisiti di inizializzazione quando si creano nuovi oggetti che usano il tipo di raccolta come valore di una proprietà.

Dichiarazione delle proprietà del contenuto XAML

Il linguaggio XAML definisce il concetto di proprietà del contenuto XAML. Ogni classe utilizzabile nella sintassi degli oggetti può avere esattamente una proprietà del contenuto XAML. Per dichiarare una proprietà come proprietà del contenuto XAML per la classe, applicare il ContentPropertyAttribute come parte della definizione della classe. Specificare il nome della proprietà del contenuto XAML desiderata come Name nell'attributo . La proprietà viene specificata come stringa con il nome, non come costrutto di reflection, ad esempio PropertyInfo.

Puoi specificare una proprietà di una raccolta come proprietà del contenuto XAML. Ciò comporta un utilizzo per tale proprietà in cui l'elemento oggetto può avere uno o più elementi figlio, senza alcun elemento dell'insieme o tag di elemento proprietà. Questi elementi vengono quindi considerati come valore per la proprietà del contenuto XAML e aggiunti all'istanza della raccolta di backup.

Alcune proprietà di contenuto XAML esistenti usano il tipo di proprietà Object. In questo modo viene abilitata una proprietà del contenuto XAML che può accettare valori primitivi, ad esempio un String e accettare un singolo valore dell'oggetto di riferimento. Se si segue questo modello, il tipo è responsabile sia della determinazione del tipo che della gestione dei possibili tipi. Il motivo tipico di un tipo di contenuto Object consiste nel supportare sia un semplice mezzo per aggiungere contenuto oggetto come stringa (che riceve un trattamento di presentazione predefinito) sia un mezzo avanzato per aggiungere contenuto oggetto che specifica una presentazione non predefinita o dati aggiuntivi.

Serializzazione di XAML

Per determinati scenari, ad esempio se sei un autore di controlli, puoi anche assicurarti che qualsiasi rappresentazione di oggetto che può essere istanziata in XAML possa anche essere serializzata nel markup XAML equivalente. I requisiti di serializzazione non sono descritti in questo argomento. Vedere Cenni preliminari sulla creazione di controlli e albero degli elementi e serializzazione.

Vedere anche