2015-08-14 21 views
11

Questo è un le migliori pratiche in materia di questione WPF themeing e più specificamente scuoiatura. Questa è più una domanda basata sull'opinione poiché non ho un problema nel fare questo lavoro ma più di un generale chiedendomi se le mie conclusioni coprono tutti gli scenari, e se qualcun altro ha trovato gli stessi pensieri sul problema e cosa era il loro approccio.WPF themeing migliori pratiche

Un po 'di sfondo, il nostro team è tenuto a definire un modo per dare al nostro sistema la possibilità di essere modificabile.

abbiamo rotto questa capacità fino a 2 categorie:

1) Gli stili dei nostri controlli che noi chiamiamo semplicemente 'Tema'.

2) Le risorse utilizzate per personalizzare il loro aspetto chiamato 'pelle' questo include pennelli, e ogni sorta di struct dimensionamento come CornerRadius, BorderThickness ecc

Il modo che una pelle è impostato per il sistema è un semplice caso di unione del dizionario della pelle nelle risorse della nostra app.

<Application.Resources> 
    <ResourceDictionary> 
     <ResourceDictionary.MergedDictionaries> 
      <ResourceDictionary Source="Default.skin.xaml" /> 
      <ResourceDictionary Source="Theme.xaml" /> 
     </ResourceDictionary.MergedDictionaries> 
    </ResourceDictionary> 
</Application.Resources> 

Un altro skin da unire per ultimo nella nostra app.

protected override void OnStartup(StartupEventArgs e) 
    { 
     base.OnStartup(e); 

     string skin = e.Args[0]; 
     if (skin == "Blue") 
     {   . 
      ResourceDictionary blueSkin = new ResourceDictionary(); 
      blueSkin.Source = new Uri("Blue.skin.xaml", UriKind.Relative); 

      Application.Current.Resources.MergedDictionaries.Add(blueSkin); 
     } 
    } 

Interno Theme.xaml:

<!-- Region TextBox ControlTemplate --> 

<ControlTemplate TargetType="{x:Type TextBox}" x:Key="TextBoxTemplate"> 
    <Border Background="{TemplateBinding Background}" 
     BorderBrush="{TemplateBinding BorderBrush}" 
     BorderThickness="{TemplateBinding BorderThickness}" 
     CornerRadius="{StaticResource TextBoxCornerRadius}" > 
     <Border x:Name="shadowBorder" BorderBrush="{StaticResource TextBoxShadowBrush}"         
     CornerRadius="{StaticResource TextBoxInnerShadowCornerRadius}" 
     BorderThickness="{StaticResource TextBoxInnerShadowBorderThickness}" 
     Margin="{StaticResource TextBoxInnerShadowNegativeMarginForShadowOverlap}" > 
      <ScrollViewer x:Name="PART_ContentHost" Padding="{TemplateBinding Padding}" 
        VerticalAlignment="{TemplateBinding VerticalContentAlignment}" 
        HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" /> 
     </Border> 
    </Border> 

    <ControlTemplate.Triggers> 
     <Trigger Property="BorderThickness" Value="0"> 
      <Setter TargetName="shadowBorder" Property="BorderThickness" Value="0" /> 
     </Trigger> 
    </ControlTemplate.Triggers> 
</ControlTemplate> 

<!-- EndRegion --> 

<!-- Region TextBox Style --> 

<Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBox}">  
    <Setter Property="BorderBrush" Value="{StaticResource TextBoxBorderBrush}" /> 
    <Setter Property="Background" Value="{StaticResource TextBoxBackgroundBrush}" /> 
    <Setter Property="BorderThickness" Value="{StaticResource TextBoxBorderThickness}" /> 

    <Setter Property="Padding" Value="{StaticResource TextBoxPadding}" /> 
    <Setter Property="Template" Value="{StaticResource TextBoxTemplate}"/> 
    <Style.Triggers> 

     <Trigger Property="IsMouseOver" Value="True"> 
      <Setter Property="Background" Value="{StaticResource TextBoxIsMouseOverBackgroundBrush}" /> 
      <Setter Property="BorderBrush" Value="{StaticResource TextBoxIsMouseOverBorderBrush}" /> 
     </Trigger> 
     <Trigger Property="IsFocused" Value="True"> 
      <Setter Property="Background" Value="{StaticResource TextBoxIsMouseWithinBackgroundBrush}" /> 
      <Setter Property="BorderBrush" Value="{StaticResource TextBoxIsMouseWithinBorderBrush}" /> 
     </Trigger> 

    </Style.Triggers> 
</Style> 

<!-- EndRegion --> 

Nel TextBox ControlTemplate vi sono elementi legati a DependencyProperties utilizzando TemplateBinding e alcuni, come il CornerRadius e InnerCornerRadius, InnerBorderThickness e InnerBorderBrush che sono dati loro valore da risorse.

Quale sarebbe l'approccio migliore?

creazione di un controllo derivato con le relative proprietà di dipendenza che fare riferimento alle risorse pertinenti e quindi associare gli elementi nel modello di controllo.

O

hanno gli elementi all'interno del modello di riferimento a queste stesse risorse.

Utilizzando l'approccio di dipendenza Proprietà:

Vantaggi:

1) chiarezza, abbiamo un API più chiara per il nostro controllo e una migliore comprensione di come i nostri sguardi di controllo e si comporta il modo in cui lo fa .

2) Il modello non deve essere modificato per essere personalizzabile. Tutto è controllato tramite lo stile.

3) I trigger cambiano anche l'aspetto del controllo senza la necessità di sovrascrivere il modello di controllo, senza necessità di trigger ControlTemplate.

4) "Blendabilty" utilizzando blend posso molto facilmente personalizzare il mio controllo.

5) Gli stili stessi sono ereditabili. quindi se voglio cambiare solo un aspetto del controllo tutto ciò che devo fare è ereditare dallo stile predefinito.

Svantaggi:

1) attuare l'ennesimo controllo personalizzato.

2) Implementare numerose proprietà di dipendenza, alcune delle quali non hanno molto a che fare con il controllo e sono lì solo per soddisfare qualcosa che abbiamo nel nostro modello.

  • Giusto per chiarire questo significa che eredita da TextBox qualcosa come InnerShadowTextBox e attuazione proprietà di dipendenza con in esso per tutto quanto sopra.

Questo si intensificherà se nel mio modello sono presenti numerosi elementi che devono essere personalizzabili.

Qualcosa di simile a questa mostruosità:

<Style x:Key="{x:Type cc:ComplexControl}" TargetType="{x:Type cc:ComplexControl}"> 
    <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="{x:Type cc:ComplexControl}"> 
        <Grid> 
         <Ellipse Fill="Red" Margin="0" Stroke="Black" StrokeThickness="1"/> 
         <Ellipse Fill="Green" Margin="6" Stroke="Red" StrokeThickness="1"/> 
         <Ellipse Fill="Blue" Margin="12"/> 
         <Ellipse Fill="Aqua" Margin="24" /> 
         <Ellipse Fill="Beige" Margin="32"/> 
         <StackPanel Orientation="Horizontal" Width="25" Height="25" 
            VerticalAlignment="Center" HorizontalAlignment="Center"> 
          <Rectangle Fill="Black" Width="2" /> 
          <Rectangle Fill="Black" Width="2" Margin="2,0,0,0"/> 
          <Rectangle Fill="Black" Width="2" Margin="2,0,0,0"/> 
          <Rectangle Fill="Black" Width="2" Margin="2,0,0,0"/> 
         </StackPanel> 

        </Grid> 
       </ControlTemplate> 
      </Setter.Value> 
    </Setter> 
</Style> 

che richiederebbe numerose risorse:

<SolidColorBrush x:Key="Ellipse1Fill">Red</SolidColorBrush> 
<SolidColorBrush x:Key="Ellipse2Fill">Green</SolidColorBrush> 
<SolidColorBrush x:Key="Ellipse3Fill">Blue</SolidColorBrush> 
<SolidColorBrush x:Key="Ellipse4Fill">Aqua</SolidColorBrush> 
<SolidColorBrush x:Key="Ellipse5Fill">Beige</SolidColorBrush> 
<SolidColorBrush x:Key="Ellipse1Stroke">Beige</SolidColorBrush> 
<sys:Double x:Key="Ellipse1StrokeThickness>1</sys:Double> 
     ......... and many more 

avrei una lunga lista di risorse in entrambi i casi. Ma con proprietà di dipendenza. Avrei anche bisogno di assegnare il bisogno di trovare un significato in ogni piccola parte, che a volte non è molto più di "sembra buono" e non ha molto a che fare con il controllo o Cosa succederebbe se domani volessi cambiare il modello .

Utilizzo dell'approccio in cui le risorse sono referenziate all'interno del modello di controllo.

Vantaggi:

1) facile da usare, lato passi la bruttezza descrive gli svantaggi sopra descritti nell'approccio Dp mentre fornisce un "hack" che consente un tema.

Svantaggi:

1) Se vorrei personalizzare ulteriormente il mio controllo, come aggiungere un trigger che influenza il bordo interno della mia TextBox che sarebbe semplicemente dovuto creare un nuovo modello di controllo.

2) Non un'API chiara, diciamo che mi piacerebbe cambiare il BorderBrush del bordo interno in una vista specifica.

  <TextBox> 
      <TextBox.Resources> 
        <SolidColorBrush x:Key="InnerBorderBrush" Color="Red" /> 
      </TextBox.Resources> 
      </TextBox> 

Il che non è poi così male venuto a pensarci ... a volte facciamo con le implementazioni di selezione che utilizzano internamente le risorse specifiche quando sbarazzarsi della selezione inattiva e Hightlight i colori in questo modo:

<ListBox> 
     <ListBox.Resources> 
      <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/> 
      <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Transparent"/> 
      <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="Transparent"/> 
      <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="Transparent"/> 
     </ListBox.Resources> 
</ListBox> 

Conclusioni:

l'ibrido descritto nella Stile TextBox sopra è la strada da percorrere.

1) Le proprietà di dipendenza verranno introdotte solo per gli aspetti del controllo relativi alla logica del controllo, inclusi specifici modelli di parte.

2) I nomi delle risorse dovrebbero essere costituiti da una convenzione di denominazione chiara e separati in file basati sul controllo a cui si riferiscono e usi comuni nelle viste, come i pennelli Common utilizzati nelle viste nella nostra app.

3) I modelli di controllo dovrebbero ambire a essere minimalisti e utilizzare le proprietà di dipendenza esistenti. Come sfondo, primo piano, BorderBrush ecc.

Apprezzerei molto il vostro contributo e le vostre riflessioni sull'argomento, grazie in anticipo.

+0

A causa della natura di questa domanda, è preferibile postarla sul sito [Code Review] (http://codereview.stackexchange.com). – Xavier

risposta

3

Come ha detto Xavier, questa potrebbe essere una domanda migliore per la revisione del codice. Ma trasmetterò alcune riflessioni chiave sulla tua domanda, anche se molte di esse arriveranno allo stile e ai requisiti personali (o di squadra).

Dopo aver creato diverse dozzine di temi, vorrei raccomandare contro i controlli personalizzati quando possibile. Nel tempo, la manutenibilità diminuisce un po '.

Se sono necessarie modifiche minori a uno stile, è preferibile utilizzare DataTemplates e Trigger dati se la situazione lo consente. In questo modo stai cambiando lo stile in modo pulito.

Inoltre, è possibile sfruttare la proprietà BasedOn. Crea il tuo stile "base" e hai più stili che hanno l'attributo BasedOn = "{myBaseStyle} .Questo ti permetterà molte opzioni senza ingombrare il tuo codice.

Come regola generale, consiglio sempre di avere più pennelli/colori/risorse rispetto a più stili o modelli. Di solito abbiamo la nostra gerarchia impostata per colori-> pennelli-> stili-> modelli.Questo aiuta a riutilizzare i colori pur mantenendo la separazione tramite pennelli

Utilizzare DynamicResource invece di StaticResource è utile anche in alcune situazioni in cui si caricano dinamicamente risorse

Spero che questo aiuti. Mi piacerebbe scrivere di più, ma alcuni dei parametri per la scrittura un tema solido è molto specifico per il contesto. Se hai ulteriori esempi, sarei lieto di aggiungere ulteriori informazioni.