使用過Excel的用戶都知道,Excel可以方便的對數據進行分組,過濾,排序等操作,而在WPF中,預設提供的DataGrid只有很簡單的功能,那麼如何才能讓我們開發的DataGrid,也像Excel一樣具備豐富的客戶端操作呢?今天就以一個簡單的小例子,簡述如何在WPF中實現DataGrid的過濾,篩... ...
使用過Excel的用戶都知道,Excel可以方便的對數據進行分組,過濾,排序等操作,而在WPF中,預設提供的DataGrid只有很簡單的功能,那麼如何才能讓我們開發的DataGrid,也像Excel一樣具備豐富的客戶端操作呢?今天就以一個簡單的小例子,簡述如何在WPF中實現DataGrid的過濾,篩選,排序等功能。僅供學習分享使用,如有不足之處,還請指正。
涉及知識點
在本示例中,涉及知識點如下所示:
- CollectionView, CollectionView 類為實現 IEnumerable 介面的數據源提供分組和排序功能。
- CollectionViewSource,CollectionViewSource 類允許你從 XAML 設置 CollectionView 的屬性。
註意:此兩個類,是我們實現客戶端過濾,分組,排序的關鍵。
普通綁定
1. 構建數據源
在WPF中,DataGrid的ItemSource屬性用於綁定數據源,而數據源必須是實現IEnumerable介面的的列表類型,在本示例中,採用具有通知屬性的列表類型ObservableCollection。當列表中元素數量發生變化時,可以實時的通知DataGrid進行刷新。
1.1 創建實體
在本示例中,為了測試,創建Student實體模型,如下所示:
public class Student
{
public string No { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool Sex { get; set; }
public string Class { get; set; }
}
1.2 初始化數據源列表
在本示例採用MVVM模式開發,在ViewModel中創建ObservableCollection類型的Students列表,如下所示:
using CommunityToolkit.Mvvm.ComponentModel;
using DemoDataGrid2.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DemoDataGrid2.ViewModels
{
public class TestWindowViewModel:ObservableObject
{
private ObservableCollection<Student> students;
public ObservableCollection<Student> Students
{
get { return students; }
set { SetProperty(ref students, value); }
}
public TestWindowViewModel()
{
var parentName = new string[5] { "張", "王", "李", "趙", "劉" };
this.Students = new ObservableCollection<Student>();
for (int i = 0; i < 100; i++)
{
Student student = new Student();
student.No = i.ToString().PadLeft(3, '0');
student.Name = parentName[(i % 4)] + i.ToString().PadLeft(2, 'A');
student.Age = 20 + (i % 5);
student.Sex = i % 2 == 0 ? true : false;
student.Class = $"{(i % 3)}班";
this.Students.Add(student);
}
}
}
}
註意:構造函數中的方法,用於創建Students列表,包含100名學生,分別對應不同的編號,姓名,年齡,性別,班級等信息。
2. 頁面綁定
在ViewModel中創建數據源後,可以在Xaml中進行綁定【語法:ItemsSource="{Binding Students}"】,如下所示:
<Window x:Class="DemoDataGrid2.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DemoDataGrid2"
mc:Ignorable="d"
Title="DataGrid示例" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0">
</DockPanel>
<DataGrid Grid.Row="1" ItemsSource="{Binding Students}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="學號" Binding="{Binding No}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="姓名" Binding="{Binding Name}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="年齡" Binding="{Binding Age}" Width="*"></DataGridTextColumn>
<DataGridTemplateColumn Header="性別" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="sex">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Sex}" Value="True">
<Setter Property="Text" Value="男"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Sex}" Value="False">
<Setter Property="Text" Value="女"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="班級" Binding="{Binding Class}" Width="*"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
以下兩點需要註意:
- 在本示例中,性別為bool類型,要轉換成漢字,用到了DataTrigger。
- DataGrid的列可以自動生成,也可以手動創建,可以通過AutoGenerateColumns="False"來設置。
3. 普通綁定示例
普通綁定示例截圖,如下所示:
DataGrid過濾
在DataGrid中,實現客戶端過濾,且不需要重新初始化數據源,則需要用到CollectionViewSource。
1. 定義資源及綁定
將CollectionViewSource定義成一種資源,並將資源的Source屬性綁定到數據源,再將DataGrid中的ItemSource綁定到此資源,然後就可以在過濾時對資源進行過濾。
定義資源如下所示:
<Window.Resources>
<CollectionViewSource x:Key="cvStudents" Source="{Binding Students}"></CollectionViewSource>
</Window.Resources>
DataGrid綁定資源【語法:ItemsSource="{Binding Source={StaticResource cvStudents}}"】,如下所示:
<DataGrid x:Name="dgStudents" Grid.Row="1" ItemsSource="{Binding Source={StaticResource cvStudents}}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="學號" Binding="{Binding No}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="姓名" Binding="{Binding Name}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="年齡" Binding="{Binding Age}" Width="*"></DataGridTextColumn>
<DataGridTemplateColumn Header="性別" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="sex">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Sex}" Value="True">
<Setter Property="Text" Value="男"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Sex}" Value="False">
<Setter Property="Text" Value="女"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="班級" Binding="{Binding Class}" Width="*"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
2. 過濾條件
在本示例中,以性別為過濾條件,當點擊過濾條件時,觸發過濾命令,如下所示:
<DockPanel Grid.Row="0" Margin="5">
<TextBlock Text="篩選條件:"></TextBlock>
<TextBlock Text="性別:"></TextBlock>
<CheckBox Content="男" IsChecked="{Binding FilterM.IsMaleChecked}" Command="{Binding FiterSexCheckedCommand}"></CheckBox>
<CheckBox Content="女" IsChecked="{Binding FilterM.IsFemaleChecked}" Command="{Binding FiterSexCheckedCommand}"></CheckBox>
</DockPanel>
當用戶點擊時,觸發Command綁定的命令,如下所示:
private ICommand fiterSexCheckedCommand;
public ICommand FiterSexCheckedCommand
{
get
{
if (fiterSexCheckedCommand == null)
{
fiterSexCheckedCommand = new RelayCommand<object>(FilterSexChecked);
}
return fiterSexCheckedCommand;
}
}
private void FilterSexChecked(object obj)
{
if (this.dataGrid != null)
{
ICollectionView cvs = CollectionViewSource.GetDefaultView(this.dataGrid.ItemsSource);
if (cvs != null && cvs.CanFilter)
{
cvs.Filter = (object obj) =>
{
bool flag = true;
bool flag1 = true;
bool flag2 = true;
var student = obj as Student;
if (!FilterM.IsMaleChecked)
{
flag1 = student.Sex != true;
}
if (!FilterM.IsFemaleChecked)
{
flag2 = student.Sex != false;
}
flag = flag1 && flag2;
return flag;
};
}
}
}
註意:通過CollectionViewSource.GetDefaultView(this.dataGrid.ItemsSource)方法獲取具有過濾功能的CollectionView類對象,然後再對Filter進行委托即可。
其中FilterM是在ViewModel中聲明的FilterConditionM類型的屬性。
private FilterConditionM filterM;
public FilterConditionM FilterM
{
get { return filterM; }
set { SetProperty(ref filterM, value); }
}
FilterConditionM是封裝的過濾條件模型類, 如下所示:
namespace DemoDataGrid2.Models
{
public class FilterConditionM:ObservableObject
{
private bool isMaleChecked;
public bool IsMaleChecked
{
get { return isMaleChecked; }
set { SetProperty(ref isMaleChecked , value); }
}
private bool isFemaleChecked;
public bool IsFemaleChecked
{
get { return isFemaleChecked; }
set { SetProperty(ref isFemaleChecked, value); }
}
}
}
3. 過濾功能示例
具備過濾功能的示例截圖,如下所示:
DataGrid分組
在WPF中,實現DataGrid的分組,也是通過CollectionViewSource來實現。
1. 設置分組列
有兩種方式可以設置分組
1.1 XAML中設置
在XAML中,通過設置CollectionViewSource的GroupDescriptions屬性,來設置具體分組的列屬性,如下所示:
<CollectionViewSource x:Key="cvStudents" Source="{Binding Students}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Class"/>
<PropertyGroupDescription PropertyName="Sex"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
1.2 後臺代碼設置
在ViewModel中設置CollectionView的GroupDescriptions屬性,如下所示:
ICollectionView cvTasks = CollectionViewSource.GetDefaultView(this.dataGrid.ItemsSource);
if (cvTasks != null && cvTasks.CanGroup == true)
{
cvTasks.GroupDescriptions.Clear();
cvTasks.GroupDescriptions.Add(new PropertyGroupDescription("Class"));
cvTasks.GroupDescriptions.Add(new PropertyGroupDescription("Sex"));
}
2. 設置分組樣式
在WPF中,通過設置DataGrid的GroupStyle屬性來改變分組樣式,如下所示:
<DataGrid x:Name="dgStudents" Grid.Row="1" ItemsSource="{Binding Source={StaticResource cvStudents}}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" >
<DataGrid.GroupStyle>
<!-- 第一層分組 -->
<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="LightGray" BorderBrush="#FF002255" Foreground="DarkBlue" BorderThickness="1">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Margin="5"/>
<TextBlock FontWeight="Bold" Text=" 班 , " VerticalAlignment="Center"></TextBlock>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}" VerticalAlignment="Center"/>
<TextBlock FontWeight="Bold" Text=" 名學生" VerticalAlignment="Center"></TextBlock>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
<!-- 第二層及之後的分組 -->
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<DockPanel Background="LightGoldenrodYellow">
<TextBlock Foreground="Blue" Margin="30,0,0,0" Width="30">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Name}" Value="True">
<Setter Property="Text" Value="男"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Name}" Value="False">
<Setter Property="Text" Value="女"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="{Binding Path=ItemCount}" Foreground="Blue"/>
</DockPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="學號" Binding="{Binding No}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="姓名" Binding="{Binding Name}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="年齡" Binding="{Binding Age}" Width="*"></DataGridTextColumn>
<DataGridTemplateColumn Header="性別" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="sex">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Sex}" Value="True">
<Setter Property="Text" Value="男"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Sex}" Value="False">
<Setter Property="Text" Value="女"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="班級" Binding="{Binding Class}" Width="*"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
DataGrid排序
在WPF中,實現DataGrid的排序,也是通過CollectionViewSource來實現。
1. 設置排序列
有兩種方式可以設置DataGrid排序列,如下所示:
1.1 XAML中設置
通過設置CollectionViewSource的SortDescriptions屬性,設置排序列和排序方向。如下所示:
<CollectionViewSource x:Key="cvStudents" Source="{Binding Students}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="No" Direction="Ascending"/>
<scm:SortDescription PropertyName="Age" Direction="Descending"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
其中scm是新定義的命名空間【xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"】
1.2 後臺代碼設置
在ViewModel中通過後臺代碼設置,同樣也需要引入對應的命名空間,如下所示:
ICollectionView cvTasks = CollectionViewSource.GetDefaultView(this.dataGrid.ItemsSource);
if (cvTasks != null && cvTasks.CanSort == true)
{
cvTasks.SortDescriptions.Clear();
cvTasks.SortDescriptions.Add(new SortDescription("No", ListSortDirection.Ascending));
cvTasks.SortDescriptions.Add(new SortDescription("Age", ListSortDirection.Ascending));
}
DataGrid整體示例
具備過濾,分組,排序的示例截圖,如下所示:
參考文獻
1. 官方文檔:https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/controls/how-to-group-sort-and-filter-data-in-the-datagrid-control?view=netframeworkdesktop-4.8
以上就是【淺談WPF之DataGrid過濾,分組,排序】的全部內容,希望能夠一起學習,共同進步。
作者:小六公子
出處:http://www.cnblogs.com/hsiang/
本文版權歸作者和博客園共有,寫文不易,支持原創,歡迎轉載【點贊】,轉載請保留此段聲明,且在文章頁面明顯位置給出原文連接,謝謝。
關註個人公眾號,定時同步更新技術及職場文章