让WPF中的DataGrid像Excel一样可以筛选
在默认情况下,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的筛选功能进行实现,右键标题栏打开浮动窗口,悬浮于标题栏下方,既可以通过文本框进行筛选,也可以通过筛选按钮弹出右键菜单,选择具体筛选方式,截图如下所示:
选择筛选方式,弹出窗口,如下所示:
https://img2023.cnblogs.com/blog/1068941/202302/1068941-20230227223442193-834388989.png
输入筛选条件,点击确定,或者取消筛选。如筛选学号里面包含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
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); }
}
}
}不足与思考
上述筛选实现方式,并非唯一实现,也并非最优实现,同样存在许多可以优化的地方。
在本示例中,存在许多冗余代码,如视图页面,对三列的弹出窗口,内容虽然相对统一,只是列名和绑定内容不同而已,却堆积了三大段代码,是否可以从控件模块或者数据模板的角度,进行简化呢?
筛选功能实现上,同样存在许多冗余代码,是否可以进行简化呢?以上是我们需要思考的地方,希望可以集思广益,共同学习,一起进步。
来源:https://www.cnblogs.com/hsiang/archive/2023/02/28/17162319.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页:
[1]