Supponiamo di voler variare, il colore di fondo delle colonne, per meglio evidenziare l’andamento di un grafico, (cose che si inventano quelli del marketing per capirsi, nulla che abbia un senso nella teoria dei grafici).

image

Essendo una richiesta pittoresca in tutti i sensi, ci si deve affidare ai template del Chart per sperare di ottenere qualcosa di interessante e con poco sforzo.

Prima di tutto andiamo a creare un Model che ci faciliti la vita a livello di View

   1: public class ColumnDataPoint : INotifyPropertyChanged
   2: {
   3:     private Brush _background;
   4:  
   5:     public Brush Background
   6:     {
   7:         get { return _background; }
   8:         set
   9:         {
  10:             _background = value;
  11:             OnPropertyChanged("Background");
  12:         }
  13:     }
  14:  
  15:     private string _label;
  16:  
  17:     public string Label
  18:     {
  19:         get { return _label; }
  20:         set
  21:         {
  22:             _label = value;
  23:             OnPropertyChanged("Label");
  24:         }
  25:     }
  26:  
  27:     private decimal _amount;
  28:  
  29:     public decimal Amount
  30:     {
  31:         get { return _amount; }
  32:         set
  33:         {
  34:             _amount = value;
  35:             OnPropertyChanged("Amount");
  36:         }
  37:     }
  38:  
  39:     #region INotifyPropertyChanged Members
  40:  
  41:     public event PropertyChangedEventHandler PropertyChanged;
  42:  
  43:     private void OnPropertyChanged(string name)
  44:     {
  45:         if (PropertyChanged != null)
  46:         {
  47:             PropertyChanged(this, new PropertyChangedEventArgs(name));
  48:         }
  49:     }
  50:  
  51:     #endregion
  52: }

la classe ColumnDataPoint contiene una proprietà Background che restituisce il Brush da usare per colorare la colonna, una proprietà Amount che rappresenta l’altezza della colonna ovvero la y, e una proprietà Label per visualizzare qualcosa di leggibile a livello di asse X. Se si vuole evitare che il model debba conoscere il tipo Brush sarà sufficiente usare un Converter, complicando leggermente la sintassi dello xaml che seguirà.

Andiamo ad inserire un Chart con una ColumnSeries e valorizziamo alcune sue proprietà in binding:

   1: <chartingToolkit:Chart Title="Chart Title">
   2:     <chartingToolkit:ColumnSeries ItemsSource="{Binding Points, Mode=OneWay}" 
   3:             IndependentValueBinding="{Binding Mode=OneWay}" 
   4:             DependentValueBinding="{Binding Amount, Mode=OneWay}" 
   5:             DataPointStyle="{StaticResource ColumnColoredDataPointStyle}">
   6:     </chartingToolkit:ColumnSeries>
   7: </chartingToolkit:Chart>

ItemSource è semplicemente in bind con la proprietà Points del VM che altro non è una List<ColumnDataPoint>

IndipendentValueBinding è in bind senza esplicitare il Path, di fatto equivale a legare i punti dell’asse X agli elementi di Points ovvero a istanze di ColumnDataPoint.

DependentValueBinding è legato alla proprietà Amount di ColumnDataPoint.

A questo punto possiamo focalizzarci sul template del DataPoint, per fare questo la via più comoda è farsi generare da Blend una copia del template che possiamo chiamare ColumnColoredDataPointStyle.

   1: <Style x:Key="ColumnColoredDataPointStyle" TargetType="chartingToolkit:ColumnDataPoint">
   2:     <Setter Property="Background" Value="Orange"/>
   3:     <Setter Property="BorderBrush" Value="Black"/>
   4:     <Setter Property="BorderThickness" Value="1"/>
   5:     <Setter Property="IsTabStop" Value="False"/>
   6:     <Setter Property="Template">
   7:         <Setter.Value>
   8:             <ControlTemplate TargetType="chartingToolkit:ColumnDataPoint">
   9:                 <Border x:Name="Root" Opacity="0" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
  10:                     <VisualStateManager.VisualStateGroups>
  11:                         <VisualStateGroup x:Name="CommonStates">
  12:                             <VisualStateGroup.Transitions>
  13:                                 <VisualTransition GeneratedDuration="0:0:0.1"/>
  14:                             </VisualStateGroup.Transitions>
  15:                             <VisualState x:Name="Normal"/>
  16:                             <VisualState x:Name="MouseOver">
  17:                                 <Storyboard>
  18:                                     <DoubleAnimation Duration="0" Storyboard.TargetName="MouseOverHighlight" Storyboard.TargetProperty="Opacity" To="0.6"/>
  19:                                 </Storyboard>
  20:                             </VisualState>
  21:                         </VisualStateGroup>
  22:                         <VisualStateGroup x:Name="SelectionStates">
  23:                             <VisualStateGroup.Transitions>
  24:                                 <VisualTransition GeneratedDuration="0:0:0.1"/>
  25:                             </VisualStateGroup.Transitions>
  26:                             <VisualState x:Name="Unselected"/>
  27:                             <VisualState x:Name="Selected">
  28:                                 <Storyboard>
  29:                                     <DoubleAnimation Duration="0" Storyboard.TargetName="SelectionHighlight" Storyboard.TargetProperty="Opacity" To="0.6"/>
  30:                                 </Storyboard>
  31:                             </VisualState>
  32:                         </VisualStateGroup>
  33:                         <VisualStateGroup x:Name="RevealStates">
  34:                             <VisualStateGroup.Transitions>
  35:                                 <VisualTransition GeneratedDuration="0:0:0.5"/>
  36:                             </VisualStateGroup.Transitions>
  37:                             <VisualState x:Name="Shown">
  38:                                 <Storyboard>
  39:                                     <DoubleAnimation Duration="0" Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="1"/>
  40:                                 </Storyboard>
  41:                             </VisualState>
  42:                             <VisualState x:Name="Hidden">
  43:                                 <Storyboard>
  44:                                     <DoubleAnimation Duration="0" Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="0"/>
  45:                                 </Storyboard>
  46:                             </VisualState>
  47:                         </VisualStateGroup>
  48:                     </VisualStateManager.VisualStateGroups>
  49:                     <ToolTipService.ToolTip>
  50:                         <ContentControl Content="{TemplateBinding FormattedDependentValue}"/>
  51:                     </ToolTipService.ToolTip>
  52:                     <Grid Background="{TemplateBinding Background}">
  53:                         <Rectangle>
  54:                             <Rectangle.Fill>
  55:                                 <LinearGradientBrush>
  56:                                     <GradientStop Color="#77ffffff" Offset="0"/>
  57:                                     <GradientStop Color="#00ffffff" Offset="1"/>
  58:                                 </LinearGradientBrush>
  59:                             </Rectangle.Fill>
  60:                         </Rectangle>
  61:                         <Border BorderBrush="#ccffffff" BorderThickness="1">
  62:                             <Border BorderBrush="#77ffffff" BorderThickness="1"/>
  63:                         </Border>
  64:                         <Rectangle x:Name="SelectionHighlight" Fill="Red" Opacity="0"/>
  65:                         <Rectangle x:Name="MouseOverHighlight" Fill="White" Opacity="0"/>
  66:                     </Grid>
  67:                 </Border>
  68:             </ControlTemplate>
  69:         </Setter.Value>
  70:     </Setter>
  71: </Style>

Per il nostro scopo è sufficiente eliminare la riga 2 dal template, e nella riga 52 modificare il binding della proprietà Background dal TemplateBinding al binding classico, legandola alla proprietà Background della classe ColumnDataPoint:

   1: <Grid Background="{Binding Background, Mode=OneWay}">

A questo punto abbiamo legato il colore di ogni colonna all’informazione contenuta nel nostro model.

Ultimo tocco necessario e modificare lo style delle label dell’asse x, e legare anche questa alla proprietà Label del model, modificando la seguente riga 6:

   1: <Style x:Key="XAxisLabelStyle" TargetType="chartingToolkit:AxisLabel">
   2:     <Setter Property="IsTabStop" Value="False"/>
   3:     <Setter Property="Template">
   4:         <Setter.Value>
   5:             <ControlTemplate TargetType="chartingToolkit:AxisLabel">
   6:                 <TextBlock Text="{Binding Label, Mode=OneWay}"/>
   7:             </ControlTemplate>
   8:         </Setter.Value>
   9:     </Setter>
  10: </Style>

la versione finale del chart è quindi la seguente:

   1: <chartingToolkit:Chart Title="Chart Title" LegendStyle="{StaticResource LegendStyle1}" >
   2:     <chartingToolkit:ColumnSeries ItemsSource="{Binding Points, Mode=OneWay}" IndependentValueBinding="{Binding Mode=OneWay}" DependentValueBinding="{Binding Amount, Mode=OneWay}" LegendItemStyle="{x:Null}" DataPointStyle="{StaticResource ColumnColoredDataPointStyle}">
   3:         <chartingToolkit:ColumnSeries.IndependentAxis>
   4:             <chartingToolkit:CategoryAxis Orientation="X" Location="Bottom" AxisLabelStyle="{StaticResource XAxisLabelStyle}" />
   5:         </chartingToolkit:ColumnSeries.IndependentAxis>
   6:     </chartingToolkit:ColumnSeries>
   7: </chartingToolkit:Chart>