WPF e l'ereditarietà degli stili

Gli Style di WPF sono certamente uno strumento molto comodo. Una caratteristica interessante degli Style è che uno ne può estendere un altro.

Supponiamo di avere la seguente definizione:

<Style x:Key="MainControlStyle" TargetType="{x:Type Control}" > <Setter Property="FontSize" Value="10" /> <Setter Property="FontWeight" Value="Normal" /> <Setter Property="Padding" Value="2" /> </Style> <Style TargetType="{x:Type Label}" BasedOn="{StaticResource MainControlStyle}" > <Setter Property="FontWeight" Value="Bold" /> </Style>

Con la proprietà BasedOn del secondo Style indichiamo che il secondo stile "eredita" le proprietà impostate da MainControlStyle.

Cosa abbiamo prodotto? Abbiamo due stili: il primo applicabile ad ogni Control che imposta FontSize, FontWeight e Padding; il secondo, che verrà applicato ad ogni Label, che "sovrascrive" il FontWeight impostato dallo stile padre, forzandolo a Bold.

Per completezza faccio un esempio in cui il primo stile non abbia una chiave che lo identifichi:

<Style TargetType="{x:Type Label}" > <Setter Property="FontSize" Value="10" /> <Setter Property="FontWeight" Value="Normal" /> <Setter Property="Padding" Value="2" /> </Style> <Style TargetType="{x:Type Label}" BasedOn="{StaticResource {x:Type Label}}" > <Setter Property="FontWeight" Value="Bold" /> </Style>

Una sola nota: il TargetType dello stile che eredita deve essere compatibile con il TargetType dello stile padre, in sostanza lo stesso tipo o una sottoclasse.

 

Matteo

 

Technorati Tag: ,,,

WPF - Usare più file di risorse

Può capitare di volere fare riferimento a più file di risorse all'interno di una Window o di uno UserControl e farli magari convivere con le risorse definite localmente. La classe ResourceDictionary ci viene incontro con la proprietà MergedDictionary.

<Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="../../MainResources.xaml" /> <ResourceDictionary Source="../../Styles.xaml" /> </ResourceDictionary.MergedDictionaries> <DataTemplate x:Key="MyTemplate"> .... </DataTemplate> </ResourceDictionary> </Window.Resources>

In questo modo possiamo gestire le nostre Resources come una ResourceDictionary che sarà il risultato dell'unione delle nostre risorse locali e le ResourceDictionary definite in file esterni.

 

Matteo

 

 

Caricare un Template in modo condizionale

Come avevo anticipato nel post precedente vorrei esporre un tip per poter selezionare un template per rappresentare un determinato dato in base ad una condizione.

Supponiamo di avere una situazione simile al post precedente, in cui ho un ViewModel che mi rappresenta un ordine: OrderViewModel. Voglio rappresentare OrderViewModel in tre modi diversi in base al fatto che esso rappresenti la scheda di un ordine, un ordine in modifica o un ordine nuovo.

Per fare ciò implemento tre View diverse con tre UserControl e le associo ad altrettanti ControlTemplate nel seguente modo:

<ControlTemplate x:Key="OrderDetailTemplate" TargetType="{x:Type Control}"> <View:OderDetailView /> </ControlTemplate> <ControlTemplate x:Key="OrderCreationTemplate" TargetType="{x:Type Control}"> <View:OrderCreationView /> </ControlTemplate> <ControlTemplate x:Key="OrderModifyTemplate" TargetType="{x:Type Control}"> <View:OderModifyView /> </ControlTemplate>

Perchè faccio tre ControlTemplate? Perchè in questo modo posso creare un DataTemplate associato ad OrderViewModel che carica il template corretto a seconda della condizione definita prima in questo modo:

<DataTemplate DataType="{x:Type ViewModel:OrderViewModel}"> <Control x:Name="control" Template="{StaticResource OrderDetailTemplate}" /> <DataTemplate.Triggers> <DataTrigger Binding="{Binding Path=IsModifying}" Value="True"> <Setter TargetName="control" Property="Template" Value="{StaticResource OrderModifyTemplate}" /> </DataTrigger> <DataTrigger Binding="{Binding Path=IsNew}" Value="True"> <Setter TargetName="control" Property="Template" Value="{StaticResource OrderCreationTemplate}" /> </DataTrigger> </DataTemplate.Triggers> </DataTemplate>

Come si vede dal codice XAML per cambiare il Template associato al Control, usato dal DataTemplate per visualizzare il mio OrderViewModel, è sufficiente definire dei DataTrigger che verificano le proprietà di interesse (nel mio caso IsModifying e IsNew).

Un DataTrigger permette di definire delle condizioni sulle proprietà dell'oggetto rappresentato dal DataTemplate; quando la condizione è verificata vengono eseguiti i Setter contenuti al suo interno, che si occupano si settare il valore Value alla proprietà Property dell'elemento TargetName. Si deve tenere conto che i DataTrigger vengono eseguiti in cascata; vince quindi l'ultima condizione verificata. Nel mio caso è importante tenere conto dell'ordine perchè nel Model quando la proprietà IsNew è True, risulta True anche la proprietà IsModifying. Per ovviare all'inconveniente di avere più condizioni vere simultaneamente avrei potuto usare dei MultiDataTrigger per mettere più condizioni in And, in modo da renderle mutuamente esclusive.

 

Matteo