在默认情况下,WPF提供的DataGrid仅拥有数据展示等简单功能,如果要实现像Excel一样复杂的筛选过滤功能,则相对比较麻烦。本文以一个简单的小例子,简述如何通过WPF实话DataGrid的筛选功能,仅供学习分享使用,如有不足之处,还请指正。
在本示例中,从数据绑定,到数据展示,涉及知识点如下所示:
本示例主要模仿Excel的筛选功能进行实现,右键标题栏打开浮动窗口,悬浮于标题栏下方,既可以通过文本框进行筛选,也可以通过筛选按钮弹出右键菜单,选择具体筛选方式,截图如下所示:
选择筛选方式,弹出窗口,如下所示:
输入筛选条件,点击确定,或者取消筛选。如筛选学号里面包含2的,效果如下所示:
注意:以上筛选都是客户端筛选,不会修改数据源,也不会重连数据库。
在本示例中,核心源码主要包含以下几个部分:
主要实现了按学号,姓名,年龄三列进行筛选,既可以单列筛选,又可以组合筛选。且三列的筛选实现方式一致,仅是绑定列有差异。
M608 864C588.8 864 576 851.2 576 832L576 448c0-6.4 6.4-19.2 12.8-25.6L787.2 256c6.4-6.4 6.4-19.2 0-19.2 0-6.4-6.4-12.8-19.2-12.8L256 224c-12.8 0-19.2 6.4-19.2 12.8 0 6.4-6.4 12.8 6.4 19.2l198.4 166.4C441.6 428.8 448 441.6 448 448l0 256c0 19.2-12.8 32-32 32S384 723.2 384 704L384 460.8 198.4 307.2c-25.6-25.6-32-64-19.2-96C185.6 179.2 217.6 160 256 160L768 160c32 0 64 19.2 76.8 51.2 12.8 32 6.4 70.4-19.2 89.6l-192 160L633.6 832C640 851.2 627.2 864 608 864z
业务逻辑处理主要复责数据初始化等业务相关内容,和UI无关,如下所示:
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DemoDataGrid
{
public class MainWindowViewModel:ObservableObject
{
#region 属性及构造函数
private List students;
public List Students
{
get { return students; }
set { SetProperty(ref students, value); }
}
private List names;
public List Names
{
get { return names; }
set { SetProperty(ref names, value); }
}
private List nos;
public List Nos
{
get { return nos; }
set {SetProperty(ref nos , value); }
}
private List ages;
public List Ages
{
get { return ages; }
set {SetProperty(ref ages , value); }
}
public MainWindowViewModel()
{
this.Students= new List();
for (int i = 0; i < 20; i++) {
this.Students.Add(new Student()
{
Id = i,
Name = $"张{i}牛",
Age = (i % 10) + 10,
No = i.ToString().PadLeft(4, '0'),
});
}
this.Nos= new List();
this.Names= new List();
this.Ages= new List();
this.Students.ForEach(s => {
this.Nos.Add(new FilterInfo() { FilterText=s.No,IsChecked=false });
this.Names.Add(new FilterInfo() { FilterText = s.Name, IsChecked = false });
this.Ages.Add(new FilterInfo() { FilterText = s.Age.ToString(), IsChecked = false });
});
this.Ages=this.Ages.Distinct().ToList();//去重
}
#endregion
}
}
本示例为了简化实现,筛选功能处理主要在cs后端实现,如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DemoDataGrid
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
private MainWindowViewModel viewModel;
public MainWindow()
{
InitializeComponent();
viewModel = new MainWindowViewModel();
this.DataContext = viewModel;
}
#region 筛选
private void TextBlock_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender != null && sender is TextBlock)
{
var textBlock = sender as TextBlock;
var tag = textBlock.Tag.ToString();
var pop = this.FindName($"popup{tag}");
if (pop != null)
{
var popup = pop as System.Windows.Controls.Primitives.Popup;
if (popup != null)
{
popup.IsOpen = true;
popup.PlacementTarget = textBlock;
popup.Placement = System.Windows.Controls.Primitives.PlacementMode.RelativePoint;
popup.VerticalOffset = 10;
popup.HorizontalOffset = 10;
}
}
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = e.OriginalSource as TextBox;
var tag = textBox.Tag;//条件
var text = textBox.Text;
if (tag != null)
{
if (tag.ToString() == "No")
{
Filter(this.lbNos.ItemsSource, this.txtNo.Text);
}
if (tag.ToString() == "Name")
{
Filter(this.lbNames.ItemsSource, this.txtName.Text);
}
if (tag.ToString() == "Age")
{
Filter(this.lbAges.ItemsSource, this.txtAge.Text);
}
}
}
private void Filter(object source, string filter)
{
var cv = CollectionViewSource.GetDefaultView(source);
if (cv != null && cv.CanFilter)
{
cv.Filter = new Predicate
学号,姓名,年龄三列过滤列表绑定内容模型一致,为FilterInfo,如下所示:
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DemoDataGrid
{
public class FilterInfo : ObservableObject
{
private string filterText;
public string FilterText
{
get { return filterText; }
set { SetProperty(ref filterText, value); }
}
private bool isChecked;
public bool IsChecked
{
get { return isChecked; }
set { SetProperty(ref isChecked, value); }
}
}
}
上述筛选实现方式,并非唯一实现,也并非最优实现,同样存在许多可以优化的地方。
在本示例中,存在许多冗余代码,如视图页面,对三列的弹出窗口,内容虽然相对统一,只是列名和绑定内容不同而已,却堆积了三大段代码,是否可以从控件模块或者数据模板的角度,进行简化呢?
筛选功能实现上,同样存在许多冗余代码,是否可以进行简化呢?以上是我们需要思考的地方,希望可以集思广益,共同学习,一起进步。