- A+
在默认情况下,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的筛选功能进行实现,右键标题栏打开浮动窗口,悬浮于标题栏下方,既可以通过文本框进行筛选,也可以通过筛选按钮弹出右键菜单,选择具体筛选方式,截图如下所示:
选择筛选方式,弹出窗口,如下所示:
输入筛选条件,点击确定,或者取消筛选。如筛选学号里面包含2的,效果如下所示:
注意:以上筛选都是客户端筛选,不会修改数据源,也不会重连数据库。
核心源码
在本示例中,核心源码主要包含以下几个部分:
前端视图【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); } } } }
不足与思考
上述筛选实现方式,并非唯一实现,也并非最优实现,同样存在许多可以优化的地方。
在本示例中,存在许多冗余代码,如视图页面,对三列的弹出窗口,内容虽然相对统一,只是列名和绑定内容不同而已,却堆积了三大段代码,是否可以从控件模块或者数据模板的角度,进行简化呢?
筛选功能实现上,同样存在许多冗余代码,是否可以进行简化呢?以上是我们需要思考的地方,希望可以集思广益,共同学习,一起进步。