WPF 中自定义懒加载与模糊过滤下拉列表控件的实现
笔记哥 /
04-30 /
13点赞 /
0评论 /
289阅读
因为项目中PC端前端针对基础数据选择时的下拉列表做了懒加载控件,PC端使用现成的组件,为保持两端的选择方式统一,WPF客户端上也需要使用懒加载的下拉选择。
WPF这种懒加载的控件未找到现成可用的组件,于是自己封装了一个懒加载和支持模糊过滤的下拉列表控件,控件使用了虚拟化加载,解决了大数据量时的渲染数据卡顿问题,下面是完整的代码和示例:
一、控件所需的关键实体类
```csharp
1 ///
2 /// 下拉项
3 ///
4 public class ComboItem
5 {
6 ///
7 /// 实际存储值
8 ///
9 public string? ItemValue { get; set; }
10 ///
11 /// 显示文本
12 ///
13 public string? ItemText { get; set; }
14 }
15
16 ///
17 /// 懒加载下拉数据源提供器
18 ///
19 public class ComboItemProvider : ILazyDataProvider
20 {
21 private readonly List _all;
22 public ComboItemProvider()
23 {
24 _all = Enumerable.Range(1, 1000000)
25 .Select(i => new ComboItem { ItemValue = i.ToString(), ItemText = $"Item {i}" })
26 .ToList();
27 }
28 public async Task> FetchAsync(string filter, int pageIndex, int pageSize)
29 {
30 await Task.Delay(100);
31 var q = _all.AsQueryable();
32 if (!string.IsNullOrEmpty(filter))
33 q = q.Where(x => x.ItemText.Contains(filter, StringComparison.OrdinalIgnoreCase));
34 var page = q.Skip(pageIndex * pageSize).Take(pageSize).ToList();
35 bool has = q.Count() > (pageIndex + 1) * pageSize;
36 return new PageResult { Items = page, HasMore = has };
37 }
38 }
39
40 ///
41 /// 封装获取数据的接口
42 ///
43 ///
44 public interface ILazyDataProvider
45 {
46 Task> FetchAsync(string filter, int pageIndex, int pageSize);
47 }
48
49 ///
50 /// 懒加载下拉分页对象
51 ///
52 ///
53 public class PageResult
54 {
55 public IReadOnlyList Items { get; set; }
56 public bool HasMore { get; set; }
57 }
```
二、懒加载控件视图和数据逻辑
```csharp
1
6
7
8
9
22
23
95
96
119
120
127
128
145
146
147
151
152
153
157
158
159
160
166
167
168
169
174
175
176
177
178
179
180
181
187
188
198
199
200
201
202
```

```csharp
1 public partial class LazyComboBox : UserControl, INotifyPropertyChanged
2 {
3 public static readonly DependencyProperty ItemsProviderProperty =
4 DependencyProperty.Register(nameof(ItemsProvider), typeof(ILazyDataProvider),
5 typeof(LazyComboBox), new PropertyMetadata(null));
6
7 public ILazyDataProvider ItemsProvider
8 {
9 get => (ILazyDataProvider)GetValue(ItemsProviderProperty);
10 set => SetValue(ItemsProviderProperty, value);
11 }
12
13 public static readonly DependencyProperty SelectedItemProperty =
14 DependencyProperty.Register(nameof(SelectedItem), typeof(ComboItem),
15 typeof(LazyComboBox),
16 new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemChanged));
17
18 public ComboItem SelectedItem
19 {
20 get => (ComboItem)GetValue(SelectedItemProperty);
21 set => SetValue(SelectedItemProperty, value);
22 }
23
24 private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
25 {
26 if (d is LazyComboBox ctrl)
27 {
28 ctrl.Notify(nameof(DisplayText));
29 }
30 }
31
32 public ObservableCollection Items { get; } = new ObservableCollection();
33 private string _currentFilter = "";
34 private int _currentPage = 0;
35 private const int PageSize = 30;
36 public bool HasMore { get; private set; }
37 public string DisplayText => SelectedItem?.ItemText ?? "请选择...";
38
39 public LazyComboBox()
40 {
41 InitializeComponent();
42 }
43
44 public event PropertyChangedEventHandler PropertyChanged;
45 private void Notify(string prop) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
46
47 private async void LoadPage(int pageIndex)
48 {
49 if (ItemsProvider == null) return;
50 var result = await ItemsProvider.FetchAsync(_currentFilter, pageIndex, PageSize);
51 if (pageIndex == 0) Items.Clear();
52 foreach (var it in result.Items) Items.Add(it);
53 HasMore = result.HasMore;
54 PART_Popup.IsOpen = true;
55 }
56
57 private void OnClearClick(object sender, RoutedEventArgs e)
58 {
59 e.Handled = true; // 阻止事件冒泡,不触发 Toggle 打开
60 SelectedItem = null; // 清空选中
61 Notify(nameof(DisplayText)); // 刷新按钮文本
62 PART_Popup.IsOpen = false; // 确保关掉弹窗
63 }
64
65 private void OnToggleClick(object sender, RoutedEventArgs e)
66 {
67 _currentPage = 0;
68 LoadPage(0);
69 PART_Popup.IsOpen = true;
70 }
71
72 private void OnSearchChanged(object sender, TextChangedEventArgs e)
73 {
74 _currentFilter = PART_SearchBox.Text;
75 _currentPage = 0;
76 LoadPage(0);
77 }
78
79 private void OnScroll(object sender, ScrollChangedEventArgs e)
80 {
81 if (!HasMore) return;
82 if (e.VerticalOffset >= e.ExtentHeight - e.ViewportHeight - 2)
83 LoadPage(++_currentPage);
84 }
85
86 private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
87 {
88 if (PART_List.SelectedItem is ComboItem item)
89 {
90 SelectedItem = item;
91 Notify(nameof(DisplayText));
92 PART_Popup.IsOpen = false;
93 }
94 }
95 }
```
LazyComboBox.cs

```csharp
1 ///
2 /// 下拉弹窗搜索框根据数据显示专用转换器
3 /// 用于将0转换为可见
4 ///
5 public class ZeroToVisibleConverter : IValueConverter
6 {
7 public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
8 {
9 if (value is int i && i == 0)
10 return Visibility.Visible;
11 return Visibility.Collapsed;
12 }
13
14 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
15 => throw new NotImplementedException();
16 }
```
转换器
三、视图页面使用示例
```csharp
xmlns:ctrl="clr-namespace:LazyComboBoxFinalDemo.Controls"
```
```csharp
```
//对应视图的VM中绑定数据:
```csharp
public ILazyDataProvider MyDataProvider { get; }
= new ComboItemProvider();
///
/// 当前选择值
///
[ObservableProperty]
private ComboItem partSelectedItem;
```
四、效果图


本文来自投稿,不代表本站立场,如若转载,请注明出处:http//www.knowhub.vip/share/2/3017
- 热门的技术博文分享
- 1 . ESP实现Web服务器
- 2 . 从零到一:打造高效的金仓社区 API 集成到 MCP 服务方案
- 3 . 使用C#构建一个同时问多个LLM并总结的小工具
- 4 . .NET 原生驾驭 AI 新基建实战系列Milvus ── 大规模 AI 应用的向量数据库首选
- 5 . 在Avalonia/C#中使用依赖注入过程记录
- 6 . [设计模式/Java] 设计模式之工厂方法模式
- 7 . 5. RabbitMQ 消息队列中 Exchanges(交换机) 的详细说明
- 8 . SQL 中的各种连接 JOIN 的区别总结!
- 9 . JavaScript 中防抖和节流的多种实现方式及应用场景
- 10 . SaltStack 远程命令执行中文乱码问题
- 11 . 推荐10个 DeepSeek 神级提示词,建议搜藏起来使用
- 12 . C#基础:枚举、数组、类型、函数等解析
- 13 . VMware平台的Ubuntu部署完全分布式Hadoop环境
- 14 . C# 多项目打包时如何将项目引用转为包依赖
- 15 . Chrome 135 版本开发者工具(DevTools)更新内容
- 16 . 从零创建npm依赖,只需执行一条命令
- 17 . 关于 Newtonsoft.Json 和 System.Text.Json 混用导致的的序列化不识别的问题
- 18 . 大模型微调实战之训练数据集准备的艺术与科学
- 19 . Windows快速安装MongoDB之Mongo实战
- 20 . 探索 C# 14 新功能:实用特性为编程带来便利