1 public partial class LazyComboBox : UserControl, INotifyPropertyChanged
2 {
3 public static readonly DependencyProperty ItemsProviderProperty =
4 DependencyProperty.Register(nameof(ItemsProvider), typeof(ILazyDataProvider<ComboItem>),
5 typeof(LazyComboBox), new PropertyMetadata(null));
6
7 public ILazyDataProvider<ComboItem> ItemsProvider
8 {
9 get => (ILazyDataProvider<ComboItem>)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<ComboItem> Items { get; } = new ObservableCollection<ComboItem>();
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 }