让WPF中的DataGrid像Excel一样可以筛选

  • 让WPF中的DataGrid像Excel一样可以筛选已关闭评论
  • 107 次浏览
  • A+
所属分类:.NET技术
摘要

在默认情况下,WPF提供的DataGrid仅拥有数据展示等简单功能,如果要实现像Excel一样复杂的筛选过滤功能,则相对比较麻烦。本文以一个简单的小例子,简述如何通过WPF实话DataGrid的筛选功能,仅供学习分享使用,如有不足之处,还请指正。

在默认情况下,WPF提供的DataGrid仅拥有数据展示等简单功能,如果要实现像Excel一样复杂的筛选过滤功能,则相对比较麻烦。本文以一个简单的小例子,简述如何通过WPF实话DataGrid的筛选功能,仅供学习分享使用,如有不足之处,还请指正。

涉及知识点

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

  • DataGrid,要WPF提供的进行二维数据展示在列表控件,默认功能非常简单,但是可以通过数据模板或者控件模板进行扩展和美化,可伸缩性很强。
  • MVVM,是Model-View-ViewModel的简写,主要进行数据和UI进行前后端分离,在本示例中,主要用到的MVVM第三方库为CommunityToolkit.Mvvm,大大简化原生MVVM的实现方式。
  • 集合视图, 要对 DataGrid 中的数据进行分组、排序和筛选,可以将其绑定到支持这些函数的 CollectionView。 然后,可以在不影响基础源数据的情况下处理 CollectionView 中的数据。 集合视图中的更改反映在 DataGrid 用户界面 (UI) 中。
  • Popup控件,直接继承FrameworkElement,提供了一种在单独的窗口中显示内容的方法,该窗口相对于指定的元素或屏幕坐标,浮动在当前Popup应用程序窗口上,可用于悬浮窗口。

示例截图

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

让WPF中的DataGrid像Excel一样可以筛选

 

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

让WPF中的DataGrid像Excel一样可以筛选

 

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

让WPF中的DataGrid像Excel一样可以筛选

 

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

核心源码

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

前端视图【MainWindow.xaml】源码

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

  1 <Window x:Class="DemoDataGrid.MainWindow"   2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   6         xmlns:local="clr-namespace:DemoDataGrid"   7         mc:Ignorable="d"   8         Title="DataGrid筛选示例" Height="650" Width="800">   9     <Window.Resources>  10         <ResourceDictionary>  11             <CollectionViewSource x:Key="ItemsSource" Source="{Binding Path=Students}"></CollectionViewSource>  12             <CollectionViewSource x:Key="Names" Source="{Binding Path=Names}"></CollectionViewSource>  13             <CollectionViewSource x:Key="Nos" Source="{Binding Path=Nos}"></CollectionViewSource>  14             <CollectionViewSource x:Key="Ages" Source="{Binding Path=Ages}"></CollectionViewSource>  15             <Style x:Key="ListBoxStyle" TargetType="{x:Type ListBox}">  16                 <Setter Property="ScrollViewer.CanContentScroll" Value="True"></Setter>  17                 <Setter Property="Template">  18                     <Setter.Value>  19                         <ControlTemplate TargetType="{x:Type ListBox}">  20                             <ScrollViewer x:Name="ScrollViewer" CanContentScroll="True">  21                                 <ItemsPresenter></ItemsPresenter>  22                             </ScrollViewer>  23                         </ControlTemplate>  24                     </Setter.Value>  25                 </Setter>  26             </Style>  27             <Geometry x:Key="Icon_Filter">  28                 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  29             </Geometry>  30             <ContextMenu x:Key="queryConditionMenu" MouseLeave="ContextMenu_MouseLeave" MenuItem.Click="ContextMenu_Click">  31                 <MenuItem FontSize="12" Header="等于" Tag="Equal"></MenuItem>  32                 <MenuItem FontSize="12" Header="不等于"  Tag="NotEqual"></MenuItem>  33                 <MenuItem FontSize="12" Header="开头"  Tag="Begin"></MenuItem>  34                 <MenuItem FontSize="12" Header="结尾"  Tag="End"></MenuItem>  35                 <MenuItem FontSize="12" Header="包含"  Tag="In"></MenuItem>  36                 <MenuItem FontSize="12" Header="不包含"  Tag="NotIn"></MenuItem>  37             </ContextMenu>  38         </ResourceDictionary>  39           40     </Window.Resources>  41     <Grid Margin="10">  42         <Grid.RowDefinitions>  43             <RowDefinition Height="20"></RowDefinition>  44             <RowDefinition Height="*"></RowDefinition>  45         </Grid.RowDefinitions>  46         <DataGrid Grid.Row="1" x:Name="dgStudents" ItemsSource="{Binding Source={StaticResource ItemsSource} }" AutoGenerateColumns="False"  47                               CanUserReorderColumns="True" CanUserDeleteRows="False" CanUserAddRows="False" HeadersVisibility="Column"  48                               CanUserSortColumns="True"  49                               VirtualizingPanel.VirtualizationMode="Recycling"  50                               EnableColumnVirtualization="True" VirtualizingPanel.IsVirtualizingWhenGrouping="True" GridLinesVisibility="All" RowHeight="25"  51                               SelectionUnit="FullRow" SelectionMode="Single" IsReadOnly="True" FontSize="12"   52                               SelectedIndex="{Binding SelectTaskItemIndex}" SelectedItem="{Binding SelectTaskItem}"  53                               CanUserResizeColumns="True">  54             <DataGrid.Columns>  55                 <DataGridTextColumn Binding="{Binding Id}" Header="Id" Width="*">  56                       57                 </DataGridTextColumn>  58                 <DataGridTextColumn Binding="{Binding No}" Width="*">  59                     <DataGridTextColumn.Header>  60                         <TextBlock Text="学号" FontWeight="Regular" MouseRightButtonDown="TextBlock_MouseRightButtonDown" Tag="No"></TextBlock>  61                     </DataGridTextColumn.Header>  62                 </DataGridTextColumn>  63                 <DataGridTextColumn Binding="{Binding Name}" Width="*">  64                     <DataGridTextColumn.Header>  65                         <TextBlock Text="姓名" FontWeight="Regular" MouseRightButtonDown="TextBlock_MouseRightButtonDown" Tag="Name"></TextBlock>  66                     </DataGridTextColumn.Header>  67                 </DataGridTextColumn>  68                 <DataGridTextColumn Binding="{Binding Age}" Width="*">  69                     <DataGridTextColumn.Header>  70                         <TextBlock Text="年龄" FontWeight="Regular" MouseRightButtonDown="TextBlock_MouseRightButtonDown" Tag="Age"></TextBlock>  71                     </DataGridTextColumn.Header>  72                 </DataGridTextColumn>  73             </DataGrid.Columns>  74         </DataGrid>  75         <Popup x:Name="popupNo" Width="135" MaxHeight="500" Height="Auto" PopupAnimation="Slide" AllowsTransparency="False" MouseLeave="popup_MouseLeave">  76             <Border BorderBrush="LightBlue" BorderThickness="1" Padding="5" Background="AliceBlue">  77                 <Grid>  78                     <Grid.ColumnDefinitions>  79                         <ColumnDefinition></ColumnDefinition>  80                         <ColumnDefinition></ColumnDefinition>  81                     </Grid.ColumnDefinitions>  82                     <Grid.RowDefinitions>  83                         <RowDefinition></RowDefinition>  84                         <RowDefinition></RowDefinition>  85                         <RowDefinition></RowDefinition>  86                     </Grid.RowDefinitions>  87                     <StackPanel Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal">  88                         <TextBox Height="25" x:Name="txtNo" MinWidth="60" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Tag="No" TextChanged="TextBox_TextChanged"></TextBox>  89                         <Button x:Name="btnNoFilter"  Tag="No" ClickMode="Press" Click="ButtonFilter_Click" ContextMenu="{StaticResource queryConditionMenu}">  90                             <StackPanel Orientation="Horizontal" VerticalAlignment="Center">  91                                 <Path Data="{StaticResource Icon_Filter}" Stroke="Gray" StrokeThickness="1"   92                        Height="12" Width="12" Stretch="Fill"></Path>  93                                 <TextBlock Margin="2,0" Text="筛选" FontSize="12"></TextBlock>  94                             </StackPanel>  95                         </Button>  96                     </StackPanel>  97                     <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}">  98                         <ListBox.ItemTemplate>  99                             <DataTemplate> 100                                 <CheckBox Content="{Binding FilterText}" IsChecked="{Binding IsChecked}"></CheckBox> 101                             </DataTemplate> 102                         </ListBox.ItemTemplate> 103                     </ListBox> 104  105                     <Button Tag="No" Content="取消" Width="60" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="0" Click="btnCancel_Click"></Button> 106                     <Button Content="确定" Width="60" HorizontalAlignment="Right" Grid.Row="2" Grid.Column="1" Click="btnOk_Click"></Button> 107                 </Grid> 108             </Border> 109         </Popup> 110         <Popup x:Name="popupName" Width="135" MaxHeight="500" Height="Auto" PopupAnimation="Slide" AllowsTransparency="False" MouseLeave="popup_MouseLeave"> 111             <Border BorderBrush="LightBlue" BorderThickness="1" Padding="5" Background="AliceBlue"> 112                 <Grid> 113                     <Grid.ColumnDefinitions> 114                         <ColumnDefinition></ColumnDefinition> 115                         <ColumnDefinition></ColumnDefinition> 116                     </Grid.ColumnDefinitions> 117                     <Grid.RowDefinitions> 118                         <RowDefinition></RowDefinition> 119                         <RowDefinition></RowDefinition> 120                         <RowDefinition></RowDefinition> 121                     </Grid.RowDefinitions> 122                     <StackPanel Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal"> 123                         <TextBox Height="25" x:Name="txtName" MinWidth="60" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Tag="Name" TextChanged="TextBox_TextChanged"></TextBox> 124                         <Button x:Name="btnNameFilter"  Tag="Name" Click="ButtonFilter_Click" ContextMenu="{StaticResource queryConditionMenu}"> 125                             <StackPanel Orientation="Horizontal" VerticalAlignment="Center"> 126                                 <Path Data="{StaticResource Icon_Filter}" Stroke="Gray" StrokeThickness="1"  127                        Height="12" Width="12" Stretch="Fill"></Path> 128                                 <TextBlock Margin="2,0" Text="筛选" FontSize="12"></TextBlock> 129                             </StackPanel> 130                         </Button> 131                     </StackPanel> 132                     <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}"> 133                         <ListBox.ItemTemplate> 134                             <DataTemplate> 135                                 <CheckBox Content="{Binding FilterText}" IsChecked="{Binding IsChecked}"></CheckBox> 136                             </DataTemplate> 137                         </ListBox.ItemTemplate> 138                     </ListBox> 139  140                     <Button Tag="No" Content="取消" Width="60" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="0" Click="btnCancel_Click"></Button> 141                     <Button Content="确定" Width="60" HorizontalAlignment="Right" Grid.Row="2" Grid.Column="1" Click="btnOk_Click"></Button> 142                 </Grid> 143             </Border> 144         </Popup> 145         <Popup x:Name="popupAge" Width="135" MaxHeight="500" Height="Auto" PopupAnimation="Slide" AllowsTransparency="False" MouseLeave="popup_MouseLeave"> 146             <Border BorderBrush="LightBlue" BorderThickness="1" Padding="5" Background="AliceBlue"> 147                 <Grid> 148                     <Grid.ColumnDefinitions> 149                         <ColumnDefinition></ColumnDefinition> 150                         <ColumnDefinition></ColumnDefinition> 151                     </Grid.ColumnDefinitions> 152                     <Grid.RowDefinitions> 153                         <RowDefinition></RowDefinition> 154                         <RowDefinition></RowDefinition> 155                         <RowDefinition></RowDefinition> 156                     </Grid.RowDefinitions> 157                     <StackPanel Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal"> 158                         <TextBox Height="25" x:Name="txtAge" MinWidth="60" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Tag="Age" TextChanged="TextBox_TextChanged"></TextBox> 159                         <Button x:Name="btnAgeFilter"  Tag="Age" Click="ButtonFilter_Click" ContextMenu="{StaticResource queryConditionMenu}"> 160                             <StackPanel Orientation="Horizontal" VerticalAlignment="Center"> 161                                 <Path Data="{StaticResource Icon_Filter}" Stroke="Gray" StrokeThickness="1"  162                        Height="12" Width="12" Stretch="Fill"></Path> 163                                 <TextBlock Margin="2,0" Text="筛选" FontSize="12"></TextBlock> 164                             </StackPanel> 165                         </Button> 166                     </StackPanel> 167                     <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}"> 168                         <ListBox.ItemTemplate> 169                             <DataTemplate> 170                                 <CheckBox Content="{Binding FilterText}" IsChecked="{Binding IsChecked}"></CheckBox> 171                             </DataTemplate> 172                         </ListBox.ItemTemplate> 173                     </ListBox> 174  175                     <Button Tag="No" Content="取消" Width="60" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="0" Click="btnCancel_Click"></Button> 176                     <Button Content="确定" Width="60" HorizontalAlignment="Right" Grid.Row="2" Grid.Column="1" Click="btnOk_Click"></Button> 177                 </Grid> 178             </Border> 179         </Popup> 180  181         <Popup x:Name="popupNoMenu" Width="300" MaxHeight="500" Height="200" PopupAnimation="Slide" AllowsTransparency="False" Tag=""> 182             <Border BorderThickness="1" BorderBrush="Beige" Padding="15" Background="AliceBlue"> 183                 <Grid> 184                     <Grid.ColumnDefinitions> 185                         <ColumnDefinition></ColumnDefinition> 186                         <ColumnDefinition></ColumnDefinition> 187                     </Grid.ColumnDefinitions> 188                     <Grid.RowDefinitions> 189                         <RowDefinition Height="Auto"></RowDefinition> 190                         <RowDefinition Height="Auto"></RowDefinition> 191                         <RowDefinition Height="Auto"></RowDefinition> 192                         <RowDefinition></RowDefinition> 193                     </Grid.RowDefinitions> 194                     <StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"> 195                         <TextBlock Text="学号" VerticalAlignment="Center"></TextBlock> 196                         <ComboBox x:Name="combNoMenu1" Height="28" Width="100" Margin="4" VerticalContentAlignment="Center"> 197                             <ComboBoxItem Content="等于" ></ComboBoxItem> 198                             <ComboBoxItem Content="不等于"></ComboBoxItem> 199                             <ComboBoxItem Content="开头"></ComboBoxItem> 200                             <ComboBoxItem Content="结尾"></ComboBoxItem> 201                             <ComboBoxItem Content="包含"></ComboBoxItem> 202                             <ComboBoxItem Content="不包含"></ComboBoxItem> 203                         </ComboBox> 204                         <TextBox x:Name="txtNoMenu1" Height="28" Width="100" VerticalContentAlignment="Center"></TextBox> 205                     </StackPanel> 206                     <StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="0"  Grid.ColumnSpan="2"> 207                         <RadioButton x:Name="rbNoAnd" Content="与" IsChecked="True" Margin="4" Background="AliceBlue" VerticalAlignment="Center" VerticalContentAlignment="Center"></RadioButton> 208                         <RadioButton x:Name="rbNoOr" Content="或" Background="AliceBlue" VerticalAlignment="Center" VerticalContentAlignment="Center"></RadioButton> 209                     </StackPanel> 210                     <StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="0"  Grid.ColumnSpan="2"> 211                         <TextBlock Text="学号" VerticalAlignment="Center"></TextBlock> 212                         <ComboBox x:Name="combNoMenu2" Height="28" Margin="4" Width="100" VerticalContentAlignment="Center"> 213                             <ComboBoxItem Content="等于" ></ComboBoxItem> 214                             <ComboBoxItem Content="不等于"></ComboBoxItem> 215                             <ComboBoxItem Content="开头"></ComboBoxItem> 216                             <ComboBoxItem Content="结尾"></ComboBoxItem> 217                             <ComboBoxItem Content="包含"></ComboBoxItem> 218                             <ComboBoxItem Content="不包含"></ComboBoxItem> 219                         </ComboBox> 220                         <TextBox x:Name="txtNoMenu2" Height="28" Width="100" VerticalContentAlignment="Center"></TextBox> 221                     </StackPanel> 222                     <Button Tag="No" Content="取消" Width="100" Height="28" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="0" Click="btnCancelFilter_Click"></Button> 223                     <Button Tag="No" Content="确定" Width="100" Height="28" HorizontalAlignment="Right" Grid.Row="3" Grid.Column="1" Click="btnOkFilter_Click"></Button> 224                 </Grid> 225             </Border> 226         </Popup> 227         <Popup x:Name="popupNameMenu" Width="300" MaxHeight="500" Height="200" PopupAnimation="Slide" AllowsTransparency="False" Tag=""> 228             <Border BorderThickness="1" BorderBrush="Beige" Padding="15" Background="AliceBlue"> 229                 <Grid> 230                     <Grid.RowDefinitions> 231                         <RowDefinition Height="Auto"></RowDefinition> 232                         <RowDefinition Height="Auto"></RowDefinition> 233                         <RowDefinition Height="Auto"></RowDefinition> 234                         <RowDefinition Height="Auto"></RowDefinition> 235                     </Grid.RowDefinitions> 236                     <StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0"> 237                         <TextBlock Text="姓名" VerticalAlignment="Center"></TextBlock> 238                         <ComboBox x:Name="combNameMenu1" Height="28" Width="100" Margin="4" VerticalContentAlignment="Center"> 239                             <ComboBoxItem Content="等于" ></ComboBoxItem> 240                             <ComboBoxItem Content="不等于"></ComboBoxItem> 241                             <ComboBoxItem Content="开头"></ComboBoxItem> 242                             <ComboBoxItem Content="结尾"></ComboBoxItem> 243                             <ComboBoxItem Content="包含"></ComboBoxItem> 244                             <ComboBoxItem Content="不包含"></ComboBoxItem> 245                         </ComboBox> 246                         <TextBox x:Name="txtNameMenu1" Height="28" Width="100" VerticalContentAlignment="Center"></TextBox> 247                     </StackPanel> 248                     <StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="0"> 249                         <RadioButton x:Name="rbNameAnd" Content="与" IsChecked="True" Margin="4" Background="AliceBlue" VerticalAlignment="Center" VerticalContentAlignment="Center"></RadioButton> 250                         <RadioButton x:Name="rbNameOr" Content="或" Background="AliceBlue" VerticalAlignment="Center" VerticalContentAlignment="Center"></RadioButton> 251                     </StackPanel> 252                     <StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="0"> 253                         <TextBlock Text="姓名" VerticalAlignment="Center"></TextBlock> 254                         <ComboBox x:Name="combNameMenu2" Height="28" Width="100" Margin="4" VerticalContentAlignment="Center"> 255                             <ComboBoxItem Content="等于" ></ComboBoxItem> 256                             <ComboBoxItem Content="不等于"></ComboBoxItem> 257                             <ComboBoxItem Content="开头"></ComboBoxItem> 258                             <ComboBoxItem Content="结尾"></ComboBoxItem> 259                             <ComboBoxItem Content="包含"></ComboBoxItem> 260                             <ComboBoxItem Content="不包含"></ComboBoxItem> 261                         </ComboBox> 262                         <TextBox x:Name="txtNameMenu2" Height="28" Width="100" VerticalContentAlignment="Center"></TextBox> 263                     </StackPanel> 264                     <Button Tag="Name" Content="取消" Width="100" Height="28" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="0" Click="btnCancelFilter_Click"></Button> 265                     <Button Tag="Name" Content="确定" Width="100" Height="28" HorizontalAlignment="Right" Grid.Row="3" Grid.Column="1" Click="btnOkFilter_Click"></Button> 266                 </Grid> 267             </Border> 268         </Popup> 269         <Popup x:Name="popupAgeMenu" Width="300" MaxHeight="500" Height="200" PopupAnimation="Slide" AllowsTransparency="False" Tag=""> 270             <Border BorderThickness="1" BorderBrush="Beige" Padding="15" Background="AliceBlue"> 271                 <Grid> 272                     <Grid.RowDefinitions> 273                         <RowDefinition Height="Auto"></RowDefinition> 274                         <RowDefinition Height="Auto"></RowDefinition> 275                         <RowDefinition Height="Auto"></RowDefinition> 276                         <RowDefinition Height="Auto"></RowDefinition> 277                     </Grid.RowDefinitions> 278                     <StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0"> 279                         <TextBlock Text="年龄" VerticalAlignment="Center"></TextBlock> 280                         <ComboBox x:Name="combAgeMenu1" Height="28" Width="100" Margin="4" VerticalContentAlignment="Center"> 281                             <ComboBoxItem Content="等于" ></ComboBoxItem> 282                             <ComboBoxItem Content="不等于"></ComboBoxItem> 283                             <ComboBoxItem Content="开头"></ComboBoxItem> 284                             <ComboBoxItem Content="结尾"></ComboBoxItem> 285                             <ComboBoxItem Content="包含"></ComboBoxItem> 286                             <ComboBoxItem Content="不包含"></ComboBoxItem> 287                         </ComboBox> 288                         <TextBox x:Name="txtAgeMenu1" Height="28" Width="100" VerticalContentAlignment="Center"></TextBox> 289                     </StackPanel> 290                     <StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="0"> 291                         <RadioButton x:Name="rbAgeAnd" Content="与"  IsChecked="True" Margin="4" Background="AliceBlue" VerticalAlignment="Center" VerticalContentAlignment="Center"></RadioButton> 292                         <RadioButton x:Name="rbAgeOr" Content="或" Background="AliceBlue" VerticalAlignment="Center" VerticalContentAlignment="Center"></RadioButton> 293                     </StackPanel> 294                     <StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="0"> 295                         <TextBlock Text="年龄" VerticalAlignment="Center"></TextBlock> 296                         <ComboBox x:Name="combAgeMenu2" Height="28" Width="100" Margin="4" VerticalContentAlignment="Center"> 297                             <ComboBoxItem Content="等于" ></ComboBoxItem> 298                             <ComboBoxItem Content="不等于"></ComboBoxItem> 299                             <ComboBoxItem Content="开头"></ComboBoxItem> 300                             <ComboBoxItem Content="结尾"></ComboBoxItem> 301                             <ComboBoxItem Content="包含"></ComboBoxItem> 302                             <ComboBoxItem Content="不包含"></ComboBoxItem> 303                         </ComboBox> 304                         <TextBox x:Name="txtAgeMenu2" Height="28" Width="100" VerticalContentAlignment="Center"></TextBox> 305                     </StackPanel> 306                     <Button Tag="Age" Content="取消" Width="100" Height="28" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="0" Click="btnCancelFilter_Click"></Button> 307                     <Button Tag="Age" Content="确定" Width="100" Height="28" HorizontalAlignment="Right" Grid.Row="3" Grid.Column="1" Click="btnOkFilter_Click"></Button> 308                 </Grid> 309             </Border> 310         </Popup> 311     </Grid> 312 </Window>

业务逻辑【MainWindowViewModel】

业务逻辑处理主要复责数据初始化等业务相关内容,和UI无关,如下所示:

 1 using CommunityToolkit.Mvvm.ComponentModel;  2 using System;  3 using System.Collections.Generic;  4 using System.Linq;  5 using System.Text;  6 using System.Threading.Tasks;  7   8 namespace DemoDataGrid  9 { 10     public class MainWindowViewModel:ObservableObject 11     { 12         #region 属性及构造函数 13  14         private List<Student> students; 15  16         public List<Student> Students 17         { 18             get { return students; } 19             set { SetProperty(ref students, value); } 20         } 21  22         private List<FilterInfo> names; 23  24         public List<FilterInfo> Names 25         { 26             get { return names; } 27             set { SetProperty(ref names, value); } 28         } 29  30         private List<FilterInfo> nos; 31  32         public List<FilterInfo> Nos 33         { 34             get { return nos; } 35             set {SetProperty(ref nos , value); } 36         } 37  38         private List<FilterInfo> ages; 39  40         public List<FilterInfo> Ages 41         { 42             get { return ages; } 43             set {SetProperty(ref ages , value); } 44         } 45  46  47  48         public MainWindowViewModel() 49         { 50             this.Students= new List<Student>(); 51             for (int i = 0; i < 20; i++) { 52                 this.Students.Add(new Student() 53                 { 54                     Id = i, 55                     Name = $"张{i}牛", 56                     Age = (i % 10) + 10, 57                     No = i.ToString().PadLeft(4, '0'), 58                 }); 59             } 60             this.Nos= new List<FilterInfo>(); 61             this.Names= new List<FilterInfo>(); 62             this.Ages= new List<FilterInfo>(); 63             this.Students.ForEach(s => { 64                 this.Nos.Add(new FilterInfo() { FilterText=s.No,IsChecked=false }); 65                 this.Names.Add(new FilterInfo() { FilterText = s.Name, IsChecked = false }); 66                 this.Ages.Add(new FilterInfo() { FilterText = s.Age.ToString(), IsChecked = false }); 67             }); 68             this.Ages=this.Ages.Distinct().ToList();//去重 69         } 70  71         #endregion 72  73  74     } 75 }

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

本示例为了简化实现,筛选功能处理主要在cs后端实现,如下所示:

  1 using System;   2 using System.Collections.Generic;   3 using System.Linq;   4 using System.Text;   5 using System.Threading.Tasks;   6 using System.Windows;   7 using System.Windows.Controls;   8 using System.Windows.Data;   9 using System.Windows.Documents;  10 using System.Windows.Input;  11 using System.Windows.Media;  12 using System.Windows.Media.Imaging;  13 using System.Windows.Navigation;  14 using System.Windows.Shapes;  15   16 namespace DemoDataGrid  17 {  18     /// <summary>  19     /// Interaction logic for MainWindow.xaml  20     /// </summary>  21     public partial class MainWindow : Window  22     {  23         private MainWindowViewModel viewModel;  24   25         public MainWindow()  26         {  27             InitializeComponent();  28             viewModel = new MainWindowViewModel();  29             this.DataContext = viewModel;  30         }  31   32   33         #region 筛选  34   35         private void TextBlock_MouseRightButtonDown(object sender, MouseButtonEventArgs e)  36         {  37             if (sender != null && sender is TextBlock)  38             {  39                 var textBlock = sender as TextBlock;  40                 var tag = textBlock.Tag.ToString();  41                 var pop = this.FindName($"popup{tag}");  42                 if (pop != null)  43                 {  44                     var popup = pop as System.Windows.Controls.Primitives.Popup;  45                     if (popup != null)  46                     {  47                         popup.IsOpen = true;  48                         popup.PlacementTarget = textBlock;  49                         popup.Placement = System.Windows.Controls.Primitives.PlacementMode.RelativePoint;  50                         popup.VerticalOffset = 10;  51                         popup.HorizontalOffset = 10;  52                     }  53                 }  54             }  55         }  56   57         private void TextBox_TextChanged(object sender, TextChangedEventArgs e)  58         {  59             TextBox textBox = e.OriginalSource as TextBox;  60             var tag = textBox.Tag;//条件  61             var text = textBox.Text;  62             if (tag != null)  63             {  64                 if (tag.ToString() == "No")  65                 {  66                     Filter(this.lbNos.ItemsSource, this.txtNo.Text);  67                 }  68                 if (tag.ToString() == "Name")  69                 {  70                     Filter(this.lbNames.ItemsSource, this.txtName.Text);  71                 }  72                 if (tag.ToString() == "Age")  73                 {  74                     Filter(this.lbAges.ItemsSource, this.txtAge.Text);  75                 }  76             }  77   78         }  79   80         private void Filter(object source, string filter)  81         {  82             var cv = CollectionViewSource.GetDefaultView(source);  83             if (cv != null && cv.CanFilter)  84             {  85                 cv.Filter = new Predicate<object>((obj) => {  86                     bool flag = true;  87                     var t = obj as FilterInfo;  88                     if (t != null)  89                     {  90                         flag = t.FilterText.Contains(filter);  91                     }  92                     return flag;  93                 });  94             }  95         }  96   97         private void popup_MouseLeave(object sender, MouseEventArgs e)  98         {  99             var popup = e.OriginalSource as System.Windows.Controls.Primitives.Popup; 100             var showContext = (this.FindResource("queryConditionMenu") as ContextMenu)?.IsOpen; 101             if (popup != null && showContext==false) 102             { 103                 popup.IsOpen = false; 104             } 105         } 106  107         private void btnCancel_Click(object sender, RoutedEventArgs e) 108         { 109             var btn = e.OriginalSource as Button; 110             if (btn != null) 111             { 112                 var tag = btn.Tag; 113                 if (tag.ToString() == "No") 114                 { 115                     ClearFilter(this.txtNo, this.viewModel.Nos); 116                 } 117                 if (tag.ToString() == "Name") 118                 { 119                     ClearFilter(this.txtName, this.viewModel.Names); 120  121                 } 122                 if (tag.ToString() == "Age") 123                 { 124                     ClearFilter(this.txtAge, this.viewModel.Ages); 125                 } 126                 FilterTask();//清除以后,重新刷新 127             } 128         } 129  130         private void ClearFilter(TextBox textBox, List<FilterInfo> collection) 131         { 132             textBox.Clear(); 133             foreach (var f in collection) 134             { 135                 f.IsChecked = false; 136             } 137         } 138  139         private void btnOk_Click(object sender, RoutedEventArgs e) 140         { 141             // 142             FilterTask(); 143         } 144  145  146         private void FilterTask() 147         { 148             var cv = CollectionViewSource.GetDefaultView(this.dgStudents.ItemsSource); 149             if (cv != null && cv.CanFilter) 150             { 151                 cv.Filter = new Predicate<object>((obj) => 152                 { 153                     bool flag = true; 154                     var t = obj as Student; 155                     if (t != null) 156                     { 157                         var nos = this.viewModel.Nos.Where(r => r.IsChecked == true).ToList(); 158                         var names = this.viewModel.Names.Where(r => r.IsChecked == true).ToList(); 159                         var ages = this.viewModel.Ages.Where(r => r.IsChecked == true).ToList(); 160                         if (nos.Count() > 0) 161                         { 162                             flag = flag && nos.Select(r => r.FilterText).Contains(t.No); 163                         } 164                         if (names.Count() > 0) 165                         { 166                             flag = flag && names.Select(r => r.FilterText).Contains(t.Name); 167                         } 168                         if (ages.Count() > 0) 169                         { 170                             flag = flag && ages.Select(r => r.FilterText).Contains(t.Age.ToString()); 171                         } 172                     } 173                     return flag; 174                 }); 175             } 176         } 177  178         #endregion 179  180         private List<string> condition = new List<string>() { "Equal", "NotEqual", "Begin", "End", "In", "NotIn" }; 181  182         private void ButtonFilter_Click(object sender, RoutedEventArgs e) 183         { 184             var btn = e.OriginalSource as Button; 185             if (btn != null) 186             { 187                 var tag = btn.Tag; 188                 var popup = this.FindName($"popup{tag}") as System.Windows.Controls.Primitives.Popup; 189                 if (popup != null) 190                 { 191                     popup.IsOpen = true; 192                 } 193                 if (btn.ContextMenu.IsOpen) 194                 { 195                     btn.ContextMenu.IsOpen = false; 196                 } 197                 else 198                 { 199                     btn.ContextMenu.Tag = tag; 200                     btn.ContextMenu.Width = 100; 201                     btn.ContextMenu.Height = 150; 202                     btn.ContextMenu.IsOpen = true; 203                     btn.ContextMenu.PlacementTarget = btn; 204                     btn.ContextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom; 205                 } 206             } 207         } 208  209         private void ContextMenu_MouseLeave(object sender, MouseEventArgs e) 210         { 211             var menu = e.OriginalSource as ContextMenu; 212             if (menu != null) 213             { 214                 menu.IsOpen = false; 215             } 216         } 217  218         private void ContextMenu_Click(object sender, RoutedEventArgs e) 219         { 220             var contextMenu = sender as ContextMenu; 221             if (contextMenu == null) 222             { 223                 return; 224             } 225             var menuItem = e.OriginalSource as MenuItem; 226             if (menuItem == null) 227             { 228                 return; 229             } 230             var tag1 = contextMenu.Tag.ToString();//点击的哪一个按钮 231             var tag2 = menuItem.Tag.ToString();//点击的是哪一个菜单 232             var pop = this.FindName($"popup{tag1}Menu"); 233             var comb = this.FindName($"comb{tag1}Menu1"); 234             HideParentPopup(tag1);//隐藏父Popup 235             if (comb != null) 236             { 237                 var combMenu = comb as ComboBox; 238                 combMenu.SelectedIndex = condition.IndexOf(tag2); 239             } 240             if (pop != null) 241             { 242                 var popup = pop as System.Windows.Controls.Primitives.Popup; 243                 popup.IsOpen = true; 244                 popup.PlacementTarget = dgStudents; 245                 popup.Placement = System.Windows.Controls.Primitives.PlacementMode.Center; 246             } 247         } 248  249         private void btnCancelFilter_Click(object sender, RoutedEventArgs e) 250         { 251             if (sender == null) 252             { 253                 return; 254             } 255             var btn = sender as System.Windows.Controls.Button; 256             if (btn != null) 257             { 258                 var tag = btn.Tag.ToString(); 259                 HidePopupMenu(tag);//隐藏Popup控件 260                 if (tag == "No") 261                 { 262                     ClearMenuFilter(this.txtNoMenu1, this.txtNoMenu2); 263                 } 264                 if (tag == "Name") 265                 { 266                     ClearMenuFilter(this.txtNameMenu1, this.txtNameMenu2); 267                 } 268                 if (tag == "Age") 269                 { 270                     ClearMenuFilter(this.txtAgeMenu1, this.txtAgeMenu2); 271                 } 272                 FilterMenuTask(); 273             } 274         } 275  276  277         private void btnOkFilter_Click(object sender, RoutedEventArgs e) 278         { 279             if (sender == null) 280             { 281                 return; 282             } 283             var btn = sender as System.Windows.Controls.Button; 284             if (btn != null) 285             { 286                 var tag = btn.Tag.ToString(); 287                 HidePopupMenu(tag); 288                 FilterMenuTask(); 289             } 290         } 291  292         /// <summary> 293         /// 隐藏父Popup 294         /// </summary> 295         /// <param name="tag"></param> 296         private void HideParentPopup(string tag) 297         { 298             //点击右键菜单时,隐藏父Popup控件 299             if (tag == "No") 300             { 301                 this.popupNo.IsOpen = false; 302             } 303             if (tag == "Name") 304             { 305                 this.popupName.IsOpen = false; 306             } 307             if (tag == "Age") 308             { 309                 this.popupAge.IsOpen = false; 310             } 311         } 312  313         /// <summary> 314         /// 隐藏菜单弹出的Popup控件 315         /// </summary> 316         /// <param name="tag"></param> 317         private void HidePopupMenu(string tag) 318         { 319             var pop = this.FindName($"popup{tag}Menu"); 320             if (pop != null) 321             { 322                 var popup = pop as System.Windows.Controls.Primitives.Popup; 323                 popup.IsOpen = false; 324             } 325         } 326  327         /// <summary> 328         /// 清除菜单中的文本过滤条件 329         /// </summary> 330         /// <param name="txt1"></param> 331         /// <param name="txt2"></param> 332         private void ClearMenuFilter(TextBox txt1, TextBox txt2) 333         { 334             txt1?.Clear(); 335             txt2?.Clear(); 336         } 337  338         /// <summary> 339         ///  340         /// </summary> 341         private void FilterMenuTask() 342         { 343             var cv = CollectionViewSource.GetDefaultView(this.dgStudents.ItemsSource); 344             if (cv != null && cv.CanFilter) 345             { 346                 cv.Filter = new Predicate<object>((obj) => 347                 { 348                     bool flag = true; 349                     var t = obj as Student; 350                     if (t != null) 351                     { 352                         string noText1 = this.txtNoMenu1.Text.Trim(); 353                         string noText2 = this.txtNoMenu2.Text.Trim(); 354                         int noConditionType1 = this.combNoMenu1.SelectedIndex; 355                         int noConditionType2 = this.combNoMenu2.SelectedIndex; 356                         string nameText1 = this.txtNameMenu1.Text.Trim(); 357                         string nameText2 = this.txtNameMenu2.Text.Trim(); 358                         int nameConditionType1 = this.combNameMenu1.SelectedIndex; 359                         int nameConditionType2 = this.combNameMenu2.SelectedIndex; 360                         string ageText1 = this.txtAgeMenu1.Text.Trim(); 361                         string ageText2 = this.txtAgeMenu2.Text.Trim(); 362                         int ageConditionType1 = this.combAgeMenu1.SelectedIndex; 363                         int ageConditionType2 = this.combAgeMenu2.SelectedIndex; 364                         bool? isNoAnd = this.rbNoAnd.IsChecked; 365                         bool? isNoOr = this.rbNoOr.IsChecked; 366                         bool? isNameAnd = this.rbNameAnd.IsChecked; 367                         bool? isNameOr = this.rbNameOr.IsChecked; 368                         bool? isAgeAnd = this.rbAgeAnd.IsChecked; 369                         bool? isAgeOr = this.rbAgeOr.IsChecked; 370                         bool flagNo = true; 371                         bool flagName = true; 372                         bool flagAge = true; 373                         flagNo = CheckConditions(noConditionType1, noConditionType2, t.No, noText1, noText2, isNoAnd, isNoOr); 374                         flagName = CheckConditions(nameConditionType1, nameConditionType2, t.Name, nameText1, nameText2, isNameAnd, isNameOr); 375                         flagAge = CheckConditions(ageConditionType1, ageConditionType2, t.Age.ToString(), ageText1, ageText2, isAgeAnd, isAgeOr); 376                         flag = flag && flagNo && flagName && flagAge; 377                     } 378                     return flag; 379                 }); 380             } 381         } 382  383         private bool CheckConditions(int conditionIndex1, int conditionIndex2, string source, string condition1, string condition2, bool? isAnd, bool? isOr) 384         { 385             bool flag = true; 386             bool flag1 = true; 387             bool flag2 = true; 388             if (!string.IsNullOrEmpty(condition1) && !string.IsNullOrWhiteSpace(condition1) && conditionIndex1 != -1) 389             { 390                 flag1 = CheckCondition(conditionIndex1, source, condition1); 391             } 392             if (!string.IsNullOrEmpty(condition2) && !string.IsNullOrWhiteSpace(condition2) && conditionIndex2 != -1) 393             { 394                 flag2 = CheckCondition(conditionIndex2, source, condition2); 395             } 396             if (isAnd == true) 397             { 398                 flag = flag1 && flag2; 399             } 400             if (isOr == true) 401             { 402                 flag = flag1 || flag2; 403             } 404             return flag; 405         } 406  407         private bool CheckCondition(int condtionIndex, string source, string condition) 408         { 409             bool flag = true; 410             if (condtionIndex == 0) 411             { 412                 flag = flag && source == condition; 413             } 414             if (condtionIndex == 1) 415             { 416                 flag = flag && source != condition; 417             } 418             if (condtionIndex == 2) 419             { 420                 flag = flag && source.StartsWith(condition); 421             } 422             if (condtionIndex == 3) 423             { 424                 flag = flag && source.EndsWith(condition); 425             } 426             if (condtionIndex == 4) 427             { 428                 flag = flag && source.Contains(condition); 429             } 430             if (condtionIndex == 5) 431             { 432                 flag = flag && !source.Contains(condition); 433             } 434             return flag; 435         } 436     } 437 }

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

不足与思考

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

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

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