Linee guida per la progettazione di controlli a cui è possibile applicare degli stili
Aggiornamento: novembre 2007
In questo documento viene riepilogata una serie di procedure ottimali da tenere in considerazione per la progettazione di un controllo a cui siano facilmente applicabili stili e modelli. Tali procedure sono il risultato di numerosi tentativi ed errori durante l'elaborazione degli stili dei controlli dei temi per il set di controlli WPF incorporato. Una corretta applicazione degli stili non dipende solo da un modello a oggetti ben progettato ma anche dallo stile stesso. Questo documento è destinato in primo luogo agli autori dei controlli, non agli autori degli stili.
Nel presente argomento sono contenute le seguenti sezioni.
- Terminologia
- Prima di iniziare: conoscenza del controllo
- Indicazioni generali
- Considerazioni sui temi
- Argomenti correlati
Terminologia
Per "applicazione di stili e modelli" si intende l'insieme di tecnologie che consente a un autore di controlli di rinviare gli aspetti visivi del controllo allo stile e al modello del controllo. In questo insieme di tecnologie sono inclusi:
Stili (compresi i metodi per l'impostazione delle proprietà, i trigger e gli storyboard).
Risorse.
Modelli di controllo.
Modelli di dati.
Per un'introduzione all'applicazione di stili e modelli, vedere Applicazione di stili e modelli.
Prima di iniziare: conoscenza del controllo
Prima di passare alla lettura di queste linee guida, è importante aver compreso e definito l'utilizzo comune del controllo. L'applicazione degli stili offre spesso una serie di possibilità che non rispondono a regole costanti. Il problema dei controlli creati per un ampio utilizzo (in un gran numero di applicazioni, da parte di molti sviluppatori) è relativo al fatto che l'applicazione degli stili può essere utilizzata per apportare modifiche di vasta portata all'aspetto visivo del controllo, per cui, il controllo a cui è stato applicato uno stile potrebbe non corrispondere al progetto dell'autore. Poiché la flessibilità offerta dall'applicazione degli stili è essenzialmente illimitata, un'idea dell'utilizzo comune consentirà di definire un ambito per le proprie decisioni.
Per comprendere l'utilizzo comune del controllo, è utile riflettere sulla proposta di valore del controllo, considerando le possibilità offerte da un determinato controllo rispetto ad altri. L'utilizzo comune non è legato a un particolare aspetto visivo, ma piuttosto alla filosofia del controllo e a una serie di ragionevoli aspettative relative all'utilizzo del controllo stesso. Questa considerazione consente di formulare alcune ipotesi relative al modello di composizione e ai comportamenti standard del controllo definiti dallo stile. Nel caso di ComboBox, ad esempio, la comprensione dell'utilizzo comune non sarà di nessun aiuto nel sapere se un determinato oggetto ComboBox avrà gli angoli arrotondati, ma consentirà di prevedere che quell'oggetto ComboBox richiede probabilmente una finestra popup e una modalità di attivazione e disattivazione nel caso in cui venga aperta.
Indicazioni generali
Non applicare in maniera rigida i contratti dei modelli. Il contratto del modello di un controllo può includere elementi, comandi, associazioni, trigger o anche impostazioni di proprietà richieste o previste per il corretto funzionamento del controllo.
Ridurre al minimo i contratti per quanto possibile.
Lavorare alla progettazione con la consapevolezza che in questa fase (ovvero quando si utilizza uno strumento di progettazione) è normale che il modello del controllo si trovi in uno stato incompleto. WPF non offre un'infrastruttura di stato della composizione; pertanto la compilazione dei controlli dovrà essere eseguita nella prospettiva che tale stato possa essere valido.
Non generare eccezioni quando un aspetto qualsiasi di un contratto del modello non viene rispettato. Secondo queste linee guida, i pannelli non devono generare eccezioni se contengono un numero troppo elevato o troppo esiguo di elementi figlio.
Scomporre le funzionalità secondarie in elementi di supporto del modello. Ogni controllo deve essere incentrato sulla funzionalità di base e sull'effettiva proposta di valore e definito in base all'utilizzo comune. A tal fine, utilizzare elementi di composizione e di supporto all'interno del modello per attivare comportamenti e visualizzazioni secondari, ovvero quei comportamenti e quelle visualizzazioni che non contribuiscono alla funzionalità di base del controllo. Gli elementi di supporto sono suddivisi in tre categorie:
I tipi di supporto autonomi sono controlli o primitive pubblici e riutilizzabili che vengono utilizzati "in modo anonimo" all'interno di un modello, vale a dire che l'elemento di supporto non è in grado di rilevare il controllo al quale è stato applicato lo stile e viceversa. Da un punto di vista tecnico, qualsiasi elemento può essere di tipo anonimo, ma in questo contesto il termine descrive i tipi che incapsulano funzionalità specializzate per attivare scenari di destinazione.
Gli elementi di supporto basati su tipi sono tipi nuovi che incapsulano funzionalità specializzate. Tali elementi vengono progettati in genere con una gamma di funzionalità più limitata rispetto ai controlli o alle primitive comuni. A differenza degli elementi di supporto autonomi, quelli basati su tipi sono in grado di rilevare il contesto in cui vengono utilizzati e in genere devono condividere i dati con il controllo del relativo modello di appartenenza.
Gli elementi di supporto con nome sono i controlli e le primitive comuni di cui è prevista l'identificazione in base al nome da parte di un controllo all'interno del relativo modello. A tali elementi viene assegnato un nome noto all'interno del modello, consentendo pertanto a un controllo di individuare l'elemento e di interagire con esso a livello di codice. In ciascun modello può esistere un solo elemento con un determinato nome.
Nella tabella seguente vengono riportati gli elementi di supporto attualmente utilizzati dagli stili del controllo (l'elenco non è completo):
Elemento
Tipo
Utilizzato da
Basato su tipi
Button, CheckBox, RadioButton, Frame e così via (tutti i tipi ContentControl)
Basato su tipi
ListBox, ComboBox, Menu e così via (tutti i tipi ItemsControl)
Con nome
Autonomo
Con nome
Con nome
Autonomo
Autonomo
Con nome
Basato su tipi
Ridurre al minimo le associazioni o le impostazioni di proprietà richieste specificate dall'utente per gli elementi di supporto. Di regola, un elemento di supporto richiede determinate associazioni o impostazioni di proprietà per un corretto funzionamento all'interno del modello di controllo. Tali impostazioni dovrebbero essere specificate, per quanto possibile, dall'elemento di supporto e dal controllo basato su modelli. Al momento dell'impostazione delle proprietà o della definizione delle associazioni, è necessario prestare attenzione a non eseguire l'override dei valori impostati dall'utente. Di seguito vengono indicate le procedure consigliate:
Gli elementi di supporto con nome devono essere identificati dall'elemento padre, che deve stabilire le impostazioni richieste nell'elemento di supporto.
Le impostazioni richieste per gli elementi di supporto basati su tipi devono essere stabilite direttamente negli elementi. Tale operazione talvolta richiede, da parte dell'elemento di supporto, l'esecuzione di una query sul contesto delle informazioni nel quale viene utilizzato, incluso l'oggetto TemplatedParent (il tipo di controllo del modello in cui viene utilizzato). ContentPresenter, ad esempio, associa automaticamente la proprietà Content del relativo oggetto TemplatedParent alla proprietà Content, se utilizzato in un tipo derivato ContentControl.
Non è possibile ottimizzare allo stesso modo gli elementi di supporto autonomi, perché, per definizione, l'elemento di supporto e l'elemento padre non sono in grado di rilevarsi a vicenda.
Utilizzare la proprietà Name per impostare i flag degli elementi all'interno di un modello. Per individuare un elemento dello stile e accedere a tale elemento a livello di codice, un controllo dovrà utilizzare la proprietà Name e il paradigma FindName. Se l'elemento non viene trovato, il controllo non dovrà generare un'eccezione ma piuttosto disattivare in modo regolare e senza visualizzare alcun messaggio la funzionalità in cui tale elemento era richiesto.
Utilizzare le procedure consigliate per esprimere lo stato e il comportamento del controllo all'interno di uno stile. Di seguito viene riportato un elenco ordinato delle procedure consigliate per indicare le modifiche e il comportamento dello stato del controllo all'interno di uno stile. È opportuno utilizzare il primo elemento dell'elenco per attivare lo scenario.
Associazione di proprietà. Esempio: associazione tra ComboBox.IsDropDownOpen e ToggleButton.IsChecked.
Modifiche delle proprietà attivate o animazioni delle proprietà. Esempio: stato di un oggetto Button al passaggio del mouse.
Comando. Esempio: LineUpCommand / LineDownCommand in ScrollBar.
Elementi di supporto autonomi. Esempio: TabPanel in TabControl.
Tipi di supporto basati su tipi. Esempio: ContentPresenter in Button, TickBar in Slider.
Elementi di supporto con nome. Esempio: TextBox in ComboBox.
Eventi propagati da tipi di supporto con nome. Se si è in ascolto di eventi propagati da un elemento dello stile, è opportuno richiedere che l'elemento che ha generato l'evento possa essere identificato in modo univoco. Esempio: Thumb in ToolBar.
Comportamento OnRender personalizzato. Esempio: ButtonChrome in Button.
Utilizzare i trigger degli stili (anziché i trigger dei modelli) in modo sporadico. I trigger che hanno effetto sulle proprietà degli elementi del modello devono essere dichiarati nel modello. I trigger che hanno effetto sulle proprietà del controllo (escluso TargetName) possono essere dichiarati nello stile, a meno che la modifica del modello non comporti l'eliminazione del trigger.
Essere coerenti con i modelli di applicazione dello stile esistenti. Spesso esistono diversi modi per risolvere un problema. È fondamentale conoscere e, se possibile, essere coerenti con i modelli di applicazione dello stile del controllo esistenti. Tale accorgimento è particolarmente importante per i controlli che derivano dallo stesso tipo di base (ad esempio, ContentControl, ItemsControl, RangeBase e così via).
Esporre le proprietà per attivare scenari di personalizzazione comuni senza applicare nuovamente i modelli. WPF non supporta parti personalizzabili o collegabili. Di conseguenza, l'utente di un controllo avrà a disposizione soltanto due metodi per la personalizzazione: l'impostazione diretta delle proprietà o l'impostazione delle proprietà tramite gli stili. Pertanto, sarà opportuno limitare il numero di proprietà destinate a scenari di personalizzazione molto comuni, ad alta priorità, che richiederebbero altrimenti una nuova applicazione dei modelli. Di seguito sono riportate le procedure consigliate per stabilire il momento e il metodo adatti per attivare gli scenari di personalizzazione:
Le personalizzazioni molto comuni devono essere esposte come proprietà nel controllo e utilizzate dal modello.
Le personalizzazioni meno comuni (benché non rare) devono essere esposte come proprietà associate e utilizzate dal modello.
È accettabile che le personalizzazioni note ma poco comuni richiedano una nuova applicazione di modelli.
Considerazioni sui temi
Gli stili dei temi dovrebbero presentare una semantica delle proprietà coerente in tutti i temi, tuttavia non offrono alcuna garanzia in proposito. Come parte della documentazione, il controllo dovrebbe includere un documento con la descrizione della semantica delle proprietà del controllo, vale a dire del "significato" di una proprietà per un controllo. Il controllo ComboBox, ad esempio, dovrebbe definire il significato della proprietà Background all'interno di ComboBox. Gli stili predefiniti per il controllo dovrebbero, per quanto possibile, attenersi alla semantica definita in tale documento per tutti i temi. Gli utenti del controllo, d'altra parte, dovrebbero essere consapevoli del fatto che la semantica delle proprietà può variare da tema a tema. In alcuni casi, potrebbe non essere possibile esprimere una determinata proprietà a causa dei vincoli visivi richiesti da un particolare tema. Ad esempio, per molti controlli nel tema Classico non è disponibile un singolo bordo a cui applicare Thickness.
Gli stili dei temi non devono necessariamente presentare una semantica dei trigger coerente in tutti i temi. Il comportamento esposto da uno stile del controllo tramite trigger o animazioni può variare da tema a tema. Gli utenti del controllo dovrebbero essere consapevoli del fatto che un controllo non impiega necessariamente lo stesso meccanismo in tutti i temi per ottenere un determinato comportamento. È possibile, ad esempio, che un tema utilizzi un'animazione per indicare un comportamento al passaggio del mouse, laddove un altro tema utilizza un trigger. Pertanto, nei controlli personalizzati è possibile che si verifichino delle incoerenze nella conservazione di un comportamento. La modifica della proprietà dello sfondo, ad esempio, potrebbe non avere effetto sullo stato del controllo al passaggio del mouse, se tale stato viene espresso tramite un trigger. Tuttavia, se lo stato al passaggio del mouse viene implementato mediante un'animazione, la modifica dello sfondo potrebbe interrompere l'animazione e di conseguenza la transizione di stato.
Gli stili dei temi non devono necessariamente presentare una semantica di "layout" coerente in tutti i temi. L'utilizzo dello stile predefinito, ad esempio, non deve garantire che un controllo conservi le stesse dimensioni in tutti i temi o che il relativo contenuto abbia gli stessi margini o la stessa spaziatura interna in tutti i temi.