One of my reader "Dan" asked me to write an article on "Group Row Header customization of Silverlight DataGrid" and here is that article for you. If you want to do UI customization of Row Group Header, this article will definitely help you. Here, I will show you how to modify the XAML to add different content to create a multi level row group header too.

Background

Earlier, I was looking for some solution to create a multilevel Group Row Header in a Silverlight DataGrid and found some good points over various forums including Tim's Blog. I explored them more and found that, yes, we can customize the template to create a multi level group header (but it is limited to only two levels, means if you add more levels of header the rest of the group header will look as same as the second header). I thought to write an article explaining all these so that, you can understand it properly. But due to lack of time, I was unable to complete it.

By that time, one of reader "Dan" asked me to help him to do the same. I thought to complete the article so that, other people will also get help from it in future.

Here is a screenshot of which we want to achieve by reading this article:

Here I will show you the customization in detail. Go through it and if you need any specific help on this, let me know. Due to the XAML code will not fit here properly, I will show only that part which is require for you to understand. But you can easily download the whole source code from the download section.

Begin with the Template

Let start with the existing application that we created earlier in the previous three articles. In this article, we will edit the template to show different group header (two levels). To start working with it, you need the default DataGridGroupRowHeader style. You can find the default style for it from MSDN and here is the link. The style should by apply to the DataGrid's RowGroupHeaderStyles. Just copy it in your XAML page as shown below:

If you check the edited style, you will notice that there is a Rectangle called "IndentSpacer". This will do the actual trick for you. IndentSpacer is used to indent subgroups. It's width specifies the amount that the immediate children of the DataGridRowGroupHeader are indented. The default value of it is 20.

See the above code to find the actual position of the IndentSpacer inside the Template.

Converter to switch between two group header

In this article, we are going to see how we can customize the template to create two different group row header while grouped with two different level. So, let us create a Converter for it. The converter checks the width of the IndentSpacer and based on the value, it will make the first or second group header visible or collapse.

For our example, if the width of that rectangle is zero and the converter parameter value specifies false, it will return Visible. Same thing is applicable if the width is not zero and the converter paramter specifies true value. In other case, it will always return Collapsed state. We will discuss more on it later while using the converter in the XAML. This will help you to understand the concept in depth.

Here is the converter for your reference:

publicclass GroupRowHeaderVisibilityConverter : IValueConverter

{

publicobject Convert(objectvalue, Type targetType,

object parameter, CultureInfo culture)

{

double width = System.Convert.ToDouble(value);

bool parameterState = parameter != null &&

bool.Parse(parameter.ToString());

if ((width == 0 && parameterState == false) ||

(width != 0 && parameterState))

{

return Visibility.Visible;

}

if ((width == 0 && parameterState) ||

(width != 0 && parameterState == false))

{

return Visibility.Collapsed;

}

return Visibility.Collapsed;

}

publicobject ConvertBack(objectvalue, Type targetType,

object parameter, CultureInfo culture)

{

thrownew NotImplementedException();

}

}

Now add the Converter reference in your XAML page. To do this, add the xmlns namespace in the xaml page and then add the converter as the resource. Have a look into the following snapshot:

Editing the Template to create Multi row group header

It's time to create our row group header. The default template has the following XAML code for the Row Group Header style:

<StackPanelGrid.Column="3"Grid.Row="1"Orientation="Horizontal"

VerticalAlignment="Center"Margin="0,1,0,1">

<TextBlockx:Name="PropertyNameElement"Margin="4,0,0,0"

Visibility="{TemplateBinding PropertyNameVisibility}"/>

<TextBlockMargin="4,0,0,0"Text="{Binding Name}"/>

<TextBlockx:Name="ItemCountElement"Margin="4,0,0,0"

Visibility="{TemplateBinding ItemCountVisibility}"/>

</StackPanel>

Let's modify it and add the following code:

<!-- This is START of the First Group Header of the DataGrid -->

<StackPanelGrid.Column="3"Grid.Row="1"Orientation="Horizontal"

VerticalAlignment="Center"Margin="0,1,0,1"

Visibility="{Binding Width,

Converter={StaticResource GroupRowHeaderVisibilityConverter},

ElementName=IndentSpacer}">

<TextBlockMargin="4,0,0,0"Text="{Binding Name}"Foreground="Red"/>

</StackPanel>

<!-- This is END of the First Group Header of the DataGrid -->

<!-- This is START of the Second Group Header of the DataGrid -->

<GridGrid.Column="3"Grid.Row="1"VerticalAlignment="Center"

HorizontalAlignment="Stretch"Margin="0,1,0,1"

Visibility="{Binding Width, ConverterParameter=true,

Converter={StaticResource GroupRowHeaderVisibilityConverter},

ElementName=IndentSpacer}">

<StackPanelOrientation="Horizontal"HorizontalAlignment="Left">

<TextBlockMargin="4,0,0,0"Text="{Binding Name}"Foreground="Blue"/>

</StackPanel>

</Grid>

<!-- This is END of Second Group Header of the DataGrid -->

Here, the first stackpanel is your first level group header where the second stackpanel inside the grid is your second level, third level... group headers. Both has visibility property binded to the Width of the IndentSpacer with the converter that we created earlier.

So, if the width is zero and converter paramter is set to false (default value), it will show the first stackpanel i.e. your first level row group header. The second stackpanel will collapse because we are passing Converter parameter as "true".

In reverse case, the first stackpanel will collapse and the second stackpanel will get the visibility. Thus shows the next level row group headers.

I just added some default values there with different foreground color set to the text of the header. You can now customize it easily if you understand the logic behind it.

Playing with code to do multi grouping

As our XAML is ready, it's time to do some coding in C# to group the records at the time of loading. First, we will go to the ViewModel and comment out the following line marked inside the snapshot:

Remember: this comment out is only for this sample to work easily. The issue here will be, if you group it from the dropdown again and again, it will create as many header as you grouped. For now, no need to worry about that.

Go to your code behind file i.e. MainPage.xaml.cs file. There, just after the InitializeComponent() method inside the constructor call the GroupDataByColumnName() method two times, passing two different column name.

Have a look into the following code snippet:

See it in Action

This is all about the code. Let's build the solution. Hope you will get no error. If you get, check what is the error and fix it properly. Now run the application. You will see the following UI:

You will see that, the first level header has a foreground color "Red" where the second level header has foreground color "Blue" (as we specified in the template).

Browse through the other pages and you will see how the group row header styled itself properly. Hope, you got the basic idea. Now, you will be able to modify it properly as per your requirement. Let me know if you have any issue with this. Feedbacks are always appreciated.

Download

If you have come this far, it means that you liked what you are reading. Why not reach little more and connect with me directly on Twitter, Facebook, Google+ and LinkedIn. I would love to hear your thoughts and opinions on my articles directly. Also, don't forget to share your views and/or feedback in the comment section below.

Thanks for your reply ! Can we make the textbox in this sample into a AutoComplete box where , when we type text we could get your datagrid as dropdown, instead of having a datagrid below in this sample. ( for example, sliverlight tool kit samples has a "DataGridAutoComplete Box" )

not exactly Datagrid but, www.silverlight.net has a toolkit which has a textbox and when we enter text in it we get a dropdown which is in the form of datagrid ! They call it "DataGridAutoCompleteBox".

Nice example, Kunal! But I have an important to me question at this moment.Can I change sdk:DataGrid.RowGroupHeaderStyles properties (Height or Visibility) dinamically?I want GroupRowHeader Height = 0 (or Visibility = Collapsed) when grouped value is empty.

Which post are you talking about? Is it on the topic "Can I change sdk:DataGrid.RowGroupHeaderStyles properties (Height or Visibility) dynamically?" If so, then that post is not yet up. I will do it soon and will inform you once it is available.

Hi Kunal,thanks for this wonderful article, I want to hide a sub level group header row if the sub level group header is null or empty, please provide me some pointer to do this. In the runtime I need to get the sub level groupheader and if group is formed of a null or empty value then make that groupheader visibility to collapsed.

Kunal,I did put a converter in the localprimitives:DataGridFrozenGrid visibility property which is inside my top level Style TargetType="sdk:DataGridRowGroupHeader", it hides the contents of the grouprow header and leaves the group row as blank row but the group row is still there. I think I'm puting the converter in the wrong place but localprimitives:DataGridFrozenGrid is the first element from the top accepts visibility binding. I took the Style TargetType="sdk:DataGridRowGroupHeader" from http://msdn.microsoft.com/en-us/library/cc278066(v=vs.95).aspx. Basically I don't want sub group level row if the value is null or empty.Really thankful to replying me instantly.

Kunal,Just saw your xaml, you have only one Style TargetType="sdk:DataGridRowGroupHeader" within that you have the sublevel as another grid, but I have two Style TargetType="sdk:DataGridRowGroupHeader" inside the sdk:DataGrid.RowGroupHeaderStyles and each of it having a stack panel containing the group row with the aggregated columns. Can I put my sublevel style within the top level and set the visibility property of the sublevel.

Ok, I guess, I got it now. In such case, you have to place a code in the xaml.cs file to loop thru the RowGroupHeader and make it collapsed. I have to write that code. Once done, I will share you that code shortly.

Alternatively, you may check this post and try whether you can achieve the functionality using XAML and Converter. The point "Editing the Template to create Multi row group header" will help you to understand the functionality.

Thanks Kunal, I tried looping through the RowGroupHeader, but the groups are readonly collection so I'm not able to remove an item, also struggled to identify the sub level group with the null value. Many Thanks for looking into this issue.

Hi Kunal, This is really a great article, first one of this kind with more design details. It gave more insight about the row group header UI design customization. Also I reached multi-level customization with different designs. So it is not only upto 2 levels as u mentioned in the (). But I am little stuck in using a text wrap for the textblock (binding name)as the 3rd level has lot of text more than 1000 characters. Have tried this ever?if so could you update this post accordingly. Thanks.

About Me

Kunal Chowdhury is a Windows Platform Development MVP of Microsoft, a Telerik MVP, Nokia Developer Champion, Speaker in various Microsoft events, Author, passionate Blogger and a Software Engineer by profession.