WPF VSM and DataGrid Sample

Now that the Visual State Manager (VSM) is part of the WPFToolKit, I thought I’d show a basic example of how to utilize VSM with the DataGrid. Some prereq’s and resources:

· You can download the toolkit here

· Info on getting VSM for WPF to work in Blend

· General VSM resource links

So, what I thought I would show is how to get it working with DataGridRows. The toolkit currently uses this VisualStateBehavior pattern to hook up the VisualStateManager to the control. If you take a look in the toolkit, you’ll find behavior classes for Button, Control, ListBoxItem, TextBoxBase, etc. They basically attach listeners of particular DPs and call VisualStateManager.GoToState as necessary.

The Functionality

In this sample I will create a DataGridRowBehavior class that will create visual states for:

· MouseOver

· Selected

· Editing

In the MouseOver state I want to make a somewhat subtle change to the background and for the Selected state I want to use the same background color but make it more illuminated. Here is what it will look like,

vsmDataGrid

For the Editing state, I will use the same background color as the Selected state but I want it to pulse to signify this editable state. Unfortunately a picture will not be able to describe this behavior so you’ll have to download the sample to see it.

Implementation Details

First I’ll start with the DataGridRowBehavior class. This class derives from ControlBehavior and basically attaches to DPs to be notified on value changed. When it is notified, it calls VisualStateManager.GoToState on the particular state that it should be in. This is very similar to the standard way of creating a control with VSM behavior except it is using the attached behavior pattern instead. More on attached behavior’s here on John Gossman’s blog. So here is what my class will look like:

public class DataGridRowBehavior : ControlBehavior

{

  protected override void OnAttach(Control control)

  {

      base.OnAttach(control);

      DataGridRow dataGridRow = (DataGridRow)control;

      Type targetType = typeof(DataGridRow);

      EventHandler handler = delegate { UpdateState(dataGridRow, true); };

      AddValueChanged(DataGridRow.IsMouseOverProperty, targetType, dataGridRow, handler);

      AddValueChanged(DataGridRow.IsSelectedProperty, targetType, dataGridRow, handler);

      AddValueChanged(DataGridRow.IsEditingProperty, targetType, dataGridRow, handler);

  }

  protected override void UpdateState(Control control, bool useTransitions)

  {

      DataGridRow dataGridRow = (DataGridRow)control;

      if (dataGridRow.IsMouseOver)

      {

        VisualStateManager.GoToState(dataGridRow, "MouseOver", useTransitions);
}

      else

      {

        VisualStateManager.GoToState(dataGridRow, "Normal", useTransitions);

      }

      if (dataGridRow.IsEditing)

      {

        VisualStateManager.GoToState(dataGridRow, "Editing", useTransitions);

      }

      else

      {

        VisualStateManager.GoToState(dataGridRow, "NotEditing", useTransitions);

      }

      if (dataGridRow.IsSelected)

      {

        VisualStateManager.GoToState(dataGridRow, "Selected", useTransitions);

      }

      else

      {

        VisualStateManager.GoToState(dataGridRow, "Unselected", useTransitions);

      }

      base.UpdateState(control, useTransitions);
}

}

 

From there you need to update the DataGridRow ControlTemplate to include the VisualStateGroups. I’ve taken the existing DataGridRow ControlTemplate and added two rectangles that will both overlap the main Border, “DGR_Border”. These rectangles will initially have an Opacity of zero and when a particular VisualState is triggered, the Opacity will be updated so that the appearance of the row’s background changes. To see the xaml for the full ControlTemplate, you can download the sample below.

For the VisualStateGroups, here are the groups I’ve set:

<VisualStateManager.VisualStateGroups>

  <VisualStateGroup x:Name="CommonStates">

    <VisualState x:Name="Normal" />

    <VisualState x:Name="MouseOver" />

  </VisualStateGroup>

  <VisualStateGroup x:Name="SelectionStates">

      <VisualState x:Name="Selected" />

      <VisualState x:Name="Unselected" />

  </VisualStateGroup>

  <VisualStateGroup x:Name="EditingStates">

      <VisualState x:Name="Editing" />

      <VisualState x:Name="NotEditing" />

  </VisualStateGroup>

</VisualStateManager.VisualStateGroups>

 

Then for each state I setup a Storyboard to create the behavior for that state. Here is an example of MouseOver:

<VisualState x:Name="MouseOver">

  <Storyboard>

      <DoubleAnimation Storyboard.TargetName="fillColor" Storyboard.TargetProperty="Opacity" Duration="0" To=".35"/>

  </Storyboard>

</VisualState>

 

fillColor represents one of the rectangles that I’ve added to the ControlTemplate. I do something similar with the Selected state and for the Editing state I create a pulsing effect by animating the rectangle’s Fill property with a different gradient brush that repeats forever.

With the VisualStateGroups setup, the last thing I have to do is register DataGridRowBehavior with the VisualStateBehavior property. This is how the VisualStateManager will recognize that my ControlTemplate makes use of VisualStateManager. I set that up in my DataGridRow style like so:

<Style x:Key="defaultRowStyle" TargetType="{x:Type dg:DataGridRow}">

  <Setter Property="dg:VisualStateBehavior.VisualStateBehavior" Value="{StaticResource DataGridRowBehavior}" />

</Style>

 

So with that, here is the full sample. I did go over the implementation details pretty quickly so please take a look at the sample to see the full code and markup details. Stay tuned for more!

 

VSM_and_DataGrid_Sample.zip