OK, the title of this blog post is not very snappy, but it is not an easy problem to describe in a few short words. Here’s the rub, the WPF DataGrid has a select-all button located in the top-left corner, just like Excel and many other grid controls / applications. However, with the default style, this button is barely visible and I would not be surprised if a user of the grid failed to see it.
Unfortunately restyling this button is not all that straightforward. The more complex WPF controls like the DataGrid and ListView typically expose the template used to construct, and the style applied to their various visual elements. However, the DataGrid does not expose a style or template for the select-all button.
Fortunately, all is not lost. With WPF, if a control does not expose a style for one of its component parts, you can replicate and replace its entire template. Sometimes this approach feels a little heavy-handed … “I can’t seem to find a way to fit those flashy alloy wheels to my car, so I’ll just by a new car that already has the wheels I like”.
I want to explore a more lightweight approach.
Whilst it is usually preferable to modify a controls template from XAML, in cases like this, I prefer the more concise approach of modifying them in code-behind. In order to do this, you need to handle the FrameworkElement.Loaded event on the control whos template you wish to modify. This event is fired after the control’s visual tree has been constructed, to verify this add a breakpoint in the event handler and inspect the visual tree with Mole. The image below shows the visual tree of my DataGrid which I have expanded to locate the select-all button.
You can see that the button is the first element, four steps down the visual tree, therefore it should be easy to locate by walking the visual tree. Once it has been reached we can simply replace its template to the one which we desire:
private void Grid_Loaded(object sender, RoutedEventArgs e) { DependencyObject dep = sender as DependencyObject; // Navigate down the visual tree to the button while (!(dep is Button)) { dep = VisualTreeHelper.GetChild(dep, 0); } Button button = dep as Button; // apply our new template object res = FindResource("SelectAllButtonTemplate"); button.Template = res as ControlTemplate } |
The template SelectAllButtonTemplate is simply defined as a resource of our Window.
This works just fine for our single grid, but what if we want to use the same trick on multiple DataGrids? (Besides, we have code-behind, which in WPF is a bit of a dirty word!).
The Attached Behaviour pattern is a useful WPF pattern that proves very useful in this situation. In brief, attached properties add state to a your WPF elements, whereas attached behaviours add behaviour. When an attached property becomes associated with a dependency object, an event is raised. We can handle this event and register handlers on the events of the object being attached to, in this case, our DataGrid’s Loaded event.
The following code is our attached behaviour in its entirety:
public static class DataGridStyleBehaviour { #region attached property public static ControlTemplate GetSelectAllButtonTemplate(DataGrid obj) { return (ControlTemplate)obj.GetValue(SelectAllButtonTemplateProperty); } public static void SetSelectAllButtonTemplate(DataGrid obj, ControlTemplate value) { obj.SetValue(SelectAllButtonTemplateProperty, value); } public static readonly DependencyProperty SelectAllButtonTemplateProperty = DependencyProperty.RegisterAttached("SelectAllButtonTemplate", typeof(ControlTemplate), typeof(DataGridStyleBehaviour), new UIPropertyMetadata(null, OnSelectAllButtonTemplateChanged)); #endregion #region property behaviour // property change event handler for SelectAllButtonTemplate private static void OnSelectAllButtonTemplateChanged( DependencyObject depObj, DependencyPropertyChangedEventArgs e) { DataGrid grid = depObj as DataGrid; if (grid == null) return; // handle the grid's Loaded event grid.Loaded += new RoutedEventHandler(Grid_Loaded); } // Handles the DataGrid's Loaded event private static void Grid_Loaded(object sender, RoutedEventArgs e) { DataGrid grid = sender as DataGrid; DependencyObject dep = grid; // Navigate down the visual tree to the button while (!(dep is Button)) { dep = VisualTreeHelper.GetChild(dep, 0); } Button button = dep as Button; // apply our new template ControlTemplate template = GetSelectAllButtonTemplate(grid); button.Template = template; } #endregion } |
This attached property can be used as illustrated below, where we apply the SelectAllButtonTemplate from our Window’s resources to a DataGrid:
<Window ... Title="BBC News RSS" Height="300" Width="400"> <Window.Resources> <XmlDataProvider x:Key="NewsFeed" Source="http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml" XPath="//item" /> <ControlTemplate x:Key="SelectAllButtonTemplate" TargetType="{x:Type Button}"> <Grid> <Rectangle x:Name="Border" Fill="Pink" SnapsToDevicePixels="True" /> <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> </Window.Resources> <Grid> <dg:DataGrid Name="dataGrid" AutoGenerateColumns="False" local:DataGridStyleBehaviour.SelectAllButtonTemplate="{StaticResource SelectAllButtonTemplate}" ItemsSource="{Binding Source={StaticResource NewsFeed}}" RowHeaderWidth="25"> <dg:DataGrid.Columns> <dg:DataGridTextColumn Header="Title" Binding="{Binding XPath=title}" Width="*"/> <dg:DataGridHyperlinkColumn Header="Url" Binding="{Binding XPath=link}" Width="*"/> <dg:DataGridTextColumn Header="Date" Binding="{Binding XPath=pubDate}" Width="*"/> </dg:DataGrid.Columns> </dg:DataGrid> </Grid> </Window> |
The result is a beautiful pink select-all button that I don’t think any user would miss in a hurry:
And a clean code-behind file for our Window
You can download the demo project for this blog post in the following zip file, wpfstylinghiddenelements.
One last thing … if you are implementing a WPF / Silverlight control, please expose the styles and templates for all component parts of your control!
Regards, Colin E.


email: ceberhardt@scottlogic.co.uk
on Google+






very useful..if not for this post , many people would have wasted hours of time just to change the appearance of it…
Thank you! I have been struggling with this for last couple of days. They key part i was missing is the Loaded Event subscription then find the Template Element. Thanks again!
This was the missing link in completing my grid look and I was suprised at how little info there is on the net. Elegantly done. So glad I stumbled on this big thanks!
I’ve updated the code a little to account for some blind spots. It seems that if the items collection is empty the previous code will first get an error when it tries to call GetChild on an element with no children. I fixed that with a quick if statement, but another issue was that the Loaded event doesn’t seem to fire when items finally are added. And apparently the button were trying to template doesn’t exist if there are no items in the items collection. So I fixed the code to handle the CLR LayoutUpdated event instead which does seem to fire off when items are finally added, and when they are initially there. I also made sure the button we are trying to template has its command property set to the selectall command. I also removed the handler once we’ve applied the template. Thanks Colin for the great post!
Here’s the updated version:
private static void OnSelectAllButtonTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid dataGrid = d as DataGrid;
if (dataGrid == null)
{
return;
}
EventHandler handler = null;
handler = delegate
{
DependencyObject dep = dataGrid;
while (dep != null && VisualTreeHelper.GetChildrenCount(dep) != 0
&& !(dep is Button && ((Button)dep).Command == DataGrid.SelectAllCommand))
{
dep = VisualTreeHelper.GetChild(dep, 0);
}
Button button = dep as Button;
if (button != null)
{
ControlTemplate template = GetSelectAllButtonTemplate(dataGrid);
button.Template = template;
dataGrid.LayoutUpdated -= handler;
}
};
dataGrid.LayoutUpdated += handler;
}
Thanks for the update
Hi Colin,
I used this implementation to walk the visual tree of a Templated element in the loaded event, but i have one doubt. The loaded event gets fired several times during the lifetime of the application, not only in the initialization phase, so that code is repeated several times, right? Removing the handler to the loaded event would be a good solution to this (because after the init. we wouldn’t need to template the button)?
Hi Jay,
Yes, Loaded gets fired quiet often – good idea about removing the event handler after the style has been applied.
Thanks, Colin E.
Hi Colin,
I fixed my problem, needed to remove the command. I added an answer to a StackOverflow question I asked here:
http://stackoverflow.com/questions/1493491/wpf-datagrid-select-all-button-unselect-all-too
Might be of use to readers of your blog.
Cheers,
Will
Will,
Nice solution … thanks for posting it.
Regards, Colin E.
Hi Colin,
Great article, I have struggled to find such information. I am trying to go one stage further than this and (at a client’s request) also have the select all button “unselect all”! To do this I have used your DataGridStyleBehavior class and have added a routedeventhandler to the button in your Grid_Loaded() method as follows:
…
button.Template = template;
button.Click += new RoutedEventHandler(SelectAllClicked);
This works fine and my SelectAllClicked method is being called when I click the button, but when my method finishes the rows are all still selected. I believe the original handler of the button is being run after mine. Could you suggest how I can fix this, or perhaps add a section in this post for how to add additional behaviour to the button to achieve what I am trying?
Very helpful post anyway, thanks again.
Will
Thanks for the sample, Colin! I was just struggling with this exact issue
Thanks Philipp, I have visited your blog a few times – you have some great posts there. Keep it up
I think the approach seems very fragile. You’re relying upon the position of the button within the visual tree (that it must be the 1st visual child). I would recommand changing this to something more robust – e.g. walk the entire tree (or even just the datagrid and its scrollviewer) looking for a button where the command property is set to the SelectAll command so you can be sure you have the right command. Its also possible that this could be broken in a future version should ms provide more than 1 template – currently I think they just define one in generic – and the os theme is changed during the running of the application.
Hi Andrew,
I disagree with you when you say that the approach seems very fragile, the approach being to walk the visual tree searching for the element to style via an attached behaviour. Rather, it is my simple implementation which is fragile – and here I would totally agree with you. I had considered using a more robust method for locating the SelectAll button, however I did not want to make that the primary focus of the article.
Thanks for your feedback.
Regards, Colin E.