WPF 简单实现下拉筛选控件
框架使用.NET40
;
Visual Studio 2022
;
使用 ICollectionView[2] 实现筛选功能,还支持其他如下:
- 使集合具有当前记录管理
- 自定义排序
- 筛选和分组功能
实现代码
1)CheckedSearch.cs
代码如下:
SearchText
用来记录输入的筛选内容Text
用来记录展示的所选内容^
拼接ItemsSource
数据源ContainsFilter
筛选数据,如果从数据源中找到则返回True
using System.Collections.ObjectModel; | |
using System.ComponentModel; | |
using System.Linq; | |
using System.Windows; | |
using System.Windows.Controls; | |
using System.Windows.Data; | |
using WpfCustomControlLibrary1.Datas; | |
namespace WpfCustomControlLibrary1 | |
{ | |
public class CheckedSearch : Control | |
{ | |
private ICollectionView _filteredCollection; | |
public ICollectionView FilteredCollection { get { return _filteredCollection; } } | |
private string _searchText = string.Empty; | |
public string SearchText | |
{ | |
get { return _searchText; } | |
set | |
{ | |
if (_searchText != value) | |
{ | |
_searchText = value; | |
_filteredCollection.Refresh(); | |
} | |
} | |
} | |
public string Text | |
{ | |
get { return (string)GetValue(TextProperty); } | |
set { SetValue(TextProperty, value); } | |
} | |
public static readonly DependencyProperty TextProperty = | |
DependencyProperty.Register("Text", typeof(string), typeof(CheckedSearch), new PropertyMetadata(string.Empty)); | |
public ObservableCollection<CheckedSearchItem> ItemsSource | |
{ | |
get { return (ObservableCollection<CheckedSearchItem>)GetValue(ItemsSourceProperty); } | |
set { SetValue(ItemsSourceProperty, value); } | |
} | |
public static readonly DependencyProperty ItemsSourceProperty = | |
DependencyProperty.Register("ItemsSource", typeof(ObservableCollection<CheckedSearchItem>), typeof(CheckedSearch), new PropertyMetadata(null, OnItemsSourceChanged)); | |
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | |
{ | |
var choseSearch = (CheckedSearch)d; | |
if (choseSearch == null) return; | |
if (choseSearch._filteredCollection == null && choseSearch.ItemsSource.Count > 0) | |
{ | |
foreach (var item in choseSearch.ItemsSource) | |
{ | |
item.PropertyChanged -= choseSearch.Item_PropertyChanged; | |
item.PropertyChanged += choseSearch.Item_PropertyChanged; | |
} | |
choseSearch._filteredCollection = CollectionViewSource.GetDefaultView(choseSearch.ItemsSource); | |
choseSearch._filteredCollection.Filter = choseSearch.ContainsFilter; | |
} | |
} | |
string GetItems() | |
{ | |
var list = ItemsSource.Where(x=>x.IsChecked).Select(x=>x.Name).ToList(); | |
var visibleItems = string.Join("^",list); | |
return visibleItems; | |
} | |
static CheckedSearch() | |
{ | |
DefaultStyleKeyProperty.OverrideMetadata(typeof(CheckedSearch), new FrameworkPropertyMetadata(typeof(CheckedSearch))); | |
} | |
private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) | |
{ | |
if (e.PropertyName == "IsChecked") | |
Text = GetItems(); | |
} | |
private bool ContainsFilter(object item) | |
{ | |
var model = item as CheckedSearchItem; | |
if (model == null) | |
return false; | |
if (string.IsNullOrEmpty(SearchText)) | |
return true; | |
if (model.Name.ToUpperInvariant().Contains(SearchText.ToUpperInvariant())) | |
return true; | |
return false; | |
} | |
} | |
} |
2)CheckedSearchItem.cs
代码如下:
IsChecked
记录是否选中Name
展示的名称
using System.ComponentModel; | |
namespace WpfCustomControlLibrary1.Datas | |
{ | |
public class CheckedSearchItem | |
{ | |
public string Name { get; set; } | |
private bool _isChecked; | |
public bool IsChecked | |
{ | |
get { return _isChecked; } | |
set | |
{ | |
_isChecked = value; | |
OnPropertyChanged("IsChecked"); | |
} | |
} | |
public event PropertyChangedEventHandler PropertyChanged; | |
protected void OnPropertyChanged(string propertyName) | |
{ | |
if (PropertyChanged != null) | |
PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); | |
} | |
} | |
} |
3)CheckedSearch.xaml
代码如下:
<Style TargetType="{x:Type local:CheckedSearch}"> | |
<Setter Property="Height" Value="40"/> | |
<Setter Property="Width" Value="200"/> | |
<Setter Property="BorderBrush" Value="DodgerBlue"/> | |
<Setter Property="Background" Value="White"/> | |
<Setter Property="BorderThickness" Value="1"/> | |
<Setter Property="Template"> | |
<Setter.Value> | |
<ControlTemplate TargetType="{x:Type local:CheckedSearch}"> | |
<Border Background="{TemplateBinding Background}" | |
BorderBrush="{TemplateBinding BorderBrush}" | |
BorderThickness="{TemplateBinding BorderThickness}" | |
x:Name="PART_Border"> | |
<Grid> | |
<Grid.ColumnDefinitions> | |
<ColumnDefinition/> | |
<ColumnDefinition Width="Auto"/> | |
</Grid.ColumnDefinitions> | |
<TextBlock Text="{Binding Text,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" | |
Foreground="Black" | |
VerticalAlignment="Center" | |
Margin="2,0"/> | |
<ToggleButton x:Name="PART_ToggleButton" | |
Focusable="False" | |
Width="30" | |
Style="{x:Null}" | |
Grid.Column="1" | |
ClickMode="Release"> | |
<Path Stretch="Fill" | |
Height="6" Width="10" | |
HorizontalAlignment="Center" | |
VerticalAlignment="Center" | |
Data="M998 352c0 -8 -4 -17 -10 -23l-50 -50c-6 -6 -14 -10 -23 -10c-8 0 -17 4 -23 10l-393 393l-393 -393c-6 -6 -15 -10 -23 -10s-17 4 -23 10l-50 50c-6 6 -10 15 -10 23s4 17 10 23l466 466c6 6 15 10 23 10s17 -4 23 -10l466 -466c6 -6 10 -15 10 -23z" | |
Fill="Black"> | |
</Path> | |
</ToggleButton> | |
<Popup IsOpen="{Binding ElementName=PART_ToggleButton,Path=IsChecked}" | |
x:Name="PART_Popup" | |
VerticalOffset="2" | |
AllowsTransparency="True" | |
PlacementTarget="{Binding ElementName=PART_Border}" | |
Placement="Bottom" StaysOpen="False"> | |
<Border Width="{TemplateBinding Width}" | |
Padding="2,4" | |
Background="{TemplateBinding Background}" | |
BorderBrush="{TemplateBinding BorderBrush}" | |
BorderThickness="{TemplateBinding BorderThickness}"> | |
<StackPanel> | |
<TextBox Text="{Binding SearchText,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" | |
Height="40" VerticalContentAlignment="Center" | |
Margin="0,0,0,2"/> | |
<ListBox ItemsSource="{TemplateBinding ItemsSource}"> | |
<ListBox.ItemTemplate> | |
<DataTemplate> | |
<CheckBox Content="{Binding Name}" | |
IsChecked="{Binding IsChecked}"/> | |
</DataTemplate> | |
</ListBox.ItemTemplate> | |
</ListBox> | |
</StackPanel> | |
</Border> | |
</Popup> | |
</Grid> | |
</Border> | |
</ControlTemplate> | |
</Setter.Value> | |
</Setter> | |
</Style> |
4)CheckedSearchExample.xaml
示例代码如下:
<wd:Window x:Class="WpfApp1.MainWindow" | |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |
xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers" | |
xmlns:custom="clr-namespace:WpfCustomControlLibrary1;assembly=WpfCustomControlLibrary1" | |
xmlns:local="clr-namespace:WpfApp1" | |
mc:Ignorable="d" | |
Title="WPFDevelopers - 搜索多选控件" Height="450" Width="800"> | |
<Window.Resources> | |
<ResourceDictionary> | |
<ResourceDictionary.MergedDictionaries> | |
<ResourceDictionary Source="pack://application:,,,/WpfApp1;component/CheckedSearch.xaml"/> | |
</ResourceDictionary.MergedDictionaries> | |
</ResourceDictionary> | |
</Window.Resources> | |
<!--公众号:WPF开发者--> | |
<Grid> | |
<custom:CheckedSearch | |
ItemsSource="{Binding ItemsSource,RelativeSource={RelativeSource AncestorType=Window}}" | |
VerticalAlignment="Top" Margin="0,10"/> | |
</Grid> | |
</wd:Window> |
5)CheckedSearchExample.xaml
数据源示例代码如下:
using System.Collections.ObjectModel; | |
using System.Windows; | |
using WpfCustomControlLibrary1.Datas; | |
namespace WpfApp1 | |
{ | |
public partial class MainWindow | |
{ | |
public ObservableCollection<CheckedSearchItem> ItemsSource | |
{ | |
get { return (ObservableCollection<CheckedSearchItem>)GetValue(ItemsSourceProperty); } | |
set { SetValue(ItemsSourceProperty, value); } | |
} | |
public static readonly DependencyProperty ItemsSourceProperty = | |
DependencyProperty.Register("ItemsSource", typeof(ObservableCollection<CheckedSearchItem>), typeof(MainWindow), new PropertyMetadata(null)); | |
public MainWindow() | |
{ | |
InitializeComponent(); | |
Loaded += MainWindow_Loaded; | |
} | |
private void MainWindow_Loaded(object sender, RoutedEventArgs e) | |
{ | |
ItemsSource = new ObservableCollection<CheckedSearchItem>(); | |
var items = new ObservableCollection<CheckedSearchItem>(); | |
items.Add(new CheckedSearchItem { Name = "Winform" }); | |
items.Add(new CheckedSearchItem { Name = "WPF" }); | |
items.Add(new CheckedSearchItem { Name = "WinUI 3" }); | |
items.Add(new CheckedSearchItem { Name = "MAUI" }); | |
items.Add(new CheckedSearchItem { Name = "Avalonia UI" }); | |
ItemsSource = items; | |
} | |
} | |
} |