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

Comments (12)

  1. Brad says:

    It certainly works, but forgive me as i do not see what that gains… Seems like a lot of work to end up w/ a very small amount of syntactic sugar.

  2. Brad,

    From a developer’s point of view, I can see how at first this really seems like one more way to do something that already exists.  But there are actually some great benefits for this feature especially for the designers.  

    Christian Schormann wrote a nice blog post around the goals behind VSM.  You can find that here, http://electricbeach.org/?p=100.  

    Karen Corby also wrote a series of posts that also talk about the motiviation behind it, http://scorbs.com/2008/06/11/parts-states-model-with-visualstatemanager-part-1-of.

  3. Manoj says:

    Hi Vincent

    What is the functionality of DatePickerTextBox control in the WPFToolKit. Can i use it as a textbox control to capture dates ? I didnt see any difference between a normal textbox and a DatePickerTexBox. Could you please help me understand it.

  4. Manoj,

    Well, for one thing DatePickerTextBox has a WaterMark DP that it uses with the VisualStateManager.  It also is different in the sense that it makes use of VSM directly.  I’m not sure exactly what you mean by using this control to capture dates though.  Could you explain further.

  5. Robert says:

    Hi Vincent,

    Thanks for this great post on using VisualStateMagager! I used your controltemplate for my own datagrid that had alternating row colors. That behaviour disappeared. Can you tell me how I can get back my alternating colors while still using your control template?

  6. sepnotic says:

    Hello Vincent,

    Nice work my friend, downloaded the sample but can’t run the app getting the error:

    "The attachable property ‘VisualStateGroups’ was not found in type ‘VisualStateManager’"

    I have VS2008 SP1 and .NET3.5 SP1.  I’ve heard that VS having problems with this VisualStateGroups.  Is there a work around.  

    Thanks in advance.

  7. sepnotic,

    Do you have the toolkit installed?  That probably shouldn’t matter as the sample references a local copy of the toolkit.  Does it build when the xaml file is not in document view?

  8. Robert,

    Here is something you can do to get the background to work.  In the controltemplate you can add a rectangle to represent the background.  So just below the two rectangle fillColors and the Border, add this rectangle:

    <Rectangle Fill="{TemplateBinding Background}" Opacity=".40" IsHitTestVisible="False" RadiusX="1" RadiusY="1" />                    

    You should be able to use the VSM and the rowbackground colors now.

  9. Woggly says:

    Hi Vincent,

    I am trying to implemnt Silverlight code in WPF now. Your Post helped me a lot. Thank you.

    But I still have a small problem with the alternating row color. I added the line

    <Rectangle Fill="{TemplateBinding Background}" Opacity=".40" IsHitTestVisible="False" RadiusX="1" RadiusY="1" />

    to my code, but it only shows the color set for background, never the alternating color. Could you please post an example that is a bit more precise?

    Thanks in advance!

  10. Mark says:

    Hi Vincent,

    at first i’d like to thank you for your wonderful post. It clears up a lot.

    But then. I am using a large section of your datagridrow controltemplate (i only did leave out the isEditing part). The behaviour i am noticing is that the MouseOver works perfectly. The selected row however stays plain old blue instead of the new orange. The behavior class is working fine of course and a breakpoint there will be hit when selecting a row.

    BTW, the last column (the spare one for filling up the grid) DOES show up in a nice orange fashion.

    Any thoughts on this?

  11. Allawi says:

    Hi Vincent,

    Thank you for your valuable work.

    How much work I need to make the columns highlighetd

    instead of the rows.

    Any idea to do that please?