自定义 Silverlight DataGrid 的分组行标题






4.89/5 (17投票s)
本文肯定会对您有所帮助。在这里,我将向您展示如何修改 XAML 以添加不同的内容来创建多级行组标题。
引言
早在 2010 年 12 月,我就发表了三篇关于 DataGrid
的文章,标题是:
- 使用 PagedCollectionView 对 Silverlight DataGrid 中的记录进行分组
- 使用 PagedCollectionView 过滤 Silverlight DataGrid 中的记录
- 使用 PagedCollectionView 分页 Silverlight DataGrid 中的记录
如果您还没有阅读它们,请阅读每篇文章以了解更多关于 Silverlight DataGrid
的信息。
我的读者之一“Dan”要求我写一篇关于“Silverlight DataGrid
的组行标题自定义”的文章,这就是这篇文章。如果您想对行组标题进行 UI 自定义,本文肯定会对您有所帮助。在这里,我将向您展示如何修改 XAML 以添加不同的内容来创建多级行组标题。
背景
此前,我一直在寻找创建 Silverlight DataGrid
中多级组行标题的解决方案,并在包括 Tim 的博客在内的各种论坛上找到了一些有用的信息。我进一步探索了它们,发现是的,我们可以自定义模板来创建多级组标题(但这仅限于两级,意味着如果您添加更多标题级别,其余的组标题将与第二个标题看起来相同)。我想写一篇文章来详细解释这一切,以便您能正确理解。但由于时间不足,我未能完成。
到那时,一位读者“Dan”请求我帮助他做同样的事情。我想着完成这篇文章,以便将来其他人也能从中获得帮助。
这是我们希望通过阅读本文达成的目标的一个屏幕截图

在这里,我将详细展示自定义。仔细阅读,如果您需要任何具体帮助,请告诉我。由于 XAML 代码无法在此处正确显示,我将仅展示理解所需的部分。但您可以轻松地从下载部分下载整个源代码。
从模板开始
让我们从我们在前三篇文章中创建的现有应用程序开始。在本文中,我们将编辑模板以显示不同的组标题(两级)。要开始处理它,您需要默认的 DataGridGroupRowHeader
样式。您可以从 MSDN 找到它的默认样式,这是链接。样式应应用于 DataGrid
的 RowGroupHeaderStyles
。只需将其复制到您的 XAML 页面中,如下所示
<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>
如果您查看编辑后的样式,您会注意到有一个名为“IndentSpacer
”的 Rectangle
。这将为您执行实际操作。IndentSpacer
用于缩进子组。它的宽度指定了 DataGridRowGroupHeader
的直接子项缩进的量。它的默认值为 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"/>
请参见上面的代码,找到 IndentSpacer
在模板中的实际位置。
用于在两个组标题之间切换的转换器
在本文中,我们将看到如何自定义模板以在与两个不同级别分组时创建两个不同的组行标题。所以,让我们为此创建一个转换器。转换器会检查 IndentSpacer
的宽度,并根据值显示或隐藏第一个或第二个组标题。
在我们的示例中,如果该矩形的宽度为零且转换器参数值指定为 false
,它将返回 Visible
。如果宽度不为零且转换器参数指定为 true 值,则情况相同。在其他情况下,它将始终返回 Collapsed
状态。稍后在 XAML 中使用转换器时,我们将对此进行更详细的讨论。这将帮助您深入理解概念。
以下是供您参考的转换器
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();
}
}
现在将转换器引用添加到您的 XAML 页面。为此,请在 XAML 页面中添加 xmlns
命名空间,然后将转换器作为资源添加。请看下面的快照

编辑模板以创建多行组标题
现在是时候创建我们的行组标题了。默认模板为行组标题样式包含以下 XAML 代码
<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>
让我们进行修改并添加以下代码
<!-- 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 -->
在此,第一个 stackpanel
是您的第一级组标题,网格内的第二个 stackpanel
是您的第二级、第三级……组标题。两者都将 visibility
属性绑定到 IndentSpacer
的 Width
,并使用我们之前创建的转换器。
因此,如果宽度为零且转换器参数设置为 false
(默认值),它将显示第一个 stackpanel
,即您的第一级行组标题。第二个 stackpanel
将折叠,因为我们传递了 Converter
参数“true
”。
反之,第一个 stackpanel
将折叠,第二个 stackpanel
将获得可见性。从而显示下一级行组标题。
我只是在那里添加了一些默认值,其中标题文本的字体颜色设置为不同的前景色。如果您理解其背后的逻辑,现在就可以轻松地对其进行自定义。
玩转代码以实现多重分组
既然我们的 XAML 已准备就绪,现在是时候在 C# 中编写一些代码来在加载时对记录进行分组了。首先,我们将转到 ViewModel
并注释掉快照中标出的以下行

请记住:此注释仅为方便本示例工作。这里的问题是,如果您一次又一次地从下拉列表中分组,它将创建您分组的尽可能多的标题。现在,无需担心。
转到您的代码隐藏文件,即 MainPage.xaml.cs 文件。在那里,在构造函数中的 InitializeComponent()
方法之后,调用 GroupDataByColumnName()
方法两次,传递两个不同的列名。
请看下面的代码片段

实际效果
这就是代码的全部内容。让我们构建解决方案。希望您不会遇到任何错误。如果遇到,请检查错误是什么并正确修复。现在运行应用程序。您将看到以下 UI

您将看到第一级标题的前景色为“Red
”,而第二级标题的前景色为“Blue
”(如我们在模板中指定的)。

浏览其他页面,您将看到组行标题如何正确设置样式。希望您已经掌握了基本概念。现在,您可以根据您的要求对其进行适当的修改。如果您对此有任何问题,请告诉我。不要忘记投票。反馈总是受欢迎的。