WPF实现带筛选功能的DataGrid

作者:小六公子 时间:2022-05-07 07:48:56 

涉及知识点

在本示例中,从数据绑定,到数据展示,涉及知识点如下所示:

  • DataGrid,要WPF提供的进行二维数据展示在列表控件,默认功能非常简单,但是可以通过数据模板或者控件模板进行扩展和美化,可伸缩性很强。

  • MVVM,是Model-View-ViewModel的简写,主要进行数据和UI进行前后端分离,在本示例中,主要用到的MVVM第三方库为CommunityToolkit.Mvvm,大大简化原生MVVM的实现方式。

  • 集合视图, 要对 DataGrid 中的数据进行分组、排序和筛选,可以将其绑定到支持这些函数的 CollectionView。 然后,可以在不影响基础源数据的情况下处理 CollectionView 中的数据。 集合视图中的更改反映在 DataGrid 用户界面 (UI) 中。

  • Popup控件,直接继承FrameworkElement,提供了一种在单独的窗口中显示内容的方法,该窗口相对于指定的元素或屏幕坐标,浮动在当前Popup应用程序窗口上,可用于悬浮窗口。

示例截图

本示例主要模仿Excel的筛选功能进行实现,右键标题栏打开浮动窗口,悬浮于标题栏下方,既可以通过文本框进行筛选,也可以通过筛选按钮弹出右键菜单,选择具体筛选方式,截图如下所示:

WPF实现带筛选功能的DataGrid

选择筛选方式,弹出窗口,如下所示:

WPF实现带筛选功能的DataGrid

输入筛选条件,点击确定,或者取消筛选。如筛选学号里面包含2的,效果如下所示:

WPF实现带筛选功能的DataGrid

 注意:以上筛选都是客户端筛选,不会修改数据源,也不会重连数据库。

核心源码

在本示例中,核心源码主要包含以下几个部分:

前端视图【MainWindow.xaml】源码

主要实现了按学号,姓名,年龄三列进行筛选,既可以单列筛选,又可以组合筛选。且三列的筛选实现方式一致,仅是绑定列有差异。

<Window x:Class="DemoDataGrid.MainWindow"
       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:DemoDataGrid"
       mc:Ignorable="d"
       Title="DataGrid筛选示例" Height="650" Width="800">
   <Window.Resources>
       <ResourceDictionary>
           <CollectionViewSource x:Key="ItemsSource" Source="{Binding Path=Students}"></CollectionViewSource>
           <CollectionViewSource x:Key="Names" Source="{Binding Path=Names}"></CollectionViewSource>
           <CollectionViewSource x:Key="Nos" Source="{Binding Path=Nos}"></CollectionViewSource>
           <CollectionViewSource x:Key="Ages" Source="{Binding Path=Ages}"></CollectionViewSource>
           <Style x:Key="ListBoxStyle" TargetType="{x:Type ListBox}">
               <Setter Property="ScrollViewer.CanContentScroll" Value="True"></Setter>
               <Setter Property="Template">
                   <Setter.Value>
                       <ControlTemplate TargetType="{x:Type ListBox}">
                           <ScrollViewer x:Name="ScrollViewer" CanContentScroll="True">
                               <ItemsPresenter></ItemsPresenter>
                           </ScrollViewer>
                       </ControlTemplate>
                   </Setter.Value>
               </Setter>
           </Style>
           <Geometry x:Key="Icon_Filter">
               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
           </Geometry>
           <ContextMenu x:Key="queryConditionMenu" MouseLeave="ContextMenu_MouseLeave" MenuItem.Click="ContextMenu_Click">
               <MenuItem FontSize="12" Header="等于" Tag="Equal"></MenuItem>
               <MenuItem FontSize="12" Header="不等于"  Tag="NotEqual"></MenuItem>
               <MenuItem FontSize="12" Header="开头"  Tag="Begin"></MenuItem>
               <MenuItem FontSize="12" Header="结尾"  Tag="End"></MenuItem>
               <MenuItem FontSize="12" Header="包含"  Tag="In"></MenuItem>
               <MenuItem FontSize="12" Header="不包含"  Tag="NotIn"></MenuItem>
           </ContextMenu>
       </ResourceDictionary>

</Window.Resources>
   <Grid Margin="10">
       <Grid.RowDefinitions>
           <RowDefinition Height="20"></RowDefinition>
           <RowDefinition Height="*"></RowDefinition>
       </Grid.RowDefinitions>
       <DataGrid Grid.Row="1" x:Name="dgStudents" ItemsSource="{Binding Source={StaticResource ItemsSource} }" AutoGenerateColumns="False"
                             CanUserReorderColumns="True" CanUserDeleteRows="False" CanUserAddRows="False" HeadersVisibility="Column"
                             CanUserSortColumns="True"
                             VirtualizingPanel.VirtualizationMode="Recycling"
                             EnableColumnVirtualization="True" VirtualizingPanel.IsVirtualizingWhenGrouping="True" GridLinesVisibility="All" RowHeight="25"
                             SelectionUnit="FullRow" SelectionMode="Single" IsReadOnly="True" FontSize="12"
                             SelectedIndex="{Binding SelectTaskItemIndex}" SelectedItem="{Binding SelectTaskItem}"
                             CanUserResizeColumns="True">
           <DataGrid.Columns>
               <DataGridTextColumn Binding="{Binding Id}" Header="Id" Width="*">

</DataGridTextColumn>
               <DataGridTextColumn Binding="{Binding No}" Width="*">
                   <DataGridTextColumn.Header>
                       <TextBlock Text="学号" FontWeight="Regular" MouseRightButtonDown="TextBlock_MouseRightButtonDown" Tag="No"></TextBlock>
                   </DataGridTextColumn.Header>
               </DataGridTextColumn>
               <DataGridTextColumn Binding="{Binding Name}" Width="*">
                   <DataGridTextColumn.Header>
                       <TextBlock Text="姓名" FontWeight="Regular" MouseRightButtonDown="TextBlock_MouseRightButtonDown" Tag="Name"></TextBlock>
                   </DataGridTextColumn.Header>
               </DataGridTextColumn>
               <DataGridTextColumn Binding="{Binding Age}" Width="*">
                   <DataGridTextColumn.Header>
                       <TextBlock Text="年龄" FontWeight="Regular" MouseRightButtonDown="TextBlock_MouseRightButtonDown" Tag="Age"></TextBlock>
                   </DataGridTextColumn.Header>
               </DataGridTextColumn>
           </DataGrid.Columns>
       </DataGrid>
       <Popup x:Name="popupNo" Width="135" MaxHeight="500" Height="Auto" PopupAnimation="Slide" AllowsTransparency="False" MouseLeave="popup_MouseLeave">
           <Border BorderBrush="LightBlue" BorderThickness="1" Padding="5" Background="AliceBlue">
               <Grid>
                   <Grid.ColumnDefinitions>
                       <ColumnDefinition></ColumnDefinition>
                       <ColumnDefinition></ColumnDefinition>
                   </Grid.ColumnDefinitions>
                   <Grid.RowDefinitions>
                       <RowDefinition></RowDefinition>
                       <RowDefinition></RowDefinition>
                       <RowDefinition></RowDefinition>
                   </Grid.RowDefinitions>
                   <StackPanel Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal">
                       <TextBox Height="25" x:Name="txtNo" MinWidth="60" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Tag="No" TextChanged="TextBox_TextChanged"></TextBox>
                       <Button x:Name="btnNoFilter"  Tag="No" ClickMode="Press" Click="ButtonFilter_Click" ContextMenu="{StaticResource queryConditionMenu}">
                           <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                               <Path Data="{StaticResource Icon_Filter}" Stroke="Gray" StrokeThickness="1"
                      Height="12" Width="12" Stretch="Fill"></Path>
                               <TextBlock Margin="2,0" Text="筛选" FontSize="12"></TextBlock>
                           </StackPanel>
                       </Button>
                   </StackPanel>
                   <ListBox x:Name="lbNos" ItemsSource="{Binding Source={StaticResource Nos}}" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" VirtualizingPanel.VirtualizationMode="Recycling" VirtualizingPanel.IsVirtualizing="True" Style="{StaticResource ListBoxStyle}">
                       <ListBox.ItemTemplate>
                           <DataTemplate>
                               <CheckBox Content="{Binding FilterText}" IsChecked="{Binding IsChecked}"></CheckBox>
                           </DataTemplate>
                       </ListBox.ItemTemplate>
                   </ListBox>

<Button Tag="No" Content="取消" Width="60" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="0" Click="btnCancel_Click"></Button>
                   <Button Content="确定" Width="60" HorizontalAlignment="Right" Grid.Row="2" Grid.Column="1" Click="btnOk_Click"></Button>
               </Grid>
           </Border>
       </Popup>
       <Popup x:Name="popupName" Width="135" MaxHeight="500" Height="Auto" PopupAnimation="Slide" AllowsTransparency="False" MouseLeave="popup_MouseLeave">
           <Border BorderBrush="LightBlue" BorderThickness="1" Padding="5" Background="AliceBlue">
               <Grid>
                   <Grid.ColumnDefinitions>
                       <ColumnDefinition></ColumnDefinition>
                       <ColumnDefinition></ColumnDefinition>
                   </Grid.ColumnDefinitions>
                   <Grid.RowDefinitions>
                       <RowDefinition></RowDefinition>
                       <RowDefinition></RowDefinition>
                       <RowDefinition></RowDefinition>
                   </Grid.RowDefinitions>
                   <StackPanel Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal">
                       <TextBox Height="25" x:Name="txtName" MinWidth="60" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Tag="Name" TextChanged="TextBox_TextChanged"></TextBox>
                       <Button x:Name="btnNameFilter"  Tag="Name" Click="ButtonFilter_Click" ContextMenu="{StaticResource queryConditionMenu}">
                           <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                               <Path Data="{StaticResource Icon_Filter}" Stroke="Gray" StrokeThickness="1"
                      Height="12" Width="12" Stretch="Fill"></Path>
                               <TextBlock Margin="2,0" Text="筛选" FontSize="12"></TextBlock>
                           </StackPanel>
                       </Button>
                   </StackPanel>
                   <ListBox x:Name="lbNames" ItemsSource="{Binding Source={StaticResource Names}}" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" VirtualizingPanel.VirtualizationMode="Recycling" VirtualizingPanel.IsVirtualizing="True" Style="{StaticResource ListBoxStyle}">
                       <ListBox.ItemTemplate>
                           <DataTemplate>
                               <CheckBox Content="{Binding FilterText}" IsChecked="{Binding IsChecked}"></CheckBox>
                           </DataTemplate>
                       </ListBox.ItemTemplate>
                   </ListBox>

<Button Tag="No" Content="取消" Width="60" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="0" Click="btnCancel_Click"></Button>
                   <Button Content="确定" Width="60" HorizontalAlignment="Right" Grid.Row="2" Grid.Column="1" Click="btnOk_Click"></Button>
               </Grid>
           </Border>
       </Popup>
       <Popup x:Name="popupAge" Width="135" MaxHeight="500" Height="Auto" PopupAnimation="Slide" AllowsTransparency="False" MouseLeave="popup_MouseLeave">
           <Border BorderBrush="LightBlue" BorderThickness="1" Padding="5" Background="AliceBlue">
               <Grid>
                   <Grid.ColumnDefinitions>
                       <ColumnDefinition></ColumnDefinition>
                       <ColumnDefinition></ColumnDefinition>
                   </Grid.ColumnDefinitions>
                   <Grid.RowDefinitions>
                       <RowDefinition></RowDefinition>
                       <RowDefinition></RowDefinition>
                       <RowDefinition></RowDefinition>
                   </Grid.RowDefinitions>
                   <StackPanel Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal">
                       <TextBox Height="25" x:Name="txtAge" MinWidth="60" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Tag="Age" TextChanged="TextBox_TextChanged"></TextBox>
                       <Button x:Name="btnAgeFilter"  Tag="Age" Click="ButtonFilter_Click" ContextMenu="{StaticResource queryConditionMenu}">
                           <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                               <Path Data="{StaticResource Icon_Filter}" Stroke="Gray" StrokeThickness="1"
                      Height="12" Width="12" Stretch="Fill"></Path>
                               <TextBlock Margin="2,0" Text="筛选" FontSize="12"></TextBlock>
                           </StackPanel>
                       </Button>
                   </StackPanel>
                   <ListBox x:Name="lbAges" ItemsSource="{Binding Source={StaticResource Ages}}" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" VirtualizingPanel.VirtualizationMode="Recycling" VirtualizingPanel.IsVirtualizing="True" Style="{StaticResource ListBoxStyle}">
                       <ListBox.ItemTemplate>
                           <DataTemplate>
                               <CheckBox Content="{Binding FilterText}" IsChecked="{Binding IsChecked}"></CheckBox>
                           </DataTemplate>
                       </ListBox.ItemTemplate>
                   </ListBox>

<Button Tag="No" Content="取消" Width="60" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="0" Click="btnCancel_Click"></Button>
                   <Button Content="确定" Width="60" HorizontalAlignment="Right" Grid.Row="2" Grid.Column="1" Click="btnOk_Click"></Button>
               </Grid>
           </Border>
       </Popup>

<Popup x:Name="popupNoMenu" Width="300" MaxHeight="500" Height="200" PopupAnimation="Slide" AllowsTransparency="False" Tag="">
           <Border BorderThickness="1" BorderBrush="Beige" Padding="15" Background="AliceBlue">
               <Grid>
                   <Grid.ColumnDefinitions>
                       <ColumnDefinition></ColumnDefinition>
                       <ColumnDefinition></ColumnDefinition>
                   </Grid.ColumnDefinitions>
                   <Grid.RowDefinitions>
                       <RowDefinition Height="Auto"></RowDefinition>
                       <RowDefinition Height="Auto"></RowDefinition>
                       <RowDefinition Height="Auto"></RowDefinition>
                       <RowDefinition></RowDefinition>
                   </Grid.RowDefinitions>
                   <StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
                       <TextBlock Text="学号" VerticalAlignment="Center"></TextBlock>
                       <ComboBox x:Name="combNoMenu1" Height="28" Width="100" Margin="4" VerticalContentAlignment="Center">
                           <ComboBoxItem Content="等于" ></ComboBoxItem>
                           <ComboBoxItem Content="不等于"></ComboBoxItem>
                           <ComboBoxItem Content="开头"></ComboBoxItem>
                           <ComboBoxItem Content="结尾"></ComboBoxItem>
                           <ComboBoxItem Content="包含"></ComboBoxItem>
                           <ComboBoxItem Content="不包含"></ComboBoxItem>
                       </ComboBox>
                       <TextBox x:Name="txtNoMenu1" Height="28" Width="100" VerticalContentAlignment="Center"></TextBox>
                   </StackPanel>
                   <StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="0"  Grid.ColumnSpan="2">
                       <RadioButton x:Name="rbNoAnd" Content="与" IsChecked="True" Margin="4" Background="AliceBlue" VerticalAlignment="Center" VerticalContentAlignment="Center"></RadioButton>
                       <RadioButton x:Name="rbNoOr" Content="或" Background="AliceBlue" VerticalAlignment="Center" VerticalContentAlignment="Center"></RadioButton>
                   </StackPanel>
                   <StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="0"  Grid.ColumnSpan="2">
                       <TextBlock Text="学号" VerticalAlignment="Center"></TextBlock>
                       <ComboBox x:Name="combNoMenu2" Height="28" Margin="4" Width="100" VerticalContentAlignment="Center">
                           <ComboBoxItem Content="等于" ></ComboBoxItem>
                           <ComboBoxItem Content="不等于"></ComboBoxItem>
                           <ComboBoxItem Content="开头"></ComboBoxItem>
                           <ComboBoxItem Content="结尾"></ComboBoxItem>
                           <ComboBoxItem Content="包含"></ComboBoxItem>
                           <ComboBoxItem Content="不包含"></ComboBoxItem>
                       </ComboBox>
                       <TextBox x:Name="txtNoMenu2" Height="28" Width="100" VerticalContentAlignment="Center"></TextBox>
                   </StackPanel>
                   <Button Tag="No" Content="取消" Width="100" Height="28" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="0" Click="btnCancelFilter_Click"></Button>
                   <Button Tag="No" Content="确定" Width="100" Height="28" HorizontalAlignment="Right" Grid.Row="3" Grid.Column="1" Click="btnOkFilter_Click"></Button>
               </Grid>
           </Border>
       </Popup>
       <Popup x:Name="popupNameMenu" Width="300" MaxHeight="500" Height="200" PopupAnimation="Slide" AllowsTransparency="False" Tag="">
           <Border BorderThickness="1" BorderBrush="Beige" Padding="15" Background="AliceBlue">
               <Grid>
                   <Grid.RowDefinitions>
                       <RowDefinition Height="Auto"></RowDefinition>
                       <RowDefinition Height="Auto"></RowDefinition>
                       <RowDefinition Height="Auto"></RowDefinition>
                       <RowDefinition Height="Auto"></RowDefinition>
                   </Grid.RowDefinitions>
                   <StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0">
                       <TextBlock Text="姓名" VerticalAlignment="Center"></TextBlock>
                       <ComboBox x:Name="combNameMenu1" Height="28" Width="100" Margin="4" VerticalContentAlignment="Center">
                           <ComboBoxItem Content="等于" ></ComboBoxItem>
                           <ComboBoxItem Content="不等于"></ComboBoxItem>
                           <ComboBoxItem Content="开头"></ComboBoxItem>
                           <ComboBoxItem Content="结尾"></ComboBoxItem>
                           <ComboBoxItem Content="包含"></ComboBoxItem>
                           <ComboBoxItem Content="不包含"></ComboBoxItem>
                       </ComboBox>
                       <TextBox x:Name="txtNameMenu1" Height="28" Width="100" VerticalContentAlignment="Center"></TextBox>
                   </StackPanel>
                   <StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="0">
                       <RadioButton x:Name="rbNameAnd" Content="与" IsChecked="True" Margin="4" Background="AliceBlue" VerticalAlignment="Center" VerticalContentAlignment="Center"></RadioButton>
                       <RadioButton x:Name="rbNameOr" Content="或" Background="AliceBlue" VerticalAlignment="Center" VerticalContentAlignment="Center"></RadioButton>
                   </StackPanel>
                   <StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="0">
                       <TextBlock Text="姓名" VerticalAlignment="Center"></TextBlock>
                       <ComboBox x:Name="combNameMenu2" Height="28" Width="100" Margin="4" VerticalContentAlignment="Center">
                           <ComboBoxItem Content="等于" ></ComboBoxItem>
                           <ComboBoxItem Content="不等于"></ComboBoxItem>
                           <ComboBoxItem Content="开头"></ComboBoxItem>
                           <ComboBoxItem Content="结尾"></ComboBoxItem>
                           <ComboBoxItem Content="包含"></ComboBoxItem>
                           <ComboBoxItem Content="不包含"></ComboBoxItem>
                       </ComboBox>
                       <TextBox x:Name="txtNameMenu2" Height="28" Width="100" VerticalContentAlignment="Center"></TextBox>
                   </StackPanel>
                   <Button Tag="Name" Content="取消" Width="100" Height="28" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="0" Click="btnCancelFilter_Click"></Button>
                   <Button Tag="Name" Content="确定" Width="100" Height="28" HorizontalAlignment="Right" Grid.Row="3" Grid.Column="1" Click="btnOkFilter_Click"></Button>
               </Grid>
           </Border>
       </Popup>
       <Popup x:Name="popupAgeMenu" Width="300" MaxHeight="500" Height="200" PopupAnimation="Slide" AllowsTransparency="False" Tag="">
           <Border BorderThickness="1" BorderBrush="Beige" Padding="15" Background="AliceBlue">
               <Grid>
                   <Grid.RowDefinitions>
                       <RowDefinition Height="Auto"></RowDefinition>
                       <RowDefinition Height="Auto"></RowDefinition>
                       <RowDefinition Height="Auto"></RowDefinition>
                       <RowDefinition Height="Auto"></RowDefinition>
                   </Grid.RowDefinitions>
                   <StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0">
                       <TextBlock Text="年龄" VerticalAlignment="Center"></TextBlock>
                       <ComboBox x:Name="combAgeMenu1" Height="28" Width="100" Margin="4" VerticalContentAlignment="Center">
                           <ComboBoxItem Content="等于" ></ComboBoxItem>
                           <ComboBoxItem Content="不等于"></ComboBoxItem>
                           <ComboBoxItem Content="开头"></ComboBoxItem>
                           <ComboBoxItem Content="结尾"></ComboBoxItem>
                           <ComboBoxItem Content="包含"></ComboBoxItem>
                           <ComboBoxItem Content="不包含"></ComboBoxItem>
                       </ComboBox>
                       <TextBox x:Name="txtAgeMenu1" Height="28" Width="100" VerticalContentAlignment="Center"></TextBox>
                   </StackPanel>
                   <StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="0">
                       <RadioButton x:Name="rbAgeAnd" Content="与"  IsChecked="True" Margin="4" Background="AliceBlue" VerticalAlignment="Center" VerticalContentAlignment="Center"></RadioButton>
                       <RadioButton x:Name="rbAgeOr" Content="或" Background="AliceBlue" VerticalAlignment="Center" VerticalContentAlignment="Center"></RadioButton>
                   </StackPanel>
                   <StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="0">
                       <TextBlock Text="年龄" VerticalAlignment="Center"></TextBlock>
                       <ComboBox x:Name="combAgeMenu2" Height="28" Width="100" Margin="4" VerticalContentAlignment="Center">
                           <ComboBoxItem Content="等于" ></ComboBoxItem>
                           <ComboBoxItem Content="不等于"></ComboBoxItem>
                           <ComboBoxItem Content="开头"></ComboBoxItem>
                           <ComboBoxItem Content="结尾"></ComboBoxItem>
                           <ComboBoxItem Content="包含"></ComboBoxItem>
                           <ComboBoxItem Content="不包含"></ComboBoxItem>
                       </ComboBox>
                       <TextBox x:Name="txtAgeMenu2" Height="28" Width="100" VerticalContentAlignment="Center"></TextBox>
                   </StackPanel>
                   <Button Tag="Age" Content="取消" Width="100" Height="28" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="0" Click="btnCancelFilter_Click"></Button>
                   <Button Tag="Age" Content="确定" Width="100" Height="28" HorizontalAlignment="Right" Grid.Row="3" Grid.Column="1" Click="btnOkFilter_Click"></Button>
               </Grid>
           </Border>
       </Popup>
   </Grid>
</Window>

业务逻辑【MainWindowViewModel】

业务逻辑处理主要复责数据初始化等业务相关内容,和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<Student> students;

public List<Student> Students
       {
           get { return students; }
           set { SetProperty(ref students, value); }
       }

private List<FilterInfo> names;

public List<FilterInfo> Names
       {
           get { return names; }
           set { SetProperty(ref names, value); }
       }

private List<FilterInfo> nos;

public List<FilterInfo> Nos
       {
           get { return nos; }
           set {SetProperty(ref nos , value); }
       }

private List<FilterInfo> ages;

public List<FilterInfo> Ages
       {
           get { return ages; }
           set {SetProperty(ref ages , value); }
       }

public MainWindowViewModel()
       {
           this.Students= new List<Student>();
           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<FilterInfo>();
           this.Names= new List<FilterInfo>();
           this.Ages= new List<FilterInfo>();
           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

}
}

筛选功能实现【MainWindow.xaml.cs】

本示例为了简化实现,筛选功能处理主要在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
{
   /// <summary>
   /// Interaction logic for MainWindow.xaml
   /// </summary>
   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<object>((obj) => {
                   bool flag = true;
                   var t = obj as FilterInfo;
                   if (t != null)
                   {
                       flag = t.FilterText.Contains(filter);
                   }
                   return flag;
               });
           }
       }

private void popup_MouseLeave(object sender, MouseEventArgs e)
       {
           var popup = e.OriginalSource as System.Windows.Controls.Primitives.Popup;
           var showContext = (this.FindResource("queryConditionMenu") as ContextMenu)?.IsOpen;
           if (popup != null && showContext==false)
           {
               popup.IsOpen = false;
           }
       }

private void btnCancel_Click(object sender, RoutedEventArgs e)
       {
           var btn = e.OriginalSource as Button;
           if (btn != null)
           {
               var tag = btn.Tag;
               if (tag.ToString() == "No")
               {
                   ClearFilter(this.txtNo, this.viewModel.Nos);
               }
               if (tag.ToString() == "Name")
               {
                   ClearFilter(this.txtName, this.viewModel.Names);

}
               if (tag.ToString() == "Age")
               {
                   ClearFilter(this.txtAge, this.viewModel.Ages);
               }
               FilterTask();//清除以后,重新刷新
           }
       }

private void ClearFilter(TextBox textBox, List<FilterInfo> collection)
       {
           textBox.Clear();
           foreach (var f in collection)
           {
               f.IsChecked = false;
           }
       }

private void btnOk_Click(object sender, RoutedEventArgs e)
       {
           //
           FilterTask();
       }

private void FilterTask()
       {
           var cv = CollectionViewSource.GetDefaultView(this.dgStudents.ItemsSource);
           if (cv != null && cv.CanFilter)
           {
               cv.Filter = new Predicate<object>((obj) =>
               {
                   bool flag = true;
                   var t = obj as Student;
                   if (t != null)
                   {
                       var nos = this.viewModel.Nos.Where(r => r.IsChecked == true).ToList();
                       var names = this.viewModel.Names.Where(r => r.IsChecked == true).ToList();
                       var ages = this.viewModel.Ages.Where(r => r.IsChecked == true).ToList();
                       if (nos.Count() > 0)
                       {
                           flag = flag && nos.Select(r => r.FilterText).Contains(t.No);
                       }
                       if (names.Count() > 0)
                       {
                           flag = flag && names.Select(r => r.FilterText).Contains(t.Name);
                       }
                       if (ages.Count() > 0)
                       {
                           flag = flag && ages.Select(r => r.FilterText).Contains(t.Age.ToString());
                       }
                   }
                   return flag;
               });
           }
       }

#endregion

private List<string> condition = new List<string>() { "Equal", "NotEqual", "Begin", "End", "In", "NotIn" };

private void ButtonFilter_Click(object sender, RoutedEventArgs e)
       {
           var btn = e.OriginalSource as Button;
           if (btn != null)
           {
               var tag = btn.Tag;
               var popup = this.FindName($"popup{tag}") as System.Windows.Controls.Primitives.Popup;
               if (popup != null)
               {
                   popup.IsOpen = true;
               }
               if (btn.ContextMenu.IsOpen)
               {
                   btn.ContextMenu.IsOpen = false;
               }
               else
               {
                   btn.ContextMenu.Tag = tag;
                   btn.ContextMenu.Width = 100;
                   btn.ContextMenu.Height = 150;
                   btn.ContextMenu.IsOpen = true;
                   btn.ContextMenu.PlacementTarget = btn;
                   btn.ContextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom;
               }
           }
       }

private void ContextMenu_MouseLeave(object sender, MouseEventArgs e)
       {
           var menu = e.OriginalSource as ContextMenu;
           if (menu != null)
           {
               menu.IsOpen = false;
           }
       }

private void ContextMenu_Click(object sender, RoutedEventArgs e)
       {
           var contextMenu = sender as ContextMenu;
           if (contextMenu == null)
           {
               return;
           }
           var menuItem = e.OriginalSource as MenuItem;
           if (menuItem == null)
           {
               return;
           }
           var tag1 = contextMenu.Tag.ToString();//点击的哪一个按钮
           var tag2 = menuItem.Tag.ToString();//点击的是哪一个菜单
           var pop = this.FindName($"popup{tag1}Menu");
           var comb = this.FindName($"comb{tag1}Menu1");
           HideParentPopup(tag1);//隐藏父Popup
           if (comb != null)
           {
               var combMenu = comb as ComboBox;
               combMenu.SelectedIndex = condition.IndexOf(tag2);
           }
           if (pop != null)
           {
               var popup = pop as System.Windows.Controls.Primitives.Popup;
               popup.IsOpen = true;
               popup.PlacementTarget = dgStudents;
               popup.Placement = System.Windows.Controls.Primitives.PlacementMode.Center;
           }
       }

private void btnCancelFilter_Click(object sender, RoutedEventArgs e)
       {
           if (sender == null)
           {
               return;
           }
           var btn = sender as System.Windows.Controls.Button;
           if (btn != null)
           {
               var tag = btn.Tag.ToString();
               HidePopupMenu(tag);//隐藏Popup控件
               if (tag == "No")
               {
                   ClearMenuFilter(this.txtNoMenu1, this.txtNoMenu2);
               }
               if (tag == "Name")
               {
                   ClearMenuFilter(this.txtNameMenu1, this.txtNameMenu2);
               }
               if (tag == "Age")
               {
                   ClearMenuFilter(this.txtAgeMenu1, this.txtAgeMenu2);
               }
               FilterMenuTask();
           }
       }

private void btnOkFilter_Click(object sender, RoutedEventArgs e)
       {
           if (sender == null)
           {
               return;
           }
           var btn = sender as System.Windows.Controls.Button;
           if (btn != null)
           {
               var tag = btn.Tag.ToString();
               HidePopupMenu(tag);
               FilterMenuTask();
           }
       }

/// <summary>
       /// 隐藏父Popup
       /// </summary>
       /// <param name="tag"></param>
       private void HideParentPopup(string tag)
       {
           //点击右键菜单时,隐藏父Popup控件
           if (tag == "No")
           {
               this.popupNo.IsOpen = false;
           }
           if (tag == "Name")
           {
               this.popupName.IsOpen = false;
           }
           if (tag == "Age")
           {
               this.popupAge.IsOpen = false;
           }
       }

/// <summary>
       /// 隐藏菜单弹出的Popup控件
       /// </summary>
       /// <param name="tag"></param>
       private void HidePopupMenu(string tag)
       {
           var pop = this.FindName($"popup{tag}Menu");
           if (pop != null)
           {
               var popup = pop as System.Windows.Controls.Primitives.Popup;
               popup.IsOpen = false;
           }
       }

/// <summary>
       /// 清除菜单中的文本过滤条件
       /// </summary>
       /// <param name="txt1"></param>
       /// <param name="txt2"></param>
       private void ClearMenuFilter(TextBox txt1, TextBox txt2)
       {
           txt1?.Clear();
           txt2?.Clear();
       }

/// <summary>
       ///
       /// </summary>
       private void FilterMenuTask()
       {
           var cv = CollectionViewSource.GetDefaultView(this.dgStudents.ItemsSource);
           if (cv != null && cv.CanFilter)
           {
               cv.Filter = new Predicate<object>((obj) =>
               {
                   bool flag = true;
                   var t = obj as Student;
                   if (t != null)
                   {
                       string noText1 = this.txtNoMenu1.Text.Trim();
                       string noText2 = this.txtNoMenu2.Text.Trim();
                       int noConditionType1 = this.combNoMenu1.SelectedIndex;
                       int noConditionType2 = this.combNoMenu2.SelectedIndex;
                       string nameText1 = this.txtNameMenu1.Text.Trim();
                       string nameText2 = this.txtNameMenu2.Text.Trim();
                       int nameConditionType1 = this.combNameMenu1.SelectedIndex;
                       int nameConditionType2 = this.combNameMenu2.SelectedIndex;
                       string ageText1 = this.txtAgeMenu1.Text.Trim();
                       string ageText2 = this.txtAgeMenu2.Text.Trim();
                       int ageConditionType1 = this.combAgeMenu1.SelectedIndex;
                       int ageConditionType2 = this.combAgeMenu2.SelectedIndex;
                       bool? isNoAnd = this.rbNoAnd.IsChecked;
                       bool? isNoOr = this.rbNoOr.IsChecked;
                       bool? isNameAnd = this.rbNameAnd.IsChecked;
                       bool? isNameOr = this.rbNameOr.IsChecked;
                       bool? isAgeAnd = this.rbAgeAnd.IsChecked;
                       bool? isAgeOr = this.rbAgeOr.IsChecked;
                       bool flagNo = true;
                       bool flagName = true;
                       bool flagAge = true;
                       flagNo = CheckConditions(noConditionType1, noConditionType2, t.No, noText1, noText2, isNoAnd, isNoOr);
                       flagName = CheckConditions(nameConditionType1, nameConditionType2, t.Name, nameText1, nameText2, isNameAnd, isNameOr);
                       flagAge = CheckConditions(ageConditionType1, ageConditionType2, t.Age.ToString(), ageText1, ageText2, isAgeAnd, isAgeOr);
                       flag = flag && flagNo && flagName && flagAge;
                   }
                   return flag;
               });
           }
       }

private bool CheckConditions(int conditionIndex1, int conditionIndex2, string source, string condition1, string condition2, bool? isAnd, bool? isOr)
       {
           bool flag = true;
           bool flag1 = true;
           bool flag2 = true;
           if (!string.IsNullOrEmpty(condition1) && !string.IsNullOrWhiteSpace(condition1) && conditionIndex1 != -1)
           {
               flag1 = CheckCondition(conditionIndex1, source, condition1);
           }
           if (!string.IsNullOrEmpty(condition2) && !string.IsNullOrWhiteSpace(condition2) && conditionIndex2 != -1)
           {
               flag2 = CheckCondition(conditionIndex2, source, condition2);
           }
           if (isAnd == true)
           {
               flag = flag1 && flag2;
           }
           if (isOr == true)
           {
               flag = flag1 || flag2;
           }
           return flag;
       }

private bool CheckCondition(int condtionIndex, string source, string condition)
       {
           bool flag = true;
           if (condtionIndex == 0)
           {
               flag = flag && source == condition;
           }
           if (condtionIndex == 1)
           {
               flag = flag && source != condition;
           }
           if (condtionIndex == 2)
           {
               flag = flag && source.StartsWith(condition);
           }
           if (condtionIndex == 3)
           {
               flag = flag && source.EndsWith(condition);
           }
           if (condtionIndex == 4)
           {
               flag = flag && source.Contains(condition);
           }
           if (condtionIndex == 5)
           {
               flag = flag && !source.Contains(condition);
           }
           return flag;
       }
   }
}

学号,姓名,年龄三列过滤列表绑定内容模型一致,为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); }
       }
   }
}

不足与思考

上述筛选实现方式,并非唯一实现,也并非最优实现,同样存在许多可以优化的地方。

在本示例中,存在许多冗余代码,如视图页面,对三列的弹出窗口,内容虽然相对统一,只是列名和绑定内容不同而已,却堆积了三大段代码,是否可以从控件模块或者数据模板的角度,进行简化呢?

筛选功能实现上,同样存在许多冗余代码,是否可以进行简化呢?以上是我们需要思考的地方,希望可以集思广益,共同学习,一起进步。

来源:https://www.cnblogs.com/hsiang/p/17162319.html

标签:WPF,DataGrid,筛选
0
投稿

猜你喜欢

  • java通过ip获取客户端Mac地址的小例子

    2021-12-22 06:37:07
  • 详解Java变量与常量

    2023-11-09 21:00:14
  • springboot大文件上传、分片上传、断点续传、秒传的实现

    2023-06-16 02:18:30
  • OpenGL实现Bezier曲线的方法示例

    2023-06-30 05:06:22
  • Android AIDL实现两个APP间的跨进程通信实例

    2022-03-29 08:37:54
  • Spring Boot启动时调用自己的非web逻辑

    2022-02-15 11:40:10
  • Android 动态加载二维码视图生成快照的示例

    2023-08-04 19:57:54
  • 教你用JAVA写文本编辑器(四)

    2023-10-18 18:30:16
  • C#基于Socket实现多人聊天功能

    2021-12-06 12:00:47
  • Android 7.0应用之间如何共享文件

    2021-09-18 08:43:15
  • Android百度地图应用之MapFragment的使用

    2022-07-07 21:16:37
  • Java编程基本概念

    2023-09-07 07:15:50
  • Java Synchronized锁的使用详解

    2022-10-11 12:59:33
  • 教你如何使用Java8实现菜单树形数据

    2022-09-11 12:29:22
  • C#实现无损压缩图片代码示例

    2022-01-23 19:59:25
  • springboot整合nacos,如何读取nacos配置文件

    2022-12-30 19:55:25
  • Unity 按钮添加OnClick事件操作

    2023-06-28 15:57:27
  • 一文详解kafka序列化器和拦截器

    2023-06-18 01:06:08
  • Java日常练习题,每天进步一点点(52)

    2023-03-31 11:23:46
  • Android项目实现视频播放器

    2022-10-05 07:53:35
  • asp之家 软件编程 m.aspxhome.com