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 descrive i requisiti che una classe personalizzata deve soddisfare perché sia 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 vantaggi e svantaggi specifici.

  • Il vantaggio della creazione di una libreria di classi consiste nella possibilità di condividere tutte queste classi personalizzate tra un gran numero di applicazioni diverse. Una libreria separata, poi, rende più semplice il controllo delle versioni delle applicazioni in caso di problemi e facilita la creazione di classi destinate a fungere da elementi radice di una pagina XAML.

  • Il vantaggio di poter definire classi personalizzate all'interno dell'applicazione consiste nel fatto che si tratta di una tecnica relativamente leggera in grado di ridurre al minimo i problemi di distribuzione e test che si riscontrano quando, oltre al file eseguibile dell'applicazione principale, si introducono assembly separati.

  • Che le classi personalizzate vengano definite all'interno dello stesso assembly o in un assembly diverso, perché siano utilizzabili come elementi in XAML è necessario effettuarne il mapping tra lo spazio dei nomi CLR e quello XML. Vedere Spazi dei nomi XAML e mapping dello spazio dei nomi per XAML WPF.

Requisiti per una classe personalizzata come elemento XAML

Perché sia possibile crearne un'istanza come elemento oggetto, la classe deve soddisfare i requisiti seguenti:

  • Deve essere pubblica e supportare un costruttore pubblico senza parametri predefinito. Per alcune note riguardanti le strutture, vedere la sezione seguente.

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

Oltre ad abilitare la sintassi degli elementi oggetto, la definizione dell'oggetto abilita la sintassi degli elementi proprietà per tutte le altre proprietà pubbliche che accettano tale oggetto come tipo valore. Ciò è dovuto al fatto che è ora possibile creare un'istanza dell'oggetto come elemento oggetto e inserirla come valore dell'elemento 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 predefinito relativo alla costruzione e/o all'utilizzo degli elementi oggetto per una struttura non è consigliabile. Ciò può essere dovuto al fatto che la struttura ha lo scopo di inserire valori e, dal punto di vista concettuale, di funzionare come un'unione, mentre i valori contenuti potrebbero essere interpretabili come reciprocamente esclusivi. Pertanto, non è possibile impostare alcuna delle proprietà della struttura. Un esempio WPF di tale struttura è GridLength. In genere, tali strutture devono implementare un convertitore di tipi che consenta di esprimere i valori sotto forma di attributo tramite convenzioni di stringa che creano interpretazioni o modalità diverse 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 classi personalizzate come attributi XAML

Le proprietà devono fare riferimento a un tipo per valore (ad esempio una primitiva) o usare una classe per il tipo con un costruttore senza parametri o un convertitore di tipi dedicato a cui un processore XAML può accedere. Nell'implementazione XAML CLR, i processori XAML trovano tali convertitori tramite il supporto nativo per le primitive del linguaggio o tramite l'applicazione di a un tipo o membro nelle definizioni dei TypeConverterAttribute tipi di supporto

In alternativa, la proprietà può fare riferimento a un tipo di una classe astratta oppure 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 degli attributi abilitata per TypeConverter

Se si fornisce un convertitore di tipi dedicato con attributi a livello di classe, la conversione di un tipo abilita la sintassi degli attributi per tutte le proprietà per le quali è necessario creare un'istanza del tipo in questione. 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. In genere, pertanto, le proprietà abilitate dal convertitore di tipi possono essere usate nella sintassi delle proprietà solo se il tipo stesso supporta anche la sintassi degli elementi oggetto. Eccezione: è possibile specificare la sintassi di elementi 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. L'utilizzo degli elementi proprietà riportato di seguito, ad esempio, accetta una stringa e l'equivalente dell'utilizzo 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 Cursor tipo. La Cursor classe dispone di un convertitore CursorConverterdi tipi dedicato, ma non espone un costruttore senza parametri, pertanto la proprietà può essere impostata solo tramite la Cursor sintassi degli attributi anche se il tipo effettivo Cursor è un tipo riferimento.

Convertitori di tipi per proprietà

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 ConvertFrom in base al tipo appropriato. In genere questa operazione viene eseguita per fornire una funzione di accesso pratica e non come unico mezzo per consentire 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. Alcuni esempi dell'API WPF sono determinate proprietà che accettano il CultureInfo tipo. 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 CultureInfo tipo non supportava i costruttori o la conversione del tipo a livello di tipo necessari per essere utilizzabile direttamente come valore della proprietà XAML.

Ogni volta che si espone una proprietà con utilizzo in XAML, in particolare se l'utente è un autore di controlli, è consigliabile supportare tale proprietà con una proprietà di dipendenza. Ciò è particolarmente vero se si usa l'implementazione di Windows Presentation Foundation (WPF) esistente del processore XAML, perché è possibile migliorare le prestazioni usando DependencyProperty il backup. Una proprietà di dipendenza espone le funzionalità del sistema di proprietà relative alla proprietà in questione che gli utenti si aspettano da una proprietà accessibile tramite XAML, ad esempio funzionalità quali l'animazione, il data binding e il supporto degli stili. Per altre informazioni, vedere Proprietà di dipendenza personalizzate e Caricamento XAML e proprietà di dipendenza.

Scrittura e assegnazione di un convertitore di tipi

In alcuni casi sarà necessario scrivere una classe derivata personalizzata TypeConverter 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 TypeConverterAttribute, vedi TypeConverters e XAML.

Requisiti per la sintassi degli attributi 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 essere usato praticamente come evento indirizzato, l'evento CLR deve implementare metodi e esplicitiadd, che aggiungono e rimuovono i gestori per la firma dell'evento CLR e inoltrano tali gestori ai AddHandler metodi e RemoveHandler .remove Questi metodi aggiungono o rimuovono i gestori dall'archivio dei gestori degli eventi indirizzati per l'istanza a cui l'evento è associato.

Nota

È possibile registrare i gestori direttamente per gli eventi indirizzati usando AddHandlere per non definire deliberatamente un evento CLR che espone l'evento indirizzato. Questa operazione non è in genere consigliata. In questo caso, infatti, per l'evento non è abilitata la sintassi degli attributi XAML per il collegamento di gestori e nella classe risultante il codice XAML relativo alle caratteristiche del tipo è meno chiaro.

Scrittura delle proprietà delle raccolte

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

  • Nella sintassi dell'elemento oggetto non è necessario specificare l'oggetto che rappresenta l'oggetto Collection. Ogni volta che in XAML si specifica una proprietà che accetta un tipo di raccolta, tale tipo di raccolta è implicito.

  • Gli elementi figlio della proprietà della raccolta nel markup vengono elaborati in modo che diventino membri della raccolta. In genere l'accesso del codice ai membri di una raccolta avviene tramite metodi di elenco o dizionario, ad esempio Add, oppure tramite un indicizzatore. La sintassi XAML, tuttavia, non supporta metodi o indicizzatori, ad eccezione di XAML 2009, che supporta i metodi. L'uso di XAML 2009, però, limita i possibili utilizzi di WPF. Vedere Funzionalità del linguaggio XAML 2009. Le raccolte sono ovviamente un requisito molto comune per la creazione di un albero di elementi. È quindi necessario individuare il modo più adatto per popolarle in XAML dichiarativo. Pertanto, gli elementi figlio di una proprietà della raccolta vengono elaborati aggiungendoli alla raccolta che rappresenta il valore del tipo della proprietà.

La definizione di proprietà di raccolta per l'implementazione dei servizi XAML di .NET Framework e quindi anche per il processore XAML di WPF è la seguente. Il tipo della proprietà deve soddisfare una delle condizioni seguenti:

In CLR ognuno di questi tipi dispone di un metodo Add, usato dal processore XAML per aggiungere elementi alla raccolta sottostante durante la creazione dell'oggetto grafico.

Nota

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

Quando si dichiara una proprietà che accetta una raccolta, prestare attenzione alla modalità di inizializzazione del valore di questa proprietà nelle nuove istanze del tipo. Se non si implementa la proprietà come proprietà di dipendenza, è adeguato che la proprietà usi un campo sottostante che chiami il costruttore del tipo della raccolta. Se la proprietà è una proprietà di dipendenza, potrebbe essere necessario inizializzare la proprietà della raccolta come parte del costruttore del tipo predefinito. Ciò è dovuto al fatto che il valore predefinito di una proprietà di dipendenza proviene dai metadati e in genere è necessario evitare che il valore iniziale di una proprietà di una raccolta corrisponda a una raccolta condivisa statica. Deve esistere un'istanza della raccolta per ogni istanza del tipo che la contiene. Per altre informazioni, vedere Proprietà Dependency personalizzate.

Per la proprietà della raccolta è possibile implementare un tipo di raccolta personalizzato. 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 modo di procedere può risultare 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 interpretano la dichiarazione esplicita di una raccolta 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 della proprietà.

Dichiarazione di proprietà di contenuto XAML

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

È possibile specificare una proprietà di raccolta come proprietà del contenuto XAML. Il risultato è un utilizzo di tale proprietà in cui l'elemento oggetto può avere uno o più elementi figlio, senza alcun tag di elementi oggetto Collection o di elementi proprietà. Questi elementi vengono considerati come valore per la proprietà del contenuto XAML e vengono aggiunti all'istanza della raccolta sottostante.

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

Serializzazione di XAML

Per alcuni scenari, ad esempio per gli autori di controlli, è anche necessario assicurarsi che qualsiasi rappresentazione di oggetti per cui può essere creata un'istanza in XAML possa anche essere serializzata di nuovo nel markup XAML equivalente. I requisiti di serializzazione non sono descritti in questo argomento. Vedere Cenni preliminari sulla modifica di controlli e Struttura ad albero e serializzazione degli elementi.

Vedi anche