Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Customizing Group Row Header of Silverlight DataGrid

0.00/5 (No votes)
20 Jan 2011 2  
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.

Introduction

Earlier in December 2010, I posted three articles on DataGrid titled as:

If you didn't read them, go through each article to know more about Silverlight DataGrid.

One of my readers "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 of writing 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 the readers "Dan" asked me to help him to do the same. I thought of completing the article so that other people will also get help from it in the future.

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

image_8__1_.png

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

Begin with the Template

Let's start with the existing application that we created earlier in the previous three articles. In this article, we will edit the template to show a 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 apply to the DataGrid's RowGroupHeaderStyles. Just copy it in your XAML page as shown below:

<sdk:DataGrid 
    IsReadOnly="True" Margin="10" Height="200" ItemsSource="{Binding Employees}">
    <sdk:DataGrid.RowGroupHeaderStyles>
        <Style TargetType="sdk:DataGridRowGroupHeader">
            <Setter Property="Cursor" Value="Arrow" />
            <Setter Property="IsTabStop" Value="False" />
            <Setter Property="Background" Value="#FFE4E8EA" />
            <Setter Property="Height" Value="20"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="sdk:DataGridRowGroupHeader">
                        <sdk:DataGridFrozenGrid x:Name="Root" 
                                                Background="{TemplateBinding Background}">
                            <sdk:DataGridFrozenGrid.Resources>
                                <ControlTemplate x:Key="ToggleButtonTemplate" 
                                                 TargetType="ToggleButton">
                                    <Grid>
                                    .
                                    .
                                    .
 
                            <Rectangle Grid.Column="1" Grid.ColumnSpan="5" 
				   Fill="#FFD3D3D3" 
                                       Height="1" Grid.Row="2"/>
                            <Rectangle x:Name="FocusVisual" Grid.Column="1" 
                                       Grid.ColumnSpan="4" Grid.RowSpan="3" 
                                       Stroke="#FF6DBDD1" StrokeThickness="1" 
                                       HorizontalAlignment="Stretch" 
                                       VerticalAlignment="Stretch" 
                                       IsHitTestVisible="false" Opacity="0" />
                            <sdk:DataGridRowHeader x:Name="RowHeader" Grid.RowSpan="3" 
                                                sdk:DataGridFrozenGrid.IsFrozen="True"/>
                        
                        </sdk:DataGridFrozenGrid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </sdk:DataGrid.RowGroupHeaderStyles>
</sdk:DataGrid>

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. Its width specifies the amount that the immediate children of the DataGridRowGroupHeader are indented. The default value of it is 20.

<Rectangle Grid.Column="1" Grid.ColumnSpan="5" Fill="#FFFFFFFF" Height="1"/>
<Rectangle Grid.Column="1" Grid.Row="1" x:Name="IndentSpacer" />
<ToggleButton Grid.Column="2" Grid.Row="1" x:Name="ExpanderButton" 
              Height="15" Width="15" IsTabStop="False" 
              Template="{StaticResource ToggleButtonTemplate}" Margin="2,0,0,0"/>

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

Converter to Switch Between Two Group Headers

In this article, we are going to see how we can customize the template to create two different group row headers while grouped with two different levels. 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 collapsed.

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 parameter 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:

public class GroupRowHeaderVisibilityConverter : IValueConverter
{
    public object Convert(object value, 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;
    }
 
    public object ConvertBack(object value, Type targetType, 
                              object parameter, CultureInfo culture)
    {
        throw new 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:

image_17__1_.png

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:

<StackPanel Grid.Column="3" Grid.Row="1" Orientation="Horizontal"
            VerticalAlignment="Center" Margin="0,1,0,1">
    <TextBlock x:Name="PropertyNameElement" Margin="4,0,0,0" 
               Visibility="{TemplateBinding PropertyNameVisibility}"/>
    <TextBlock Margin="4,0,0,0" Text="{Binding Name}" />
    <TextBlock x: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 -->
<StackPanel Grid.Column="3" Grid.Row="1" Orientation="Horizontal" 
            VerticalAlignment="Center" Margin="0,1,0,1" 
            Visibility="{Binding Width, 
                        Converter={StaticResource GroupRowHeaderVisibilityConverter}, 
                        ElementName=IndentSpacer}">
    <TextBlock Margin="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 -->
<Grid Grid.Column="3" Grid.Row="1" VerticalAlignment="Center" 
      HorizontalAlignment="Stretch" Margin="0,1,0,1"
      Visibility="{Binding Width, ConverterParameter=true, 
                   Converter={StaticResource GroupRowHeaderVisibilityConverter}, 
                   ElementName=IndentSpacer}">
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
        <TextBlock Margin="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 have visibility property binded to the Width of the IndentSpacer with the converter that we created earlier.

So, if the width is zero and converter parameter 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:

image_20__1_.png

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 headers 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 names.

Have a look into the following code snippet:

image_23__1_.png

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:

image_11__1_.png

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).

image_14__1_.png

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. Don't forget to vote. Feedback is always appreciated.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here