다음을 통해 공유


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

  • Anonymous
    November 04, 2008
    PingBack from http://www.tmao.info/wpf-vsm-and-datagrid-sample/

  • Anonymous
    November 04, 2008
    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.

  • Anonymous
    November 05, 2008
    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.

  • Anonymous
    November 05, 2008
    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.

  • Anonymous
    November 05, 2008
    The comment has been removed

  • Anonymous
    November 19, 2008
    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?

  • Anonymous
    December 01, 2008
    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.

  • Anonymous
    December 03, 2008
    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?

  • Anonymous
    December 04, 2008
    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.

  • Anonymous
    February 23, 2009
    The comment has been removed

  • Anonymous
    March 11, 2009
    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?

  • Anonymous
    July 22, 2009
    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?