Linee guida per la progettazione di controlli a cui è possibile applicare degli stili

Questo documento offre un riepilogo di una serie di procedure consigliate per la progettazione di un controllo a cui siano facilmente applicabili stili e modelli. In questo set di procedure consigliate è stato eseguito un sacco di prove ed errori durante l'uso degli stili di controllo del tema per il set di controlli WPF predefinito. La corretta applicazione degli stili è una funzione che non dipende solo dalla corretta progettazione del modello a oggetti ma anche dallo stile stesso. I destinatari di questo documento sono gli autori dei controlli, non gli autori degli stili.

Terminologia

Per applicazione di stili e modelli si intende un insieme di tecnologie che consente a un autore di controlli di rinviare gli aspetti visivi del controllo allo stile e al modello del controllo. Questo insieme di tecnologie include:

  • Stili (inclusi i setter 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: informazioni sul controllo

Prima di leggere queste linee guida, è importante aver compreso e definito l'uso comune del controllo. L'applicazione degli stili offre spesso una serie di possibilità che non seguono regole precise. I controlli creati per un ampio utilizzo (in un gran numero di applicazioni, da molti sviluppatori) presentano il problema che l'applicazione degli stili può essere usata per apportare modifiche di vasta portata all'aspetto visivo del controllo, al punto che 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, è possibile usare il concetto di uso comune per circoscrivere l’ambito delle proprie decisioni.

Per comprendere l'uso comune del controllo, è consigliabile valutare la proposta di valore del controllo. Quali sono i vantaggi che porta il controllo alla tabella e che nessun altro controllo può offrire? L'uso comune non è legato a uno specifico aspetto visivo, ma piuttosto alla filosofia del controllo e a una serie di ragionevoli aspettative per l'uso del controllo stesso. Questa considerazione consente di formulare delle ipotesi sul modello di composizione e sui comportamenti standard del controllo definiti dallo stile. Nel caso di ComboBox, ad esempio, la comprensione dell'utilizzo comune non fornirà informazioni dettagliate sul fatto che un particolare ComboBox ha angoli arrotondati, ma ti darà informazioni dettagliate sul fatto che probabilmente ComboBox ha bisogno di una finestra popup e un modo per attivare o disattivare se è aperto.

Linee guida 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 il più possibile.

    • Eseguire la progettazione con la consapevolezza che in questa fase (ovvero quando si usa uno strumento di progettazione) è normale che il modello del controllo sia in uno stato incompleto. WPF non offre un'infrastruttura di stato di composizione, quindi i controlli devono essere compilati con l'aspettativa che tale stato potrebbe essere valido.

    • Non generare eccezioni quando un aspetto qualsiasi di un contratto del modello non viene seguito. In tal senso i pannelli non devono generare eccezioni se contengono un numero troppo elevato o troppo esiguo di elementi figlio.

  • Scomporre le funzionalità secondarie in elementi helper del modello. Ogni controllo deve essere incentrato sulla funzionalità di base e sulla reale proposta di valore e definito in base all'uso comune. A tal fine, usare elementi di composizione e helper all'interno del modello per abilitare comportamenti e visualizzazioni secondari, cioè quei comportamenti e quelle visualizzazioni che non contribuiscono alla funzionalità di base del controllo. Questi elementi sono suddivisi in tre categorie:

    • Gli helper autonomi sono controlli o primitive pubblici e riutilizzabili che vengono usati "in modo anonimo" in un modello, vale a dire che l'elemento helper non è in grado di rilevare il controllo al quale è stato applicato lo stile e viceversa. Tecnicamente, qualsiasi elemento può essere di tipo anonimo, ma in questo contesto il termine descrive i tipi che incapsulano funzionalità specializzate per abilitare scenari di destinazione.

    • Gli elementi helper basati su tipi sono nuovi tipi che incapsulano funzionalità specializzate. Questi elementi vengono progettati in genere con una gamma di funzionalità più limitata rispetto ai controlli o alle primitive comuni. A differenza degli elementi helper autonomi, quelli basati su tipi sono in grado di rilevare il contesto in cui vengono usati e in genere devono condividere i dati con il controllo del relativo modello di appartenenza.

    • Gli elementi helper denominati sono controlli o primitive comuni che un controllo prevede di identificare in base al nome all'interno del relativo modello. A tali elementi viene assegnato un nome noto all'interno del modello, consentendo in questo modo 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.

    La tabella seguente include gli elementi helper attualmente usati dagli stili del controllo (l'elenco non è completo):

    Elemento Tipo Usato da
    ContentPresenter Basato su tipi Button, CheckBox, Frame, e così via (tutti i ContentControl tipi) RadioButton
    ItemsPresenter Basato su tipi ListBox, ComboBox, Menue così via (tutti i ItemsControl tipi)
    ToolBarOverflowPanel denominata ToolBar
    Popup Autonomo ComboBox, ToolBar, , ToolTipe Menucosì via
    RepeatButton denominata Slider, ScrollBare così via
    ScrollBar denominata ScrollViewer
    ScrollViewer Autonomo ListBox, ComboBox, , Framee Menucosì via
    TabPanel Autonomo TabControl
    TextBox denominata ComboBox
    TickBar Basato su tipi Slider
  • Ridurre al minimo le associazioni o le impostazioni di proprietà richieste specificate dall'utente per gli elementi helper. Un elemento helper richiede determinate associazioni o impostazioni di proprietà per un corretto funzionamento all'interno del modello di controllo. Tali impostazioni dovrebbero essere indicate, per quanto possibile, dall'elemento helper 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 sono indicate le procedure consigliate:

    • Gli elementi helper con nome devono essere identificati dall'elemento padre, che deve stabilire le impostazioni richieste nell'elemento helper.

    • Le impostazioni richieste per gli elementi helper basati su tipi devono essere stabilite direttamente negli elementi stessi. Tale operazione potrebbe richiedere, da parte dell'elemento di supporto, l'esecuzione di una query sul contesto delle informazioni in cui viene usato, incluso l'oggetto TemplatedParent (il tipo di controllo del modello in cui viene usato). Ad esempio, ContentPresenter associa automaticamente la Content proprietà della TemplatedParent proprietà alla relativa Content proprietà quando viene utilizzata in un ContentControl tipo derivato.

    • Non è possibile ottimizzare allo stesso modo gli elementi helper autonomi, perché, per definizione, l'elemento helper e l'elemento padre non sono in grado di rilevarsi a vicenda.

  • Usare la proprietà Name contrassegnare gli elementi all'interno di un modello. Un controllo che deve individuare un elemento dello stile e accedervi a livello di codice, dovrà usare la proprietà Name e il paradigma FindName. Se l'elemento non viene trovato, il controllo non dovrà generare un'eccezione, ma disabilitare in modo normale e senza visualizzare messaggi la funzionalità in cui tale elemento era richiesto.

  • Usare le procedure consigliate per esprimere lo stato e il comportamento del controllo in uno stile. Di seguito è riportato un elenco ordinato delle procedure consigliate per indicare le modifiche e il comportamento dello stato del controllo in uno stile. È consigliabile usare il primo elemento dell'elenco per abilitare lo scenario.

    1. Associazione di proprietà. Esempio: associazione tra ComboBox.IsDropDownOpen e ToggleButton.IsChecked.

    2. Modifiche delle proprietà attivate o animazioni delle proprietà. Esempio: stato del passaggio del mouse di un oggetto Button.

    3. Comando. Esempio: LineUpCommand / LineDownCommand in ScrollBar.

    4. Elementi helper autonomi. Esempio: TabPanel in TabControl.

    5. Tipi di helper basati su tipi. Esempio: ContentPresenter in Button, TickBar in Slider.

    6. Elementi helper con nome. Esempio: TextBox in ComboBox.

    7. Eventi propagati da tipi di supporto con nome. Se si è in ascolto di eventi propagati da un elemento dello stile, è consigliabile richiedere che l'elemento che ha generato l'evento possa venire identificato in modo univoco. Esempio: Thumb in ToolBar.

    8. Comportamento di OnRender personalizzato. Esempio: ButtonChrome in Button.

  • Usare i trigger degli stili (anziché i trigger dei modelli) con moderazione. 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 (eccetto 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 di stili esistenti. Spesso esistono diversi modi per risolvere un problema. Essere consapevoli e, se possibile, essere coerenti con i modelli di applicazione dello stile del controllo esistenti. Ciò è particolarmente importante per i controlli che derivano dallo stesso tipo di base (ad esempio, ContentControl, ItemsControl, RangeBasee così via).

  • Esporre le proprietà per abilitare scenari di personalizzazione comuni senza applicare nuovamente i modelli. WPF non supporta parti collegabili/personalizzabili, quindi un utente del controllo viene lasciato con solo due metodi di personalizzazione: l'impostazione diretta delle proprietà o l'impostazione delle proprietà tramite stili. È consigliabile pertanto di 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 abilitare gli scenari di personalizzazione:

    • Le personalizzazioni molto comuni devono essere esposte come proprietà nel controllo e usate dal modello.

    • Le personalizzazioni meno comuni (anche se non rare) devono essere esposte come proprietà associate e usate dal modello.

    • È accettabile che le personalizzazioni note ma rare richiedano una nuova applicazione di modelli.

Considerazioni sui temi

  • Gli stili dei temi dovrebbero avere una semantica delle proprietà coerente in tutti i temi, tuttavia non offrono alcuna garanzia. Come parte della documentazione, il controllo dovrebbe includere un documento con la descrizione della semantica delle proprietà del controllo, vale a dire il "significato" di una proprietà per un controllo. Ad esempio, il ComboBox controllo deve definire il significato della proprietà all'interno ComboBoxdi Background . Gli stili predefiniti per il controllo dovrebbero 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à per i vincoli visivi richiesti da un determinato tema. Ad esempio, nel tema Classico non è disponibile un singolo bordo a cui applicare Thickness per molti controlli.

  • Gli stili dei temi non devono necessariamente avere 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 che un controllo non impiega necessariamente lo stesso meccanismo per ottenere un comportamento specifico in tutti i temi. Un tema, ad esempio, può usare un'animazione per esprimere il comportamento al passaggio del mouse mentre un altro tema usa un trigger. Nei controlli personalizzati è pertanto possibile che si verifichino incoerenze nella conservazione di un comportamento. La modifica della proprietà di sfondo, ad esempio, potrebbe non influire sullo stato del passaggio del mouse del controllo se tale stato viene espresso usando un trigger. Tuttavia, se lo stato del passaggio del mouse viene implementato usando un'animazione, la modifica in background potrebbe interrompere in modo irreparabilmente l'animazione e quindi la transizione dello stato.

  • Gli stili dei temi non devono necessariamente presentare una semantica dei "layout" coerente in tutti i temi. L'uso dello stile predefinito, ad esempio, non deve garantire che un controllo abbia le stesse dimensioni in tutti i temi o che il contenuto abbia gli stessi margini o la stessa spaziatura interna in tutti i temi.

Vedi anche