Customizing Group Row Header of Silverlight DataGrid
Earlier in December 2010, I posted three articles on DataGrid titled as " Grouping Records in Silverlight DataGrid using PagedCollect...- Article authored by Kunal Chowdhury on .
Earlier in December 2010, I posted three articles on DataGrid titled as " Grouping Records in Silverlight DataGrid using PagedCollect...- Article authored by Kunal Chowdhury on .
Earlier in December 2010, I posted three articles on DataGrid titled as "Grouping Records in Silverlight DataGrid using PagedCollectionView", "Filtering Records in Silverlight DataGrid using PagedCollectionView" and "Paginating Records in Silverlight DataGrid using PagedCollectionView". If you didn't read them, go thru each articles to know more about Silverlight DataGrid.
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.
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.
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:
<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. It's 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.
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:
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:
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 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.
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:
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.
Thank you for visiting our website!
We value your engagement and would love to hear your thoughts. Don't forget to leave a comment below to share your feedback, opinions, or questions.
We believe in fostering an interactive and inclusive community, and your comments play a crucial role in creating that environment.