Skip to content
Aug 20 09

Using FileSystemWatcher to synchronize ComboBox options

by Samuel Moura

In this post I will present you with my latest addition to my personal user experience arsenal: direct file system synchronization. You will be shown how easily it is for you to setup a FileSystemWatcher to observe file changes on disc, astonishing your user when he least expects.

Download sample source code

You can download a running sample for this blog post by following this link.

Introduction

Suppose you have a report templates folder under your application documents folder containing a series of report formats that you want your user to be able to add, edit or remove through the file system.

FileSystemWatcher-ss01

In that folder you store Report1.rpt, Report2.rpt and Report3.rpt. Your application has a dialog that enables the user to select which report template he should use to print data. He is running the application and notices that the template he wants to use is not on the application’s template folder. He goes to the email that his boss had sent him, and gets the NewReport.rpt file. He copies the file to your Software’s template folder.

The Problem

At this point the user has the report on his folder but the combo box on the UI he is using has not update. There is no way for him to select the new template without reopening the UI, or maybe the application (if you are caching the report templates list). This may force him to lose some work.

A normal approach in this scenario is to place a refresh button in the UI that may be used to refresh the templates list. Although this fixes the problem, it forces the user to an additional step.

Let FileSystemWatcher do its magic

A much clear solution is to use the FileSystemWatcher class that is part of System.IO and it has lived there for a long time now! You would create a method that updates the report templates from disk. Then create a new FileSystemWatcher that monitors *.rpt files that change on the templates folder.

var watcher = new FileSystemWatcher(TemplatesPath, TemplatesSearchPattern);
watcher.Created += (fileSystemWatcher_FilesChanged);
watcher.Changed += (fileSystemWatcher_FilesChanged);
watcher.Deleted += (fileSystemWatcher_FilesChanged);
watcher.Renamed += (fileSystemWatcher_FilesChanged);
watcher.EnableRaisingEvents = true;

Has you can see you can monitor specific events only. In this case we want to monitor every change that might occur in our report templates folder that respects to rpt files. Now whenever a change is made on the file system the UpdateAvailableTemplates method will be called and we can synchronize the report templates list.

The only additional catch is to guarantee that UpdateAvailableTemplates runs on the thread it is supposed to run. We do this by saving the SynchronizationContext in which our FileSystemWatcher is created.

this.syncContext = SynchronizationContext.Current;

Then when a change is detected we post the UpdateAvailableTemplates to the context we saved on the constructor:

private void fileSystemWatcher_FilesChanged(object sender, FileSystemEventArgs e)
{
    this.syncContext.Post(UpdateAvailableTemplates, null);
}

To test your application go ahead and delete, rename, create some files and see how your application keeps on updating the list it displays. Notice that it is instantaneous.

You can download a running sample for this blog post by following this link.

Conclusion

Obviously that the templates list will not change that often, you may be wondering if this extra mile is worth the effort. Either way you will have to implement the UpdateAvailableTemplates logic. At the cost of these extra lines of code you can add that extra touch of excellence to your user’s experience.

Most of your users will not require this feature but when they actually need it, it will probably save them a lot of effort. For the effort it takes to implement such thing, if you save 1 costumer the endeavor of filling that form again it will be worth it.

When I added this to the software I am currently working on, I had a few WOW’s, when people saw that the UI just worked. After several deletes, adds, edits, etc, on the files the software updated in real time!

Shout it kick it on DotNetKicks.com

Jul 8 09

Playing Storyboards on DataTriggers to animate a Path

by Samuel Moura

In the application I am working on, we had to display an image accordingly to the value of a field in our ViewModel represented by an Enum. The Enum had 3 possible values: normal, up or down. An arrow pointing up, right or down was to be drawn as a result of the bounded field’s value.

WpfAnim-ss001

Download Sample Source code

You can download the sample source code for this blog post by following this link.

Initial sample setup

The plan was to draw an arrow using a Path. Then use animations to rotate the arrow accordingly to what was set on the ViewModel.

I went ahead and created a sample application that draws all this stuff on the UI. It also adds three buttons at the bottom which allow us to manipulate the value on the bounded field to control the tendency that should be displayed:

WpfAnim-ss002

As mentioned before, a Path was used to create the arrow. For us to be able to animate the arrow’s rotation we had to create a render transform with the initial value to animate from:

<Path x:Name="path"
      Fill="#FFCC0707" Stretch="Fill" Stroke="#FF000000"
      Height="32" Width="64" RenderTransformOrigin="0.5,0.5"
      Data="M78,88.333333 L141.5,87.500336 142.16667,65.833664 175.83283,96.833545 143.49989,127.16709 142.16656,109.16693 78.166666,108.16694 z">
    <Path.RenderTransform>
        <TransformGroup>
            <ScaleTransform ScaleX="1" ScaleY="1"/>
            <SkewTransform AngleX="0" AngleY="0"/>
            <RotateTransform Angle="0"/>
            <TranslateTransform X="0" Y="0"/>
        </TransformGroup>
    </Path.RenderTransform>
</Path>

Next I added the three Storyboards: OnTendencyUp, OnTendencyNormal and OnTendencyDown. Each storyboard animates the rotation of the arrow to its corresponding Enum value.

<Storyboard x:Key="OnTendencyUp">
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                   Storyboard.Target="{Binding TemplatedParent}"
                                   Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="-45"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="OnTendencyNormal">
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                   Storyboard.Target="{Binding TemplatedParent}"
                                   Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="OnTendencyDown">
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                   Storyboard.Target="{Binding TemplatedParent}"
                                   Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="45"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

Everything is now in place for us to start playing with the animation. We have a path and three animations that change its rotation.

Controlling Storyboards with DataTriggers based on the Enum value

The next step was to create the DataTriggers. The plan was that whenever the tendency changes we kick off the consequent storyboard. For this we would use DataTrigger.EnterActions to initiate the storyboard. The DataTriggers should be added to the style of the Path:

<Path.Style>
    <Style>
        <Style.Triggers>
            <DataTrigger Binding="{Binding Tendency}" Value="Down">
                <DataTrigger.EnterActions>
                    <BeginStoryboard Storyboard="{StaticResource OnTendencyDown}"  x:Name="OnTendencyDown_BeginStoryboard"/>
                </DataTrigger.EnterActions>
            </DataTrigger>
            <DataTrigger Binding="{Binding Tendency}" Value="Up">
                <DataTrigger.EnterActions>
                    <BeginStoryboard Storyboard="{StaticResource OnTendencyUp}" x:Name="OnTendencyUp_BeginStoryboard"/>
                </DataTrigger.EnterActions>
            </DataTrigger>
            <DataTrigger Binding="{Binding Tendency}" Value="Normal">
                <DataTrigger.EnterActions>
                    <BeginStoryboard Storyboard="{StaticResource OnTendencyNormal}" x:Name="OnTendencyNormal_BeginStoryboard"/>
                </DataTrigger.EnterActions>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Path.Style>

In theory, whenever a value changes the DataTrigger fires the storyboard thus animating the rotation either way. The strange thing is that no animation is played, none whatsoever, zip, rien, niente, nenhuma! This happens because we have competing DataTriggers over the same binding making the last one the winner! (For additional information check this link)

If you try and change the order by which the DataTriggers are displayed you will notice that a single animation will be played (the last one on the list), and when the rotation gets to its target value no other animation will play.

Controlling the Storyboards through Buttons and EventTriggers

Now if you trigger the animations via normal event triggers all animations will play appropriately. I went ahead and produced a sample that fires the storyboards based on the clicking event on each button:

<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="btnDown">
    <BeginStoryboard Storyboard="{StaticResource OnTendencyDown}"/>
</EventTrigger>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="btnNormal">
    <BeginStoryboard Storyboard="{StaticResource OnTendencyNormal}"/>
</EventTrigger>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="btnUp">
    <BeginStoryboard Storyboard="{StaticResource OnTendencyUp}"/>
</EventTrigger>

In order for the animations to play correctly we have to change the bindings on the storyboards that lead to the rotation of the Path. Instead of using TemplatedParent we will use a binding to a specific element. For each storyboard I changed the binding to:

(…)
Storyboard.Target="{Binding ElementName=path}"
(…)

Now, all the animations play appropriately every time you click one of the buttons.

How to enable controlling the Storyboards with DataTriggers

The solution to the problem with the DataTriggers is for us to remove the Storyboard on each DataTrigger whenever DataTrigger.ExitActions is invoked, likewise:

<DataTrigger Binding="{Binding Tendency}" Value="Down">
    <DataTrigger.EnterActions>
        <BeginStoryboard Storyboard="{StaticResource OnTendencyDown}"
                         x:Name="OnTendencyDown_BeginStoryboard"/>
    </DataTrigger.EnterActions>
    <DataTrigger.ExitActions>
        <RemoveStoryboard BeginStoryboardName="OnTendencyDown_BeginStoryboard" />
    </DataTrigger.ExitActions>
</DataTrigger>
<DataTrigger Binding="{Binding Tendency}" Value="Up">
    <DataTrigger.EnterActions>
        <BeginStoryboard Storyboard="{StaticResource OnTendencyUp}"
                         x:Name="OnTendencyUp_BeginStoryboard"/>
    </DataTrigger.EnterActions>
    <DataTrigger.ExitActions>
        <RemoveStoryboard BeginStoryboardName="OnTendencyUp_BeginStoryboard" />
    </DataTrigger.ExitActions>
</DataTrigger>
<DataTrigger Binding="{Binding Tendency}" Value="Normal">
    <DataTrigger.EnterActions>
        <BeginStoryboard Storyboard="{StaticResource OnTendencyNormal}"
                         x:Name="OnTendencyNormal_BeginStoryboard"/>
    </DataTrigger.EnterActions>
    <DataTrigger.ExitActions>
        <RemoveStoryboard BeginStoryboardName="OnTendencyNormal_BeginStoryboard" />
    </DataTrigger.ExitActions>
</DataTrigger>

By removing the Storyboards on ExitActions we guarantee that animations will play correctly every time the Storyboard is initiated.

Conclusion

This solution feels a bit hacky but the truth is that it fixed our issue allowing our animations to play smoothly!

You can download a sample application for this post by following this link.

Updates

2009/07/10: I have updated the order by which the DataTriggers are defined on the ‘How to enable controlling the Storyboards with DataTriggers’. The sample source code was updated as well. An issue remains however: when you click up and then click down there will be a jump in the animation from the up to normal and then animate to down. I am still trying to figure this one out…

Thank you to Carlos for spotting this glitch!

Shout it kick it on DotNetKicks.com

Jun 15 09

WPF Toolkit DataGrid Part IV: TemplateColumns and Row Grouping

by Samuel Moura

This post is part of the WPF Toolkit DataGrid series. Here is a list with the complete set of blog posts:

Introduction

In Part III we got to know a bit better how stuff gets rendered on the WPF DataGrid. We have gone through several samples on how to configure the way data gets displayed on each column by either creating new column types or by styling existing ones.

In Part IV we will have a look into more advanced concepts. First we will see how we can use the DataGridTemplateColumn to imitate the behavior of a DataGridComboBoxColumn and to show a small chart representing the age deviation from average for each player. We will then have a look on how we can group rows on our DataGrid using two separate methodologies.

Roadmap

  • Creating a ComboBox column from a TemplateColumn
  • Using DataGridTemplateColumns to create a chart column
  • Grouping rows using ItemsControl.GroupStyle
  • Fake grouping with the help of the ViewModel

Download sample source code

Here is a list with the samples presented on this blog post:

Creating a ComboBox column from a TemplateColumn

The ItemsSource property of DataGridComboBox column does not allow for you to directly bind to data on your DataContext, at least for now (WPF Toolkit – March 2009 release). It is likely that in a future release binding support will be added (if you want further information on this checkout this CodePlex workitem).

There are a lot of ways in which you could implement this! For instance, you could extend DataGridComboBoxColumn adding binding support on it, you could add the binding support directly to DataGridComboBoxColumn recompiling WPF Toolkit, etc.

The solution I came up with in my application was a simpler one, IMHO. I restyled a DataGridTemplateColumn to behave like a DataGridComboBox column.

This is very simple to accomplish. The only issue I found was that the effort required to actually finding the property to bind to from within the created template – I had to use a tricky binding expression.

You must start by adding to your view model the data you are going to bind to:

 (…)
public IList<string> Categories { get; set; }
(…)
public TeamModel()
{
    Categories = new List<string>
        { "Mini", "Cadet", "Junior", "Senior", "Veteran" };
}
(…)

In order for you to use a DataGridTemplateColumn you must create two specific templates, one for the cell in its default view and one for when it is in editing mode. In the default view of our cell only text will be displayed. You can just add a new template that uses a label to render the selected text:

<DataTemplate x:Key="ComboBoxCellDataTemplate">
    <Label x:Name="lblCombo" Content="{Binding Category}"
           Style="{StaticResource BaseLabelCellStyle}" />
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.Categories}" Value="Both">
            <Setter TargetName="lblCombo" Property="IsEnabled" Value="False" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

As you may have noticed you had to use RelativeSource to find the properties to bind to. Now you have to create the editing mode template that uses a ComboBox and lets the users select which category he wants to set a player in.

<DataTemplate x:Key="ComboBoxCellEditingTemplate">
    <ComboBox x:Name="comboBox"
              ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.Categories}"
              SelectedItem="{Binding Category}"/>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Enabled}" Value="False">
            <Setter TargetName="comboBox" Property="IsEnabled" Value="False" />
        </DataTrigger>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.Categories}" Value="Both">
            <Setter TargetName="comboBox" Property="IsEnabled" Value="False" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

After adding these two DataTemplates to the resources you can change the definition of the Category column to use the DataGridTemplateColumn with these two specific templates.

<WpfToolkit:DataGridTemplateColumn
    Header="Category" Width="1*"
    CellTemplate="{StaticResource ComboBoxCellDataTemplate}"
    CellEditingTemplate="{StaticResource ComboBoxCellEditingTemplate}"/>

It could not get simpler than this! You have just made a ComboBoxColumn out of a DataGridTemplateColumn.

This is how our sample application is looking after these changes:

WpfToolkitDataGrid-ss015a

You can download a working sample of this code by following this link.

Using DataGridTemplateColumns to create a chart column

Each player shown in the DataGrid has an age associated with him. For each player we will be presenting a chart column picturing the deviation of his age towards the team average.

All calculations regarding the team age average and each players deviation should be done in the backend. Basically you will have to change the code behind file for TeamView since there is where we setup the view model for the view (this is definitely a bad practice – it is being done this way for simplicity). In the DeviationChart column we will expose the DeviationPercentage previously calculated. This value will be used to calculate the width of the bars to draw our chart with.

Essentially this is the code used to calculate these values:

(…)
SetupTeamAgeAverage(playerAges.Where(player => player.Parent == alphaTeam));

(…)

private static void SetupTeamAgeAverage(IEnumerable<IPlayerAge> teamPlayers)
{
    double teamAgeAverage = teamPlayers.Average(x => x.Age);
    foreach (var player in teamPlayers)
    {
        player.Deviation = player.Age - teamAgeAverage;
        player.DeviationPercentage =
            (player.Age - teamAgeAverage) / teamAgeAverage;
    }
}
(…)

Now that the data is processed you need to create a new element style to apply on the DataGridTemplateColumn that picks this value and transforms it to a chart.

Deviation percentage goes from -1 to 1. If it is negative we will create a bar on the left of the chart, otherwise a bar will be added on the right. The length of the bar will be proportional to the amount of deviation. All this will be done using standard grids, background colors and star notation on grid column widths.

Since we do not have a direct way of converting a value into a percentage of width we will be doing so in a hacky way by using an IValueConverter (you can see the actual implementation of the converter by downloading the sample code).

After creating the converter you will have to add this new style to the resources of your view:

<Style x:Key="BarChartCellStyle" TargetType="{x:Type WpfToolkit:DataGridCell}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridCell}">
                <Grid Background="{TemplateBinding Background}">
                    <Grid x:Name="BarChart" Background="LightBlue"
                          Height="14"  Margin="2,0,2,0">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition x:Name="lowerToleranceColumn"
                                              Width="*"/>
                            <ColumnDefinition Width="1"/>
                            <ColumnDefinition x:Name="upperToleranceColumn"
                                              Width="*"/>
                        </Grid.ColumnDefinitions>

                        <Grid Margin="2,0,1,0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="{Binding DeviationPercentage,
                                    Converter={StaticResource percentageWidthConverter},
                                    ConverterParameter=1}"/>
                                <ColumnDefinition Width="{Binding DeviationPercentage,
                                    Converter={StaticResource percentageWidthConverter},
                                    ConverterParameter=2}"/>
                            </Grid.ColumnDefinitions>
                            <Border x:Name="LowerBarChart" Grid.Column="1"
                                    Height="10" Background="MediumBlue">
                                <TextBlock x:Name="LowerToleranceLabel"
                                           Text="&lt;" Foreground="White"
                                           HorizontalAlignment="Left" VerticalAlignment="Center"
                                           Margin="2,-2,0,0"/>
                            </Border>
                        </Grid>

                        <Border Grid.Column="1" Width="1" Background="Gray" />

                        <Grid Grid.Column="2" Margin="1,0,2,0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="{Binding DeviationPercentage,
                                    Converter={StaticResource percentageWidthConverter},
                                    ConverterParameter=3}"/>
                                <ColumnDefinition Width="{Binding DeviationPercentage,
                                    Converter={StaticResource percentageWidthConverter},
                                    ConverterParameter=4}"/>
                            </Grid.ColumnDefinitions>
                            <Border x:Name="UpperBarChart" Height="10" Background="DarkOrange">
                                <TextBlock x:Name="UpperToleranceLabel"
                                           Text="&gt;" Foreground="White"
                                           HorizontalAlignment="Right" VerticalAlignment="Center"
                                           Margin="0,-2,2,0"/>
                            </Border>
                        </Grid>

                        <Border BorderBrush="White" BorderThickness="1,1,0,0" Grid.ColumnSpan="3"/>
                        <Border BorderBrush="LightGray" BorderThickness="0,0,1,1" Grid.ColumnSpan="3"/>
                    </Grid>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Now that you have a new style on your view’s resource dictionary you can go ahead and set the corresponding styles in the DataGridTemplateColumn. To do so you will have to replace the Deviation Chart column on your DataGrid by a DataGridTemplateColumn:

(…)
<WpfToolkit:DataGridTemplateColumn
    Header="Deviation Chart" Width="1*"
    HeaderStyle="{StaticResource CenterAlignedColumnHeaderStyle}"
    CellStyle="{StaticResource BarChartCellStyle}"/>
(…)

By replacing this column we gave our DataGrid a much cooler look. For each player an age chart is displayed on the right:

WpfToolkitDataGrid-ss015

You can download a working sample of this code by following this link.

Grouping rows using ItemsControl.GroupStyle

From the beginning of this series we have displayed data that was supposed to be grouped. As you have noticed there are two teams and several players per team. A team should not be displayed as a regular row on our DataGrid, it should be some sort of group header that we can collapse and expand as required, showing the players that are part of it.

In this implementation we will follow the recommendations on how to do grouping on WPF Toolkit’s DataGrid.

For you to be able to do grouping you will have to create a CollectionViewSource and specify by which field the grouping will be done. In this case grouping will be done on each player’s Parent field, which corresponds to the actual Team instance.

<CollectionViewSource x:Key="PlayerData">
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="Parent"/>
    </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

Note that Source is not specified for the CollectionViewSource. This is due to the fact that the list is being created in code behind; as a consequence, it cannot be instantiated in xaml. You will have to set the Source for this collection after building the players list on the code behind file:

var dataProvider = (CollectionViewSource)FindResource("PlayerData");
dataProvider.Source = playerAges;

After setting up this grouped collection view source you will have to change the way binding is done on the DataGrid. Previously we were binding to a field on our view model. Now you will have to bind to a static resource.

(…)
<WpfToolkit:DataGrid
    ItemsSource="{Binding Source={StaticResource PlayerData}}"
    HorizontalScrollBarVisibility="Hidden" SelectionMode="Extended"
    CanUserAddRows="False" CanUserDeleteRows="False"
    CanUserResizeRows="False" CanUserSortColumns="False"
    AutoGenerateColumns="False"
    RowHeaderWidth="17" RowHeight="25">
(…)

The only thing left is for you to create the style of the group’s header. An expander will be used to present the grouped data. On the expander’s header each team’s name will be displayed:

<Style x:Key="GroupHeaderStyle" TargetType="{x:Type GroupItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type GroupItem}">
                <Expander IsExpanded="True"
                          Background="{StaticResource GroubHeaderBackgroundBrush}"
                          Foreground="{StaticResource DefaultControlForegroundBrush}">
                    <Expander.Header>
                        <TextBlock Text="{Binding Name.Name}"/>
                    </Expander.Header>
                    <ItemsPresenter />
                </Expander>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Now that everything is in place you have to setup the DataGrid to use this specific style when rendering the groups:

<WpfToolkit:DataGrid.GroupStyle>
    <GroupStyle ContainerStyle="{StaticResource GroupHeaderStyle}">
        <GroupStyle.Panel>
            <ItemsPanelTemplate>

                <WpfToolkit:DataGridRowsPresenter/>
            </ItemsPanelTemplate>
        </GroupStyle.Panel>
    </GroupStyle>
</WpfToolkit:DataGrid.GroupStyle>

After these changes this is how our DataGrid is looking:

WpfToolkitDataGrid-ss016

If you click on a group header so that it collapses its contents this is how it will look:

WpfToolkitDataGrid-ss017

This implementation works well when you do not allow editing on you cells. If you allow the users to edit something on your DataGrid you will notice that the edited value goes to the bottom of the list instead of remaining in its actual row. The following screenshot is the outcome of editing the age of John Smith. Notice that John was on top and is now presented at the bottom of the Alpha Team player list.

WpfToolkitDataGrid-ss018

Our grouped data is being handled by ListCollectionView. I took a peek on the code for ListCollectionView using Reflector and found out that the source of this problem is that when you edit an item the item is resorted for grouping purposes like follows:

public void CommitEdit()
{
(…)
        if (this.IsGrouping)
        {
            this.RemoveItemFromGroups(item);
            this.AddItemToGroups(item);
        }
(…)
}

BAAM! Obviously that by removing and adding the item back again to the list will cause the item to be presented last. Not only does this affect the order of our list –which is not the intended result – it also deselects the row we are working with (I confess I did not take the time to see this through, I was short on time to finish up what I was working on time, thus the workaround presented on the following subject).

You can download a working sample of this code by following this link.

Fake grouping with the help of the ViewModel

The grouping method previously demonstrated, although of clear implementation, has several issues that could not be easily fixed. In my application I ended up using another method for grouping data, a more archaic method but will allow us greater control over how data behaves.

The actual grouping of data will be our view model’s responsibility. Group headers will be authentic rows but with a different style. The logic for hiding or showing rows accordingly to their groups being expanded or not will be controlled by the view model through data binding.

In the PlayerAge class – the data behind each row – there is a field named IsExpanded that is only set on Parent rows (the teams). We can control if the child rows are collapsed or not by adding a DataTrigger that binds to the IsExpanded property of the Parent row for each player.

There will be two different control templates: one for players and one for teams. You will have to create a new style for your DataGridRows. By default you will set the ControlTemplate to be the one regarding the Players:

<ControlTemplate TargetType="{x:Type WpfToolkit:DataGridRow}">
    <Border x:Name="DGR_Border"
            Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}"
            SnapsToDevicePixels="True">
        <WpfToolkit:SelectiveScrollingGrid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>

            <WpfToolkit:DataGridCellsPresenter
                Grid.Column="1"
                ItemsPanel="{TemplateBinding ItemsPanel}"
                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
            <WpfToolkit:DataGridDetailsPresenter
                WpfToolkit:SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding
                    RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGrid}},
                    Path=AreRowDetailsFrozen,
                    Converter={x:Static WpfToolkit:DataGrid.RowDetailsScrollingConverter},
                    ConverterParameter={x:Static WpfToolkit:SelectiveScrollingOrientation.Vertical}}"
                Grid.Column="1" Grid.Row="1"
                Visibility="{TemplateBinding DetailsVisibility}" />
            <WpfToolkit:DataGridRowHeader
                WpfToolkit:SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"  Grid.RowSpan="2"
                Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGrid}},
                    Path=HeadersVisibility,
                    Converter={x:Static WpfToolkit:DataGrid.HeadersVisibilityConverter},
                    ConverterParameter={x:Static WpfToolkit:DataGridHeadersVisibility.Row}}"/>
        </WpfToolkit:SelectiveScrollingGrid>
    </Border>
    <ControlTemplate.Triggers>
        <DataTrigger Binding="{Binding Parent.IsExpanded}" Value="False">
            <Setter TargetName="DGR_Border" Property="Visibility" Value="Collapsed"/>
        </DataTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

The most important part of this ControlTemplate is the DataTrigger on the bottom. Has you can see we set the visibility of our row based on if the parent is expanded or not.

Now you will have to add a new ControlTemplate for the parent rows (teams). For this purpose you will have to set a DataTrigger on your Style that checks whether the Parent of the bounded item is null or not – indicating that it is a parent, or not (for the complete style please see the sample code for this post).

<Style.Triggers>
    <DataTrigger Binding="{Binding Parent}" Value="{x:Null}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridRow}">
                    <Border x:Name="DGR_Border"
                            Background="{StaticResource GroubHeaderBackgroundBrush}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            SnapsToDevicePixels="True">
                        <WpfToolkit:SelectiveScrollingGrid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>

                            <WpfToolkit:DataGridCellsPresenter
                                Grid.Column="1"
                                Background="Red"
                                ItemsPanel="{TemplateBinding ItemsPanel}"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                            <WpfToolkit:DataGridDetailsPresenter
                                WpfToolkit:SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding
                                    RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGrid}},
                                    Path=AreRowDetailsFrozen,
                                    Converter={x:Static WpfToolkit:DataGrid.RowDetailsScrollingConverter},
                                    ConverterParameter={x:Static WpfToolkit:SelectiveScrollingOrientation.Vertical}}"
                                Grid.Column="1" Grid.Row="1"
                                Visibility="{TemplateBinding DetailsVisibility}" />
                            <WpfToolkit:DataGridRowHeader
                                WpfToolkit:SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
                                Grid.RowSpan="2"
                                Visibility="{Binding RelativeSource={RelativeSource
                                    AncestorType={x:Type WpfToolkit:DataGrid}},
                                    Path=HeadersVisibility,
                                    Converter={x:Static WpfToolkit:DataGrid.HeadersVisibilityConverter},
                                    ConverterParameter={x:Static WpfToolkit:DataGridHeadersVisibility.Row}}"/>
                        </WpfToolkit:SelectiveScrollingGrid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </DataTrigger>
</Style.Triggers>

The ControlTemplate’s trigger was removed given that this row is a parent and will not collapse. After inserting this new style for DataGridRows on the view’s resource dictionary, you will immediately see the result, were group headers display different from other cells. The problem is that we still have not implemented a mechanism that allows us to modify the expanded state of the group.

An example on how to control the IsExpanded state of your team is to create a specific CellStyle for the first column that by knowing which type of row it is binding to will display a toggle button that allows the user to expand or collapse the group:

<Style x:Key="FirstColumnCellStyle" TargetType="{x:Type WpfToolkit:DataGridCell}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridCell}">
                <Grid Background="{TemplateBinding Background}">
                    <ContentPresenter HorizontalAlignment="Right"
                                      VerticalAlignment="Center" Margin="0,0,4,0" />
                </Grid>
                <ControlTemplate.Triggers>
                    <DataTrigger Binding="{Binding ToleranceState}"
                                 Value="NotToleranced">
                        <Setter Property="IsEnabled" Value="False" />
                    </DataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <DataTrigger Binding="{Binding Parent}" Value="{x:Null}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridCell}">
                        <Grid Background="{TemplateBinding Background}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>
                            <ToggleButton VerticalAlignment="Center"
                                          IsChecked="{Binding IsExpanded}"
                                          Height="12" Width="12" Content="+" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </DataTrigger>
    </Style.Triggers>
</Style>

By now you will be able to expand or collapse each team’s players. This is how the DataGrid looks when these styles get applied.

WpfToolkitDataGrid-ss019

Now when you run the sample you can edit any cell and see that it does not change. I have kept the same style for the header rows but you can easily change it so that no cells are rendered on it, you could even make it look like the one on the previous topic using an expander.

You can download a working sample of this code by following this link.

End of Part IV

In this part we have delve into more advanced concepts. We have seen how to work around the issue with binding and the DataGridComboBoxColumn. We also saw the amazing power and flexibility of the DataGridTemplateColumn. The second half of the blog post walked you through two different methods of grouping data within the DataGrid.

By now, and assuming you have gone through all the IV parts, you should be able to represent any type of data as you see fit.

In the next and final part of this series we will look at DataGridCellDetails as well as handling insertion / removal of data from your DataGrid. Stay tuned for Part V Dear Reader!

Shout it kick it on DotNetKicks.com

May 19 09

WPF Toolkit DataGrid, Part III – Playing with Columns and Cells

by Samuel Moura

This post is part of the WPF Toolkit DataGrid series. Here is a list with the complete set of blog posts:

Introduction

In Part II we gave a bluish style to our DataGrid. In this part we will go through setting up how our data gets displayed within the DataGrid. We will have a look on how to create styles for our cells and cell elements along with creating some new types of DataGridColumns that extend existing ones in order to enrich them with further functionality.

Roadmap

  1. Aligning DataGridColumnHeaders and DataGridCells content using Styles
  2. Aligning DataGridCells content by extending a DataGridColumn
  3. Enabling and disabling DataGridRows
  4. Creating a LabeledTextBoxColumn
  5. Creating an AutoCommitCheckBoxColumn

Download sample source code

Here is a list with the samples presented on this blog post:

Aligning DataGridColumnHeaders and DataGridCells content

If you are using the DataGrid then you will surely want to control the alignment of text within the cells and column headers. Unfortunately the WPF Toolkit DataGrid does not support setting content alignment on a DataGridColumn. To align cells content you will have to create specific styles for DataGridColumnHeaders based on their default style and assign them individually to each column.

By the end of Part II this was how our DataGrid was looking:

WpfToolkitDataGrid-ss010

To be able to align DataGridColumnHeaders content go to your “DataGrid.Generic.xaml” file and add a couple of styles for right and center aligned column headers:

<!-- Right Aligned DataGridColumnHeader Style -->
<Style x:Key="RightAlignedColumnHeaderStyle"
       TargetType="{x:Type WpfToolkit:DataGridColumnHeader}"
       BasedOn="{StaticResource ColumnHeaderStyle}">
    <Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
<!-- Center Aligned DataGridColumnHeader Style -->
<Style x:Key="CenterAlignedColumnHeaderStyle"
       TargetType="{x:Type WpfToolkit:DataGridColumnHeader}"
       BasedOn="{StaticResource ColumnHeaderStyle}">
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>

With these styles defined you be able to apply them to your column headers by changing their design. For this you will need to set the DataGridColumnHeader.HeaderStyle property. In your sample go ahead and set the Age and Deviation columns HeaderStyle to the RightAlignedColumnHeaderStyle and the Deviation Chart to CenterAlignedColumnHeaderStyle:

(…)
<WpfToolkit:DataGridTextColumn
    Header="Age" Width="1*"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    Binding="{Binding Path=Age}"/>
(…)

After changing the DataGridColumnHeaders you will have to specify styles for our cells so that they also get aligned. In you resources file create specific styles to align the content vertically on the center and horizontally on the right, another style to align it horizontally on center and another to align it on the left:

(…)
<!-- Left Aligned DataGridCell Style -->
<Style x:Key="LeftAlignedCellStyle" TargetType="{x:Type WpfToolkit:DataGridCell}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridCell}">
                <Grid Background="{TemplateBinding Background}">
                    <ContentPresenter HorizontalAlignment="Left"
                                      VerticalAlignment="Center"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
(…)

As done previously with the DataGridColumnHeaders, you will now have to assign each style to the corresponding DataGridColumn. Go ahead and set the PlayerName’s CellStyle to LeftAlignedCellStyle – since we do need to align its content vertically. Then you will have to set Age and Deviation CellStyles to RightAlignedColumnHeaderStyle. Enabled and DeviationChart will be center aligned so you will have to set the corresponding CellStyle on their DataGridColumns.

<WpfToolkit:DataGridCheckBoxColumn
    Header="Enabled" Width=".5*"
    CellStyle="{StaticResource CenterAlignedCellStyle}"
    Binding="{Binding Path=IsEnabled}"/>
<WpfToolkit:DataGridTextColumn
    Header="Player Name" Width="2*"
    CellStyle="{StaticResource LeftAlignedCellStyle}"
    Binding="{Binding Path=Name}"/>
<WpfToolkit:DataGridTextColumn
    Header="Age" Width="1*"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    CellStyle="{StaticResource RightAlignedCellStyle}"
    Binding="{Binding Path=Age}"/>
<WpfToolkit:DataGridTextColumn
    Header="Deviation" Width="1*"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    CellStyle="{StaticResource RightAlignedCellStyle}"
    Binding="{Binding Path=Deviation}"/>
<WpfToolkit:DataGridComboBoxColumn
    Header="Category" Width="1*"
    ItemsSource="{DynamicResource Categories}"
    SelectedValueBinding="{Binding Path=Category}"
    TextBinding="{Binding Path=Category}" />
<WpfToolkit:DataGridTextColumn
    Header="Deviation Chart" Width="1*"
    HeaderStyle="{StaticResource CenterAlignedColumnHeaderStyle}"
    CellStyle="{StaticResource CenterAlignedCellStyle}"
    Binding="{Binding Path=DeviationPercentage}"/>

After correctly aligning our cells this is how our DataGrid is looking:
WpfToolkitDataGrid-ss011

You can download a working sample of this code by following this link.

Aligning DataGridCells content by extending a DataGridColumn

A little background before moving on! Each cell can be in either two states: normal state or editing state. For each of these states we can specify the template to be used (by setting DataGridColumn.ElementTemplate and DataGridColumn.EditingElementTemplate) or apply to them a specific style (by setting DataGridColumn.ElementStyle and DataGridColumn.EditingElementStyle). CellStyles are applied to the DataGridCell itself, not the elements within it. This brings us to why our previous solution has some undesired side effects.

If you noticed when you select a cell for editing you see that the Textbox does not occupy the full content of the DataGridCell. The reason for this is that the new style we have defined for our cells applies the alignment to the TextBox within the cell and not to its content.

This is WPF – 10 different ways for doing the same thing. One solution to this, and a more clean solution than the previous one, is to extend the DataGridTextColumn giving it support for content alignment by adding a couple of properties that can be used to set the alignment on the generated elements for each cell. For this you will need to add new class – ExtendedTextBoxColumn – that extends DataGridTextColumn and add to it a couple of properties – HorizontalAlignment and VerticalAlignment. Afterwards you must override the GenerateElement and GenerateEditingElement methods in order to intercept the creation of the corresponding cell elements and set the values accordingly to the ones specified when the DataGridColumn was created.

protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
    var element = base.GenerateElement(cell, dataItem);

    element.HorizontalAlignment = HorizontalAlignment;
    element.VerticalAlignment = VerticalAlignment;

    return element;
}

protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
    var textBox = (TextBox)base.GenerateEditingElement(cell, dataItem);

    textBox.TextAlignment = GetTextAlignment();
    textBox.VerticalContentAlignment = VerticalAlignment;

    return textBox;
}

Please note that the GetTextAlignment method is just a helper method that maps a HorizontalAlignment to a TextAlignment. The only thing remaining is for you to replace the DataGridTextColumns by the newly created ExtendedTextColumn and remove its CellStyle:

<WpfToolkit:DataGridCheckBoxColumn
    Header="Enabled" Width=".5*"
    CellStyle="{StaticResource CenterAlignedCellStyle}"
    Binding="{Binding Path=IsEnabled}"/>
<Controls:ExtendedTextColumn
    Header="Player Name" Width="2*"
    HorizontalAlignment="Left" VerticalAlignment="Center"
    Binding="{Binding Path=Name}"/>
<Controls:ExtendedTextColumn
    Header="Age" Width="1*"
    HorizontalAlignment="Right" VerticalAlignment="Center"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    Binding="{Binding Path=Age}"/>
<Controls:ExtendedTextColumn
    Header="Deviation" Width="1*"
    HorizontalAlignment="Right" VerticalAlignment="Center"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    Binding="{Binding Path=Deviation}"/>
<WpfToolkit:DataGridComboBoxColumn
    Header="Category" Width="1*"
    ItemsSource="{DynamicResource Categories}"
    SelectedValueBinding="{Binding Path=Category}"
    TextBinding="{Binding Path=Category}" />
<Controls:ExtendedTextColumn
    Header="Deviation Chart" Width="1*"
    HorizontalAlignment="Center" VerticalAlignment="Center"
    HeaderStyle="{StaticResource CenterAlignedColumnHeaderStyle}"
    Binding="{Binding Path=DeviationPercentage}"/>

This is pretty basic stuff but replaces all of our styles when using text columns. It also solves the issue with the size of the Textbox for the EditingElement when in editing mode. On the left there is a screenshot of how a cell looked when in edit mode, on the right we have the cell being edited using ExtendedTextColumn:

WpfToolkitDataGrid-ss012

You can download a working sample of this code by following this link.

Enabling and disabling DataGridRows

Now let’s go and add some functionality to our DataGrid. The requirement is a simple one: whenever the user checks the CheckBox on the first column, the DataGridRow should be disabled, and he must not be able to edit the contents of any cell.

Although this is a simple requirement there is no direct way of doing it without creating a specific editing element style per column type. For each Player we have an IsEnabled field to which we bind our column in order to populate the CheckBoxes. We then have to bind each of the controls in the elements and disallow the editing on the cell based on the corresponding IsEnabled value.

To accomplish this, go ahead and add three new styles to the resources of your UserControl (these styles can be defined locally because they are specific and will probably make no sense outside this context), one for each type of control you use o your columns – TextBlock, TextBox and ComboBox.

<Style x:Key="BaseTextBlockCellStyle" TargetType="{x:Type TextBlock}">
    <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
</Style>
<Style x:Key="BaseTextBoxCellStyle" TargetType="{x:Type TextBox}">
    <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
</Style>
<Style x:Key="BaseComboBoxBoxCellStyle" TargetType="{x:Type ComboBox}">
    <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
</Style>

Having defined the styles, go through the DataGridColumn specifications and set their ElementStyle and EditingElementStyle to the corresponding resources:

(…)
<Controls:ExtendedTextColumn
    Header="Deviation" Width="1*"
    HorizontalAlignment="Right" VerticalAlignment="Center"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    ElementStyle="{StaticResource BaseTextBlockCellStyle}"
    EditingElementStyle="{StaticResource BaseTextBoxCellStyle}"
    Binding="{Binding Path=Deviation}"/>
<WpfToolkit:DataGridComboBoxColumn
    Header="Category" Width="1*"
    ItemsSource="{DynamicResource Categories}"
    SelectedValueBinding="{Binding Path=Category}"
    ElementStyle="{StaticResource BaseComboBoxBoxCellStyle}"
    EditingElementStyle="{StaticResource BaseComboBoxBoxCellStyle}"
    TextBinding="{Binding Path=Category}" />
(…)

Now when we run the sample this is what we see when we try editing a value which we have disabled for editing:

WpfToolkitDataGrid-ss013

As you can see we have unchecked player “John Mufin” and the player’s name edit box is disabled.

You can download a working sample of this code by following this link.

Creating the LabelTextBoxColumn

If you run the sample and uncheck a row, you will not notice changes in the row until you select a field to edit, even though the TextBlock IsEnabled property was set to false. We could create a new style for TextBlock and change its foreground based on its enabled state. I have chosen another approach. Let’s change the cells element to be a Label instead of a TextBlock since the Label has already support for enable. When we set IsEnabled to false on a Label it will display a gray foreground.

Go ahead and add a new class that extends our ExtendedTextBoxColumn and name it LabelTextBoxColumn.

public class LabelTextBoxColumn : ExtendedTextBoxColumn
{
    private void ApplyStyle(bool isEditing, bool defaultToElementStyle,
        FrameworkElement element)
    {
        var style = PickStyle(isEditing, defaultToElementStyle);
        if (style != null)
            element.Style = style;
    }

    private Style PickStyle(bool isEditing, bool defaultToElementStyle)
    {
        var style = isEditing ? EditingElementStyle : ElementStyle;
        if (isEditing && defaultToElementStyle && (style == null))
            style = ElementStyle;
        return style;
    }

    private void ApplyBinding(DependencyObject target,
        DependencyProperty property)
    {
        var binding = Binding;
        if (binding != null)
            BindingOperations.SetBinding(target, property, binding);
        else
            BindingOperations.ClearBinding(target, property);
    }

    protected override FrameworkElement GenerateElement(DataGridCell cell,
        object dataItem)
    {
        var label = new Label
                         {
     HorizontalAlignment = this.HorizontalAlignment,
     VerticalAlignment = this.VerticalAlignment
                         };

        ApplyStyle(false, false, label);
        ApplyBinding(label, ContentControl.ContentProperty);

        return label;
    }
}

This class is of very simple implementation. In fact we are only replacing the GenerateElement method with a custom implementation that creates a Label instead of a TextBlock. The ApplyStyle, PickStyle and ApplyBinding methods are all part of WPF Toolkit, I had to copy them to this sample since WPF Toolkit does not expose them publically – they are either private or internal and part of DataGridTextBoxColumn.

Since you now have a new type of column a new style must be added to bind the LabelTextBoxColumn Label element to the IsEnabled property of the current player. In fact just change the style you have for the TextBlock so that it applies to Labels:

<Style x:Key="BaseLabelCellStyle" TargetType="{x:Type Label}">
    <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
</Style>

Now you have to change your DataGrid to use this new LabelTextBoxColumn. Just replace all the ExtendedTextBoxColumn by the LabelTextBoxColumn. You will also have to replace the ElementStyle by the new BaseLabelCellStyle:

<Controls:LabelTextBoxColumn
    Header="Age" Width="1*"
    HorizontalAlignment="Right" VerticalAlignment="Center"
    HeaderStyle="{StaticResource RightAlignedColumnHeaderStyle}"
    ElementStyle="{StaticResource BaseLabelCellStyle}"
    EditingElementStyle="{StaticResource BaseTextBoxCellStyle}"
    Binding="{Binding Path=Age}"/>

After these changes when you uncheck the Enabled CheckBox the row will turn gray and you will have an obvious feedback that it is disabled:

WpfToolkitDataGrid-ss014

You can download a working sample of this code by following this link.

Creating an AutoCommitCheckBoxColumn

By default the DataGrid only allows you to check a CheckBox on a column when this column is in editing mode. Basically, in order for you to disable a row you will have to uncheck the CheckBox, move the focus out of the cell and only then will your change be committed.

What a user would expect of this column was for it to be effective on the first click. He is not expecting to have to click the CheckBox and only see the results of the commit after he clicks away from this control in order for it to lose focus. This is where the AutoCommitCheckBoxColumn comes into play.

We will be creating a type of column that is tightly bounded to the CheckBox it generates listening to its changes and committing them to the DataSource.

public class AutoCommitCheckBoxColumn : DataGridCheckBoxColumn
{
    private void checkBox_Unchecked(object sender, RoutedEventArgs e)
    {
        CommitCellEdit((FrameworkElement)sender);
    }

    private void checkBox_Checked(object sender, RoutedEventArgs e)
    {
        CommitCellEdit((FrameworkElement)sender);
    }

    protected override FrameworkElement GenerateEditingElement(
        DataGridCell cell, object dataItem)
    {
        var checkBox = (CheckBox)base.GenerateEditingElement(cell, dataItem);

        checkBox.Checked += checkBox_Checked;
        checkBox.Unchecked += checkBox_Unchecked;

        return checkBox;
    }
}

We just have to hook the Checked and Unchecked events and force a commit on the cell. Now whenever the user clicks the CheckBox he will have an instant response giving him a higher impression of feedback.

You can download a working sample of this code by following this link.

End of Part III

I hope you have enjoyed this new post on the WPF Toolkit DataGrid, it has been a pleasure writing it! In this part we have seen how to create custom DataGridColumns. Our main focus was on how to correctly display data and allow the user to interact with it. We have seen how to control each element rendered within a cell.

On part IV we will play with DataGridTemplateColumns and get our team grouping to work. Stay tuned Dear Reader!

Shout it kick it on DotNetKicks.com

May 15 09

WPF: Padding does not work on styled button

by Samuel Moura

This is one of those things that can easily fall through the cracks when creating a new ControlTemplate for a WPF Button.

The problem

Let’s suppose you have the following code to render an OK and a Cancel button on the bottom of a window:

<Button Content="_OK" Margin="0,0,8,0" Padding="8,2"
        HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button Content="_Cancel" Padding="8,2"
        HorizontalAlignment="Center" VerticalAlignment="Center" />
A nice padding was added to add some space between the content text and the outer borders of the button. This is how these buttons look:
WpfTips0004
Now you want to add a new ControlTemplate to your Button style and create a ContentPresenter within that template to accommodate the content the user of your style will provide, similar to what is achieved in this reference page at MSDN:
(…)
<ControlTemplate TargetType="Button">
    <Border x:Name="Border"
            CornerRadius="2" BorderThickness="1"
            Background="{StaticResource NormalBrush}"
            BorderBrush="{StaticResource NormalBorderBrush}">
        <ContentPresenter Margin="2"
                          HorizontalAlignment="Center"
                          VerticalAlignment="Center"
                          RecognizesAccessKey="True"/>
(…)

After applying this new style and running your application the subsequent picture shows how the buttons would look.

WpfTips0005

As you can see the nice space we had specified for our buttons with Padding is not visible anymore and the text almost touches the borders of our buttons. Basically the padding you are adding on your Button’s xaml is being ignored by the new style.

The solution

The solution to this problem is a very simple one. You just have to ensure that you bind your ContentPresenter’s margin to the TemplatedParent Padding property, as follows:

(…)
<ControlTemplate TargetType="Button">
    <Border x:Name="Border"
            CornerRadius="2" BorderThickness="1"
            Background="{StaticResource NormalBrush}"
            BorderBrush="{StaticResource NormalBorderBrush}">
        <ContentPresenter Margin="{TemplateBinding Padding}"
                          HorizontalAlignment="Center"
                          VerticalAlignment="Center"
                          RecognizesAccessKey="True"/>
(…)

This way a Padding will be applied when the Button’s content gets rendered. After this change this is how our would buttons look:

WpfTips0006

As you can see padding is now accounted for and our Buttons look much cooler!

Shout it kick it on DotNetKicks.com

Apr 27 09

WPF Toolkit DataGrid, Part II – Custom styling

by Samuel Moura

This post is part of the WPF Toolkit DataGrid series. Here is a list with the complete set of blog posts:

Introduction

In Part I we went through basic DataGrid setup creating a sample application that showcased the usage of DataGridColumns. In this part we will dive into more advanced concepts. It is my ambition that you get a good understanding of how to setup a DataGrid to do whatever you wish without restrictions. In this part we will focus on restyling the DataGrid, DataGridColumnHeaders and DataGridRowHeaders.

Roadmap

  1. Visual breakdown of styles and layout
  2. Styling the DataGrid
  3. Styling DataGridColumnHeaders
  4. Styling DataGridRowHeaders
  5. Creating a new template for the SelectAllButton

Download sample source code

Here is a list with the samples presented on this blog post:

  1. WPF Toolkit DataGrid Sample 03 – Styling the DataGrid
  2. WPF Toolkit DataGrid Sample 04 – Styling DataGridColumnHeaders
  3. WPF Toolkit DataGrid Sample 05 – Styling DataGridRowHeaders
  4. WPF Toolkit DataGrid Sample 06 – Styling the SelectAllRows button

Visual breakdown of layout and styles

Before we start digging dipper into DataGrid’s functionalities and restyling let’s take a look on what WPF Toolkit uses to build it. It is essential that you understand what each class is responsible for in order to find which styles you need to replace so that you can change the looks of a specific region of the DataGrid.

First let’s look at the DataGrid from a layout perspective. Along with the DataGrid this are the main components used to display the information inside of it:

WpfToolkitDataGrid-ss003

These classes are the ones used by the DataGrid to render itself. They are the objects that end up on the application’s visual tree:

  • DataGridColumnHeadersPresenter: this control will go through the DataGridColumns collection of the DataGrid and create a DataGridColumnHeader for each;
  • DataGridColumnHeader: picks up the information in its DataGridColumn and renders it as a header on the corresponding column position;
  • DataGridRowsPresenter: the ItemsControl that iterates the ItemsSource creating a DataGridRow for each item;
  • DataGridRow: represents an item of ItemsSource;
  • DataGridRowHeader: this is a Button that lays on the beginning of each DataGridRow. It allows the user to select the row, resize it, and can be used to show information – for instance, on the current operation being performed over the selected row or still to provide error information;
  • DataGridCellsPresenter: this is the ItemsControl used to render each cell, based on the column bindings, for the DataGridRow bounded item;
  • DataGridCell: this is the control used to render a specific value of an Item. Essentially it is made of two templates, one to display the item and one for editing it;
  • DataGridDetailsPresenter: this is an extension of the DataGridRow used to show further information about a row than the one displayed by the cells. It is a panel that is normally laid bellow the cells. On WPF Toolkit’s DataGrid, by default, this panel is only shown when the user selects a row.

On the following image you can see where each of this controls are laid. Take some time to get to know what component is responsible for what part of the DataGrid.

    WpfToolkitDataGrid-ss005a

    Starting from the top, in the DataGrid, we will have a DataGridColumnHeadersPresenter used to present all DataGridColumnHeaders, there will be one header per bounded column. Each column header has two grippers. Inside the DataGrid along with the DataGridColumnHeadersPresenter there will be a DataGridRowsPresenter which will contain all our rows. The DataGridRow will contain a DataGridRowHeader and a DataGridCellsPresenter which contains all the DataGridCells. Also part of the DataGridRow, although not shown in the screenshot, is the DataGridDetailsPresenter, which can be used to present further details on the selected row.

    If you use Snoop to browse the visual tree of our sample from Part I you will see the following (I have marked in red the items you should pay more attention to):

    WpfToolkitDataGrid-ss005b

Side note: Snoop is a great tool for analyzing how a WPF application is structured. If you do not have this tool on you arsenal, stop reading, go to their web site, download it and install it. This is a must have if you are developing WPF applications.

You can see each of the classes we looked at above in the visual tree of our WPF sample application. Take some time to assimilate the structure used before proceeding.

By now you should have a solid understanding about the structure of the DataGrid. You should be able to easily spot each component along with which class is responsible for rendering it. This is the foundation for our next job – restyling the WPF Toolkit’s DataGrid.

Styling the DataGrid

Since the DataGrid is divided in several parts and each has individual styles, our ControlTemplate for it will not change that much, most of it will be based on the one that comes with WPF Toolkit.

The DataGrid style has five major components:

  • Select all button: this is the button on the top left corner of the DataGrid that is used to select all rows;
  • PART_ColumnHeadersPresenter: this is the ItemsControl used to present all of our DataGridColumnHeaders;
  • PART_ScrollContentPresenter: the main area of the DataGrid where all the DataGridRows will be rendered;
  • PART_VerticalScrollBar: renders the vertical scroll bar of our ScrollViewer;
  • PART_HorizontalScrollBar: renders the horizontal scroll bar of our ScrollViewer.

I have taken a screenshot from Snoop in order to show you how these parts fit together on visual tree:

WpfToolkitDataGrid-ss007
In the last sample we worked with on Part I, you may have notice that the vertical scroll bar does not occupy the full height of the DataGrid. In our new style, we will make it fill that empty space. This is just a minor tweak in the default DataGrid ControlTemplate.

Since you may want to reuse this new style just add a new resource dictionary to your solution where you can place the new DataGrid style – in the sample code I have named it ‘DataGrid.Generic.xaml’ and placed it under the Themes folder.

The change we are planning to make is a small one, so we can reuse the style defined within WPF Toolkit, you just have to copy the DataGrid style from the ‘Generic.xaml’ file on WPF Toolkit and add it to your new dictionary. Find the definition of the vertical scroll bar in the ControlTemplate part of the style. Change Grid.Row to 0 and give it a Grid.RowSpan of 2, as shown below:

(…)
<ScrollBar Name="PART_VerticalScrollBar"
           Grid.Row="0" Grid.RowSpan="2" Grid.Column="2" Orientation="Vertical"
           Maximum="{TemplateBinding ScrollableHeight}"
           ViewportSize="{TemplateBinding ViewportHeight}"
           Value="{Binding Path=VerticalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
           Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
(…)

Along with extending the scroll bar you can also style your DataGrid by setting some nice colors. To give your DataGrid a cooler look you can start by creating a series of brushes:

<!-- Generic brushes -->
<SolidColorBrush x:Key="DefaultControlBorderBrush" Color="#FF688CAF"/>
<SolidColorBrush x:Key="DefaultControlBackgroundBrush" Color="#FFE3F1FE"/>
<SolidColorBrush x:Key="DefaultControlForegroundBrush" Color="#FF10257F"/>

<!-- DataGrid related brushes -->
<SolidColorBrush x:Key="GridLineColorBrush" Color="#FFCEDFF6" />
<LinearGradientBrush x:Key="AlternateRowBackgroundBrush"
                     StartPoint="0.5,0.0" EndPoint="0.5,1.0">
    <GradientStop Color="#FFFEFEFF" Offset="0"/>
    <GradientStop Color="#FFE4F0FC" Offset="1"/>
</LinearGradientBrush>

These brushes will then be used to set the following properties on the style:

(…)
<Setter Property="Background"
        Value="{StaticResource DefaultControlBackgroundBrush}"/>
<Setter Property="Foreground"
        Value="{StaticResource DefaultControlForegroundBrush}"/>
<Setter Property="BorderBrush"
        Value="{StaticResource DefaultControlBorderBrush}" />
<Setter Property="HorizontalGridLinesBrush"
        Value="{StaticResource GridLineColorBrush}" />
<Setter Property="VerticalGridLinesBrush"
        Value="{StaticResource GridLineColorBrush}" />
<Setter Property="AlternatingRowBackground"
        Value="{StaticResource AlternateRowBackgroundBrush}" />
<Setter Property="BorderThickness" Value="1" />
(…)

In the sample you will find all the style for the DataGrid. When you go through its ControlTemplate you will see most of the items described in ‘Visual breakdown of layout and styles’. This is how our DataGrid currently looks with the new style:

WpfToolkitDataGrid-ss006

You can download a working sample by following this link.

Styling DataGridColumnHeaders

The next step will be to create custom styles for our DataGridColumnHeaders. In the class diagram above you can see that DataGridColumnHeaders extend ButtonBase, so when we style a DataGridColumnHeader we are actually creating a style for a button. We also need to keep in mind the two styles for the grippers – one for each side of the DataGridColumnHeader. The grippers will be used to resize the DataGridColumnHeader, thus they need to exist as controls the user can interact with!

Let’s start by creating the style for the grippers, both left and right, since they will be referenced as static resources by the DataGridColumnHeader style.

<!-- DataGridColumnHeader Right Gripper Style -->
<Style x:Key="ColumnHeaderRightGripperStyle" TargetType="{x:Type Thumb}">
    <Setter Property="Width" Value="8"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Cursor" Value="SizeWE"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Thumb}">
                <Border Padding="{TemplateBinding Padding}"
                        Background="{TemplateBinding Background}">
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<!-- DataGridColumnHeader Left Gripper Style -->
<Style x:Key="ColumnHeaderLeftGripperStyle"
       BasedOn="{StaticResource ColumnHeaderRightGripperStyle}"
       TargetType="{x:Type Thumb}">
    <Setter Property="Width" Value="8"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Cursor" Value="SizeWE"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Thumb}">
                <Border Name="Border"
                        Padding="{TemplateBinding Padding}"
                        Background="{TemplateBinding Background}">
                    <Canvas>
                        <Line RenderOptions.EdgeMode="Aliased" Stroke="#88B0E4"
                              X1="7" Y1="{Binding ElementName=Border, Path=ActualHeight}"
                              X2="7" Y2="0"/>
                    </Canvas>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

As you may have noticed we are drawing a line on the left gripper but not drawing it on the right gripper. If we were to draw lines on both sides the end result would be a thicker line. Instead, on the right gripper, we just reserve some space so that the user will have somewhere to click and drag to resize the column.

Now that you have created the grippers you can go ahead and create the style for the DataGridColumnHeader. In this style we will be adding a nice blue gradient background and a highlight gradient that gets displayed when the user put his mouse over a column header:

<!-- DataGridColumnHeader Style -->
<Style x:Key="ColumnHeaderStyle" TargetType="{x:Type WpfToolkit:DataGridColumnHeader}">
    <Setter Property="VerticalContentAlignment" Value="Center" />
    <Setter Property="Background" Value="{StaticResource HeaderBackgroundBrush}"/>
    <Setter Property="BorderBrush" Value="{StaticResource HeaderBorderBrush}" />
    <Setter Property="BorderThickness" Value="0,1,0,1" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridColumnHeader}">
                <Grid>
                    <WpfToolkit:DataGridHeaderBorder
                        x:Name="headerBorder"
                        SortDirection="{TemplateBinding SortDirection}"
                        IsHovered="{TemplateBinding IsMouseOver}"
                        IsPressed="{TemplateBinding IsPressed}"
                        IsClickable="{TemplateBinding CanUserSort}"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        Padding ="{TemplateBinding Padding}"
                        SeparatorVisibility="{TemplateBinding SeparatorVisibility}"
                        SeparatorBrush="{TemplateBinding SeparatorBrush}">
                        <Border BorderBrush="{StaticResource HeaderInnerBorderBrush}"
                                BorderThickness="0,1,0,0">
                            <TextBlock
                                Text="{Binding}" Margin="4,0,4,0"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />
                        </Border>
                    </WpfToolkit:DataGridHeaderBorder>

                    <Thumb x:Name="PART_LeftHeaderGripper"
                           HorizontalAlignment="Left"
                           Style="{StaticResource ColumnHeaderRightGripperStyle}"/>
                    <Thumb x:Name="PART_RightHeaderGripper"
                           HorizontalAlignment="Right"
                           Style="{StaticResource ColumnHeaderLeftGripperStyle}"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="headerBorder" Property="Background"
                                Value="{StaticResource HeaderHighlightedBackgoundBrush}" />
                    </Trigger>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter TargetName="headerBorder" Property="Background"
                                Value="{StaticResource HeaderPressedBackgroundBrush}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Go ahead now and create a ‘Brushes.xaml’ beside the ‘DataGrid.Generic.xaml’ resource dictionary. In here you will define all of the brushes used by the styles in the DataGrid. This will make it easier for you to reuse them on the remaining controls of your application.

<!-- Header related brushes -->
<SolidColorBrush x:Key="HeaderBorderBrush" Color="#88B0E4"/>
<SolidColorBrush x:Key="HeaderInnerBorderBrush" Color="#FFFFFF" />
<LinearGradientBrush x:Key="HeaderBackgroundBrush"
                     StartPoint="0.5,0.0" EndPoint="0.5,1.0">
    <GradientStop Color="#FF98BFEB" Offset="0"/>
    <GradientStop Color="#FFB8D4F2" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HeaderHighlightedBackgoundBrush"
                     StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFA3CBF7" Offset="0"/>
    <GradientStop Color="#FFD9E6F9" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HeaderPressedBackgroundBrush"
                     StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFA3CBF7" Offset="1"/>
    <GradientStop Color="#FFD9E6F9" Offset="0"/>
</LinearGradientBrush>

In order for you to see the outcome of these new styles you will have to alter the DataGrid style by adding it a setter for the ColumnHeaderStyle property:

<Setter Property="ColumnHeaderStyle" Value="{StaticResource ColumnHeaderStyle}"/>

In the following picture you can see how our DataGrid is actually looking after restyling the DataGridColumnHeaders:

WpfToolkitDataGrid-ss008Here are our fancy looking DataGridColumnHeaders. Notice that the ‘Player Name’ column was purposely highlighted when the screenshot was taken.

You can download a working sample of the code presented here by following this link.

Styling DataGridRowHeaders

DataGridRowHeaders pretty much follow the DataGridColumnHeaders in the way they are implemented. So you have two grippers, one on top and another on bottom, and a style for header itself. In this case we will use the same style for both the grippers just to lay the required space for the user to interact with. The visual separator for the columns will be the control template’s border.

<!-- DataGridRowHeader Gripper -->
<Style x:Key="RowHeaderGripperStyle" TargetType="{x:Type Thumb}">
    <Setter Property="Height" Value="8"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Cursor" Value="SizeNS"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Thumb}">
                <Border Padding="{TemplateBinding Padding}"
                        Background="{TemplateBinding Background}"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Now you can go ahead and include the style for the DataGridRowHeader. For this you will use most of the code from the DataGridColumnHeader and the corresponding brushes. We want to keep the look and feel of both header types consistent.

<!-- DataGridRowHeader Style -->
<Style x:Key="{x:Type WpfToolkit:DataGridRowHeader}"
       TargetType="{x:Type WpfToolkit:DataGridRowHeader}">
    <Setter Property="Background" Value="{StaticResource HeaderBackgroundBrush}" />
    <Setter Property="BorderBrush" Value="{StaticResource HeaderBorderBrush}" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridRowHeader}">
                <Grid>
                    <WpfToolkit:DataGridHeaderBorder
                        x:Name="headerBorder"
                        IsSelected="{TemplateBinding IsRowSelected}"
                        IsHovered ="{TemplateBinding IsMouseOver}"
                        IsPressed="{TemplateBinding IsPressed}"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="1,0,1,1"
                        Padding ="{TemplateBinding Padding}"
                        Orientation="Horizontal"
                        SeparatorVisibility="{TemplateBinding SeparatorVisibility}"
                        SeparatorBrush="{TemplateBinding SeparatorBrush}">
                        <Border BorderBrush="{StaticResource HeaderInnerBorderBrush}"
                                BorderThickness="0,1,0,0">
                            <StackPanel Orientation="Horizontal">
                                <ContentPresenter
                                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                    VerticalAlignment="Center"/>
                                <Control
                                    SnapsToDevicePixels="false"
                                    Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGridRow}}, Path=(Validation.HasError), Converter={StaticResource bool2VisibilityConverter}}"
                                    Template="{Binding RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGridRow}}, Path=ValidationErrorTemplate}" />
                            </StackPanel>
                        </Border>
                    </WpfToolkit:DataGridHeaderBorder>

                    <Thumb x:Name="PART_TopHeaderGripper"
                           VerticalAlignment="Top"
                           Style="{StaticResource RowHeaderGripperStyle}"/>
                    <Thumb x:Name="PART_BottomHeaderGripper"
                           VerticalAlignment="Bottom"
                           Style="{StaticResource RowHeaderGripperStyle}"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="headerBorder" Property="Background"
                                Value="{StaticResource HeaderHighlightedBackgoundBrush}" />
                    </Trigger>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter TargetName="headerBorder" Property="Background"
                                Value="{StaticResource HeaderPressedBackgroundBrush}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

As you can see this style is pretty much the same as the one for the DataGridColumnHeader. After these changes this is how our application is looking:

WpfToolkitDataGrid-ss009

It is looking pretty cool! To download the sample for this part of the tutorial just follow this link.

Creating a new template for the SelectAllButton

The only thing that is currently killing the look of our DataGrid is that top left corner button. This button is used to select all the rows on the DataGrid and its appearance can be altered by setting it on the DataGrid’s style. First you have to create the style for the button:

<!-- SelectAllButton ControlTemplate -->
<ControlTemplate x:Key="SelectAllButtonTemplate" TargetType="{x:Type Button}">
    <Grid>
        <Rectangle x:Name="Border" SnapsToDevicePixels="True"
                   Stroke="{StaticResource HeaderBorderBrush}"
                   Fill="{StaticResource HeaderBackgroundBrush}" />
        <Border SnapsToDevicePixels="True" Margin="1,1,1,0"
                BorderBrush="White" BorderThickness="0,1,0,0" />
        <Polygon x:Name="Arrow"
                 HorizontalAlignment="Right"
                 VerticalAlignment="Bottom"
                 Margin="8,8,3,3"
                 Opacity="0.15"
                 Fill="Black"
                 Stretch="Uniform"
                 Points="0,10 10,10 10,0" />
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter TargetName="Border" Property="Fill"
                    Value="{StaticResource HeaderHighlightedBackgoundBrush}" />
        </Trigger>
        <Trigger Property="IsPressed" Value="True">
            <Setter TargetName="Border" Property="Fill"
                    Value="{StaticResource HeaderPressedBackgroundBrush}" />
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter TargetName="Arrow" Property="Visibility" Value="Collapsed" />
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

The only thing remaining now is to set this template on the corresponding button on the DataGrid style:

<!--Left Column Header Corner -->
(...)
<Button
    Command="{x:Static WpfToolkit:DataGrid.SelectAllCommand}"
    Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGrid}}, Path=CellsPanelHorizontalOffset}"
    Focusable="false"
    Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGrid}}, Path=HeadersVisibility, Converter={x:Static WpfToolkit:DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static WpfToolkit:DataGridHeadersVisibility.All}}"
    Template="{StaticResource SelectAllButtonTemplate}"/>
(...)

Congrats! You have now restyled all of the relevant components of the DataGrid. This is the final screenshot of the sample developed on this blog post:

WpfToolkitDataGrid-ss010
The final sample for this blog post can be downloaded by following this link.

End of Part II

In this part you have seen how to style the look of the DataGrid. You got to know a bit more about the structure used by WPF Toolkit to render its DataGrid and its data.

Don’t miss the next part of this series. We will look into more advanced stuff like styling cells, columns and creating custom DataGridColumns.

Shout it