将 PropertyGroupDescription 的其他实例添加到 GroupDescriptions 集合,以添加更多级别的分组。
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="ProjectName"/>
<PropertyGroupDescription PropertyName="Complete"/>
</CollectionViewSource.GroupDescriptions>
ICollectionView cvTasks = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
if (cvTasks != null && cvTasks.CanGroup == true)
cvTasks.GroupDescriptions.Clear();
cvTasks.GroupDescriptions.Add(new PropertyGroupDescription("ProjectName"));
cvTasks.GroupDescriptions.Add(new PropertyGroupDescription("Complete"));
Dim cvTasks As ICollectionView = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource)
If cvTasks IsNot Nothing And cvTasks.CanGroup = True Then
cvTasks.GroupDescriptions.Clear()
cvTasks.GroupDescriptions.Add(New PropertyGroupDescription("ProjectName"))
cvTasks.GroupDescriptions.Add(New PropertyGroupDescription("Complete"))
End If
要删除该组,请从 GroupDescriptions 集合中删除 PropertyGroupDescription。
要删除所有组,请调用 GroupDescriptions 集合的 Clear 方法。
ICollectionView cvTasks = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
if (cvTasks != null)
cvTasks.GroupDescriptions.Clear();
Dim cvTasks As ICollectionView = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource)
If cvTasks IsNot Nothing Then
cvTasks.GroupDescriptions.Clear()
End If
在 DataGrid 中对项进行分组后,可以定义一个 GroupStyle 来指定每个组的外观。 通过将 GroupStyle 添加到 DataGrid 的 GroupStyle 集合来应用它。 如果你有多个级别的分组,则可以对每个级别的组应用不同的样式。 样式按照定义顺序应用。 例如,如果定义两种样式,则第一个样式将应用于顶级行组。 第二种样式将应用于第二级或更低级别的所有行组。 GroupStyle 的 DataContext 就是该组所代表的 CollectionViewGroup。
创建定义行组外观的 GroupStyle。
将 GroupStyle 放入 <DataGrid.GroupStyle>
标记中。
<DataGrid.GroupStyle>
<!-- Style for groups at top level. -->
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" Background="#FF112255" BorderBrush="#FF002255" Foreground="#FFEEEEEE" BorderThickness="1,1,1,5">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Margin="5,0,0,0" Width="100"/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
<!-- Style for groups under the top level. -->
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<DockPanel Background="LightBlue">
<TextBlock Text="{Binding Path=Name, Converter={StaticResource completeConverter}}" Foreground="Blue" Margin="30,0,0,0" Width="100"/>
<TextBlock Text="{Binding Path=ItemCount}" Foreground="Blue"/>
</DockPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</DataGrid.GroupStyle>
对 DataGrid 中的项进行排序
要指定如何对 DataGrid 中的项进行排序,请使用 SortDescription 类型对源视图中的项进行排序。
对 DataGrid 中的项进行排序
创建一个 SortDescription,指定要按其排序的属性。 可以在 XAML 或代码中指定该属性。
在 XAML 中,将 PropertyName 设置要按其排序的属性的名称。
在代码中,将按其排序的属性的名称和 ListSortDirection 传递给构造函数。
将 SortDescription 添加到 CollectionViewSource.SortDescriptions 集合中。
将 SortDescription 的其他实例添加到 SortDescriptions 集合,以按其他属性排序。
<CollectionViewSource.SortDescriptions>
<!-- Requires 'xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"' declaration. -->
<scm:SortDescription PropertyName="ProjectName"/>
<scm:SortDescription PropertyName="Complete" />
<scm:SortDescription PropertyName="DueDate" />
</CollectionViewSource.SortDescriptions>
// Requires using System.ComponentModel;
ICollectionView cvTasks = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
if (cvTasks != null && cvTasks.CanSort == true)
cvTasks.SortDescriptions.Clear();
cvTasks.SortDescriptions.Add(new SortDescription("ProjectName", ListSortDirection.Ascending));
cvTasks.SortDescriptions.Add(new SortDescription("Complete", ListSortDirection.Ascending));
cvTasks.SortDescriptions.Add(new SortDescription("DueDate", ListSortDirection.Ascending));
Dim cvTasks As ICollectionView = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource)
If cvTasks IsNot Nothing And cvTasks.CanSort = True Then
cvTasks.SortDescriptions.Clear()
cvTasks.SortDescriptions.Add(New SortDescription("ProjectName", ListSortDirection.Ascending))
cvTasks.SortDescriptions.Add(New SortDescription("Complete", ListSortDirection.Ascending))
cvTasks.SortDescriptions.Add(New SortDescription("DueDate", ListSortDirection.Ascending))
End If
对 DataGrid 中的项进行筛选
要使用 CollectionViewSource 筛选 DataGrid 中的项,需要在处理程序中为 CollectionViewSource.Filter 事件提供筛选逻辑。
筛选 DataGrid 中的项
为 CollectionViewSource.Filter 事件添加处理程序。
在 Filter 事件处理程序中,定义筛选逻辑。
每次刷新视图时,都会应用该筛选器。
<CollectionViewSource x:Key="cvsTasks" Source="{StaticResource tasks}"
Filter="CollectionViewSource_Filter">
private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
Task t = e.Item as Task;
if (t != null)
// If filter is turned on, filter completed items.
if (this.cbCompleteFilter.IsChecked == true && t.Complete == true)
e.Accepted = false;
e.Accepted = true;
Private Sub CollectionViewSource_Filter(ByVal sender As System.Object, ByVal e As System.Windows.Data.FilterEventArgs)
Dim t As Task = e.Item
If t IsNot Nothing Then
' If filter is turned on, filter completed items.
If Me.cbCompleteFilter.IsChecked = True And t.Complete = True Then
e.Accepted = False
e.Accepted = True
End If
End If
End Sub
或者,可以创建一个提供筛选逻辑的方法并设置 CollectionView.Filter 属性来应用筛选器,从而筛选 DataGrid 中的项。 要查看此方法的示例,请参阅筛选视图中的数据。
下面的示例演示如何对 CollectionViewSource 中的 Task
数据进行分组、排序和筛选,并在 DataGrid 中显示经过分组、排序和筛选的 Task
数据。 CollectionViewSource 用作 DataGrid 的 ItemsSource。 对 CollectionViewSource 执行分组、排序和筛选,并在 DataGrid UI 中显示。
要测试此示例,需要调整 DGGroupSortFilterExample 名称以匹配项目名称。 如果使用 Visual Basic,则需要将 Window 的类名更改为以下形式。
<Window x:Class="MainWindow"
<Window x:Class="DGGroupSortFilterExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DGGroupSortFilterExample"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
Title="Group, Sort, and Filter Example" Height="575" Width="525">
<Window.Resources>
<local:CompleteConverter x:Key="completeConverter" />
<local:Tasks x:Key="tasks" />
<CollectionViewSource x:Key="cvsTasks" Source="{StaticResource tasks}"
Filter="CollectionViewSource_Filter">
<CollectionViewSource.SortDescriptions>
<!-- Requires 'xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"' declaration. -->
<scm:SortDescription PropertyName="ProjectName"/>
<scm:SortDescription PropertyName="Complete" />
<scm:SortDescription PropertyName="DueDate" />
</CollectionViewSource.SortDescriptions>
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="ProjectName"/>
<PropertyGroupDescription PropertyName="Complete"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<DataGrid x:Name="dataGrid1"
ItemsSource="{Binding Source={StaticResource cvsTasks}}"
CanUserAddRows="False">
<DataGrid.GroupStyle>
<!-- Style for groups at top level. -->
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" Background="#FF112255" BorderBrush="#FF002255" Foreground="#FFEEEEEE" BorderThickness="1,1,1,5">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Margin="5,0,0,0" Width="100"/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
<!-- Style for groups under the top level. -->
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<DockPanel Background="LightBlue">
<TextBlock Text="{Binding Path=Name, Converter={StaticResource completeConverter}}" Foreground="Blue" Margin="30,0,0,0" Width="100"/>
<TextBlock Text="{Binding Path=ItemCount}" Foreground="Blue"/>
</DockPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Foreground" Value="Black" />
<Setter Property="Background" Value="White" />
</Style>
</DataGrid.RowStyle>
</DataGrid>
<StackPanel Orientation="Horizontal" Grid.Row="1">
<TextBlock Text=" Filter completed items " VerticalAlignment="Center" />
<CheckBox x:Name="cbCompleteFilter" VerticalAlignment="Center"
Checked="CompleteFilter_Changed" Unchecked="CompleteFilter_Changed" />
<Button Content="Remove Groups" Margin="10,2,2,2" Click="UngroupButton_Click" />
<Button Content="Group by Project/Status" Margin="2" Click="GroupButton_Click" />
</StackPanel>
</Grid>
</Window>
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
namespace DGGroupSortFilterExample
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
public MainWindow()
InitializeComponent();
// Get a reference to the tasks collection.
Tasks _tasks = (Tasks)this.Resources["tasks"];
// Generate some task data and add it to the task list.
for (int i = 1; i <= 14; i++)
_tasks.Add(new Task()
ProjectName = "Project " + ((i % 3) + 1).ToString(),
TaskName = "Task " + i.ToString(),
DueDate = DateTime.Now.AddDays(i),
Complete = (i % 2 == 0)
private void UngroupButton_Click(object sender, RoutedEventArgs e)
ICollectionView cvTasks = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
if (cvTasks != null)
cvTasks.GroupDescriptions.Clear();
private void GroupButton_Click(object sender, RoutedEventArgs e)
ICollectionView cvTasks = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
if (cvTasks != null && cvTasks.CanGroup == true)
cvTasks.GroupDescriptions.Clear();
cvTasks.GroupDescriptions.Add(new PropertyGroupDescription("ProjectName"));
cvTasks.GroupDescriptions.Add(new PropertyGroupDescription("Complete"));
private void CompleteFilter_Changed(object sender, RoutedEventArgs e)
// Refresh the view to apply filters.
CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource).Refresh();
private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
Task t = e.Item as Task;
if (t != null)
// If filter is turned on, filter completed items.
if (this.cbCompleteFilter.IsChecked == true && t.Complete == true)
e.Accepted = false;
e.Accepted = true;
[ValueConversion(typeof(Boolean), typeof(String))]
public class CompleteConverter : IValueConverter
// This converter changes the value of a Tasks Complete status from true/false to a string value of
// "Complete"/"Active" for use in the row group header.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
bool complete = (bool)value;
if (complete)
return "Complete";
return "Active";
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
string strComplete = (string)value;
if (strComplete == "Complete")
return true;
return false;
// Task Class
// Requires using System.ComponentModel;
public class Task : INotifyPropertyChanged, IEditableObject
// The Task class implements INotifyPropertyChanged and IEditableObject
// so that the datagrid can properly respond to changes to the
// data collection and edits made in the DataGrid.
// Private task data.
private string m_ProjectName = string.Empty;
private string m_TaskName = string.Empty;
private DateTime m_DueDate = DateTime.Now;
private bool m_Complete = false;
// Data for undoing canceled edits.
private Task temp_Task = null;
private bool m_Editing = false;
// Public properties.
public string ProjectName
get { return this.m_ProjectName; }
if (value != this.m_ProjectName)
this.m_ProjectName = value;
NotifyPropertyChanged("ProjectName");
public string TaskName
get { return this.m_TaskName; }
if (value != this.m_TaskName)
this.m_TaskName = value;
NotifyPropertyChanged("TaskName");
public DateTime DueDate
get { return this.m_DueDate; }
if (value != this.m_DueDate)
this.m_DueDate = value;
NotifyPropertyChanged("DueDate");
public bool Complete
get { return this.m_Complete; }
if (value != this.m_Complete)
this.m_Complete = value;
NotifyPropertyChanged("Complete");
// Implement INotifyPropertyChanged interface.
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
// Implement IEditableObject interface.
public void BeginEdit()
if (m_Editing == false)
temp_Task = this.MemberwiseClone() as Task;
m_Editing = true;
public void CancelEdit()
if (m_Editing == true)
this.ProjectName = temp_Task.ProjectName;
this.TaskName = temp_Task.TaskName;
this.DueDate = temp_Task.DueDate;
this.Complete = temp_Task.Complete;
m_Editing = false;
public void EndEdit()
if (m_Editing == true)
temp_Task = null;
m_Editing = false;
// Requires using System.Collections.ObjectModel;
public class Tasks : ObservableCollection<Task>
// Creating the Tasks collection in this way enables data binding from XAML.
Imports System.ComponentModel
Imports System.Collections.ObjectModel
Class MainWindow
Public Sub New()
InitializeComponent()
' Get a reference to the tasks collection.
Dim _tasks As Tasks = Me.Resources("tasks")
' Generate some task data and add it to the task list.
For index = 1 To 14
_tasks.Add(New Task() With _
{.ProjectName = "Project " & ((index Mod 3) + 1).ToString(), _
.TaskName = "Task " & index.ToString(), _
.DueDate = Date.Now.AddDays(index), _
.Complete = (index Mod 2 = 0) _
End Sub
Private Sub UngroupButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim cvTasks As ICollectionView = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource)
If cvTasks IsNot Nothing Then
cvTasks.GroupDescriptions.Clear()
End If
End Sub
Private Sub GroupButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim cvTasks As ICollectionView = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource)
If cvTasks IsNot Nothing And cvTasks.CanGroup = True Then
cvTasks.GroupDescriptions.Clear()
cvTasks.GroupDescriptions.Add(New PropertyGroupDescription("ProjectName"))
cvTasks.GroupDescriptions.Add(New PropertyGroupDescription("Complete"))
End If
End Sub
Private Sub CompleteFilter_Changed(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
' Refresh the view to apply filters.
CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource).Refresh()
End Sub
Private Sub CollectionViewSource_Filter(ByVal sender As System.Object, ByVal e As System.Windows.Data.FilterEventArgs)
Dim t As Task = e.Item
If t IsNot Nothing Then
' If filter is turned on, filter completed items.
If Me.cbCompleteFilter.IsChecked = True And t.Complete = True Then
e.Accepted = False
e.Accepted = True
End If
End If
End Sub
End Class
Public Class CompleteConverter
Implements IValueConverter
' This converter changes the value of a Tasks Complete status from true/false to a string value of
' "Complete"/"Active" for use in the row group header.
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
Dim complete As Boolean = value
If complete = True Then
Return "Complete"
Return "Active"
End If
End Function
Public Function ConvertBack1(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Dim strComplete As String = value
If strComplete = "Complete" Then
Return True
Return False
End If
End Function
End Class
' Task class
' Requires Imports System.ComponentModel
Public Class Task
Implements INotifyPropertyChanged, IEditableObject
' The Task class implements INotifyPropertyChanged and IEditableObject
' so that the datagrid can properly respond to changes to the
' data collection and edits made in the DataGrid.
' Private task data.
Private m_ProjectName As String = String.Empty
Private m_TaskName As String = String.Empty
Private m_DueDate As DateTime = Date.Now
Private m_Complete As Boolean = False
' Data for undoing canceled edits.
Private temp_Task As Task = Nothing
Private m_Editing As Boolean = False
' Public properties.
Public Property ProjectName() As String
Return Me.m_ProjectName
End Get
Set(ByVal value As String)
If Not value = Me.m_ProjectName Then
Me.m_ProjectName = value
NotifyPropertyChanged("ProjectName")
End If
End Set
End Property
Public Property TaskName() As String
Return Me.m_TaskName
End Get
Set(ByVal value As String)
If Not value = Me.m_TaskName Then
Me.m_TaskName = value
NotifyPropertyChanged("TaskName")
End If
End Set
End Property
Public Property DueDate() As Date
Return Me.m_DueDate
End Get
Set(ByVal value As Date)
If Not value = Me.m_DueDate Then
Me.m_DueDate = value
NotifyPropertyChanged("DueDate")
End If
End Set
End Property
Public Property Complete() As Boolean
Return Me.m_Complete
End Get
Set(ByVal value As Boolean)
If Not value = Me.m_Complete Then
Me.m_Complete = value
NotifyPropertyChanged("Complete")
End If
End Set
End Property
' Implement INotifyPropertyChanged interface.
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Sub NotifyPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
' Implement IEditableObject interface.
Public Sub BeginEdit() Implements IEditableObject.BeginEdit
If Not Me.m_Editing Then
Me.temp_Task = Me.MemberwiseClone()
Me.m_Editing = True
End If
End Sub
Public Sub CancelEdit() Implements IEditableObject.CancelEdit
If m_Editing = True Then
Me.ProjectName = Me.temp_Task.ProjectName
Me.TaskName = Me.temp_Task.TaskName
Me.DueDate = Me.temp_Task.DueDate
Me.Complete = Me.temp_Task.Complete
Me.m_Editing = False
End If
End Sub
Public Sub EndEdit() Implements IEditableObject.EndEdit
If m_Editing = True Then
Me.temp_Task = Nothing
Me.m_Editing = False
End If
End Sub
End Class
' Requires Imports System.Collections.ObjectModel
Public Class Tasks
Inherits ObservableCollection(Of Task)
' Creating the Tasks collection in this way enables data binding from XAML.
End Class
数据绑定概述
创建和绑定到 ObservableCollection
在视图中筛选数据
在视图中对数据进行排序
在 XAML 中使用视图对数据进行排序和分组