欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

WPF封裝實(shí)現(xiàn)懶加載下拉列表控件(支持搜索)

 更新時(shí)間:2025年04月30日 11:11:33   作者:流浪阿丁  
WPF這種懶加載的控件未找到現(xiàn)成可用的組件,所以本文我們就自己封裝一個(gè)懶加載和支持模糊過(guò)濾的下拉列表控件,有需要的小伙伴可以參考一下

因?yàn)轫?xiàng)目中PC端前端針對(duì)基礎(chǔ)數(shù)據(jù)選擇時(shí)的下拉列表做了懶加載控件,PC端使用現(xiàn)成的組件,為保持兩端的選擇方式統(tǒng)一,WPF客戶(hù)端上也需要使用懶加載的下拉選擇。

WPF這種懶加載的控件未找到現(xiàn)成可用的組件,于是自己封裝了一個(gè)懶加載和支持模糊過(guò)濾的下拉列表控件,控件使用了虛擬化加載,解決了大數(shù)據(jù)量時(shí)的渲染數(shù)據(jù)卡頓問(wèn)題,下面是完整的代碼和示例:

一、控件所需的關(guān)鍵實(shí)體類(lèi)

/// <summary>
/// 下拉項(xiàng)
/// </summary>
public class ComboItem
{
    /// <summary>
    /// 實(shí)際存儲(chǔ)值
    /// </summary>
    public string? ItemValue { get; set; }
    /// <summary>
    /// 顯示文本
    /// </summary>
    public string? ItemText { get; set; }
}

/// <summary>
/// 懶加載下拉數(shù)據(jù)源提供器
/// </summary>
public class ComboItemProvider : ILazyDataProvider<ComboItem>
{
    private readonly List<ComboItem> _all;
    public ComboItemProvider()
    {
        _all = Enumerable.Range(1, 1000000)
                         .Select(i => new ComboItem { ItemValue = i.ToString(), ItemText = $"Item {i}" })
                         .ToList();
    }
    public async Task<PageResult<ComboItem>> FetchAsync(string filter, int pageIndex, int pageSize)
    {
        await Task.Delay(100);
        var q = _all.AsQueryable();
        if (!string.IsNullOrEmpty(filter))
            q = q.Where(x => x.ItemText.Contains(filter, StringComparison.OrdinalIgnoreCase));
        var page = q.Skip(pageIndex * pageSize).Take(pageSize).ToList();
        bool has = q.Count() > (pageIndex + 1) * pageSize;
        return new PageResult<ComboItem> { Items = page, HasMore = has };
    }
}

/// <summary>
/// 封裝獲取數(shù)據(jù)的接口
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ILazyDataProvider<T>
{
    Task<PageResult<T>> FetchAsync(string filter, int pageIndex, int pageSize);
}

/// <summary>
/// 懶加載下拉分頁(yè)對(duì)象
/// </summary>
/// <typeparam name="T"></typeparam>
public class PageResult<T>
{
    public IReadOnlyList<T> Items { get; set; }
    public bool HasMore { get; set; }
}

二、懶加載控件視圖和數(shù)據(jù)邏輯

<UserControl
    x:Class="LazyComboBoxFinalDemo.Controls.LazyComboBox"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:LazyComboBoxFinalDemo.Controls">
    <UserControl.Resources>
        <local:ZeroToVisibleConverter x:Key="ZeroToVisibleConverter" />
        <!--  清除按鈕樣式:透明背景、圖標(biāo)  -->
        <Style x:Key="ClearButtonStyle" TargetType="Button">
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="Padding" Value="0" />
            <Setter Property="Cursor" Value="Hand" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!--  ToggleButton 樣式  -->
        <Style x:Key="ComboToggleButtonStyle" TargetType="ToggleButton">
            <Setter Property="Background" Value="White" />
            <Setter Property="BorderBrush" Value="#CCC" />
            <Setter Property="BorderThickness" Value="1" />
            <Setter Property="Padding" Value="4" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ToggleButton">
                        <Border
                            Padding="{TemplateBinding Padding}"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="4">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition />
                                    <ColumnDefinition Width="20" />
                                    <ColumnDefinition Width="20" />
                                </Grid.ColumnDefinitions>
                                <!--  按鈕文本  -->
                                <ContentPresenter
                                    Grid.Column="0"
                                    Margin="4,0,0,0"
                                    VerticalAlignment="Center"
                                    Content="{TemplateBinding Content}" />
                                <!--  箭頭  -->
                                <Path
                                    x:Name="Arrow"
                                    Grid.Column="2"
                                    VerticalAlignment="Center"
                                    Data="M 0 0 L 4 4 L 8 0 Z"
                                    Fill="Gray"
                                    RenderTransformOrigin="0.5,0.5">
                                    <Path.RenderTransform>
                                        <RotateTransform Angle="0" />
                                    </Path.RenderTransform>
                                </Path>
                                <!--  清除按鈕  -->
                                <Button
                                    x:Name="PART_ClearButton"
                                    Grid.Column="1"
                                    Width="16"
                                    Height="16"
                                    VerticalAlignment="Center"
                                    Click="OnClearClick"
                                    Style="{StaticResource ClearButtonStyle}"
                                    Visibility="Collapsed">
                                    <Path
                                        Data="M0,0 L8,8 M8,0 L0,8"
                                        Stroke="Gray"
                                        StrokeThickness="2" />
                                </Button>

                            </Grid>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="PART_ClearButton" Property="Visibility" Value="Visible" />
                            </Trigger>
                            <DataTrigger Binding="{Binding IsOpen, ElementName=PART_Popup}" Value="True">
                                <Setter TargetName="Arrow" Property="RenderTransform">
                                    <Setter.Value>
                                        <RotateTransform Angle="180" />
                                    </Setter.Value>
                                </Setter>
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!--  ListBoxItem 懸停/選中樣式  -->
        <Style TargetType="ListBoxItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Border
                            x:Name="Bd"
                            Padding="4"
                            Background="Transparent">
                            <ContentPresenter />
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="Bd" Property="Background" Value="#EEE" />
                            </Trigger>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter TargetName="Bd" Property="Background" Value="#CCC" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!--  Popup 邊框  -->
        <Style x:Key="PopupBorder" TargetType="Border">
            <Setter Property="CornerRadius" Value="5" />
            <Setter Property="Background" Value="White" />
            <Setter Property="BorderBrush" Value="#CCC" />
            <Setter Property="BorderThickness" Value="2" />
            <Setter Property="Padding" Value="10" />
        </Style>
        <!--  水印 TextBox  -->
        <Style x:Key="WatermarkTextBox" TargetType="TextBox">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="TextBox">
                        <Grid>
                            <ScrollViewer x:Name="PART_ContentHost" />
                            <TextBlock
                                Margin="4,2,0,0"
                                Foreground="Gray"
                                IsHitTestVisible="False"
                                Text="搜索…"
                                Visibility="{Binding Text.Length, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ZeroToVisibleConverter}}" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>
    <Grid>
        <ToggleButton
            x:Name="PART_Toggle"
            Click="OnToggleClick"
            Style="{StaticResource ComboToggleButtonStyle}">
            <Grid>
                <!--  顯示文本  -->
                <TextBlock
                    Margin="4,0,24,0"
                    VerticalAlignment="Center"
                    Text="{Binding DisplayText, RelativeSource={RelativeSource AncestorType=UserControl}}" />
                <!--  箭頭已在模板內(nèi),略  -->
            </Grid>
        </ToggleButton>
        <Popup
            x:Name="PART_Popup"
            AllowsTransparency="True"
            PlacementTarget="{Binding ElementName=PART_Toggle}"
            PopupAnimation="Fade"
            StaysOpen="False">
            <!--  AllowsTransparency 啟用透明,PopupAnimation 彈窗動(dòng)畫(huà)  -->
            <Border Width="{Binding ActualWidth, ElementName=PART_Toggle}" Style="{StaticResource PopupBorder}">
                <Border.Effect>
                    <DropShadowEffect
                        BlurRadius="15"
                        Opacity="0.7"
                        ShadowDepth="0"
                        Color="#e6e6e6" />
                </Border.Effect>
                <Grid Height="300">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <!--  搜索框  -->
                    <TextBox
                        x:Name="PART_SearchBox"
                        Margin="0,0,0,8"
                        VerticalAlignment="Center"
                        Style="{StaticResource WatermarkTextBox}"
                        TextChanged="OnSearchChanged" />
                    <!--  列表  -->
                    <ListBox
                        x:Name="PART_List"
                        Grid.Row="1"
                        DisplayMemberPath="ItemText"
                        ItemsSource="{Binding Items, RelativeSource={RelativeSource AncestorType=UserControl}}"
                        ScrollViewer.CanContentScroll="True"
                        ScrollViewer.ScrollChanged="OnScroll"
                        SelectionChanged="OnSelectionChanged"
                        VirtualizingStackPanel.IsVirtualizing="True"
                        VirtualizingStackPanel.VirtualizationMode="Recycling" />
                </Grid>
            </Border>
        </Popup>
    </Grid>
</UserControl>

LazyComboBox.cs

  public partial class LazyComboBox : UserControl, INotifyPropertyChanged
  {
      public static readonly DependencyProperty ItemsProviderProperty =
           DependencyProperty.Register(nameof(ItemsProvider), typeof(ILazyDataProvider<ComboItem>),
               typeof(LazyComboBox), new PropertyMetadata(null));
  
       public ILazyDataProvider<ComboItem> ItemsProvider
       {
           get => (ILazyDataProvider<ComboItem>)GetValue(ItemsProviderProperty);
          set => SetValue(ItemsProviderProperty, value);
      }
 
      public static readonly DependencyProperty SelectedItemProperty =
          DependencyProperty.Register(nameof(SelectedItem), typeof(ComboItem),
              typeof(LazyComboBox),
              new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemChanged));
 
      public ComboItem SelectedItem
      {
          get => (ComboItem)GetValue(SelectedItemProperty);
          set => SetValue(SelectedItemProperty, value);
      }
 
      private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
          if (d is LazyComboBox ctrl)
          {
              ctrl.Notify(nameof(DisplayText));
          }
      }
 
      public ObservableCollection<ComboItem> Items { get; } = new ObservableCollection<ComboItem>();
      private string _currentFilter = "";
      private int _currentPage = 0;
      private const int PageSize = 30;
      public bool HasMore { get; private set; }
      public string DisplayText => SelectedItem?.ItemText ?? "請(qǐng)選擇...";
 
      public LazyComboBox()
      {
          InitializeComponent();
      }
 
      public event PropertyChangedEventHandler PropertyChanged;
      private void Notify(string prop) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
 
      private async void LoadPage(int pageIndex)
      {
          if (ItemsProvider == null) return;
          var result = await ItemsProvider.FetchAsync(_currentFilter, pageIndex, PageSize);
          if (pageIndex == 0) Items.Clear();
          foreach (var it in result.Items) Items.Add(it);
          HasMore = result.HasMore;
          PART_Popup.IsOpen = true;
     }
 
      private void OnClearClick(object sender, RoutedEventArgs e)
      {
          e.Handled = true;  // 阻止事件冒泡,不觸發(fā) Toggle 打開(kāi)
         SelectedItem = null; // 清空選中
          Notify(nameof(DisplayText)); // 刷新按鈕文本
         PART_Popup.IsOpen = false;   // 確保關(guān)掉彈窗
      }
 
      private void OnToggleClick(object sender, RoutedEventArgs e)
      {
          _currentPage = 0;
          LoadPage(0);
          PART_Popup.IsOpen = true;
      }
 
      private void OnSearchChanged(object sender, TextChangedEventArgs e)
      {
          _currentFilter = PART_SearchBox.Text;
          _currentPage = 0;
          LoadPage(0);
      }

      private void OnScroll(object sender, ScrollChangedEventArgs e)
      {
         if (!HasMore) return;
          if (e.VerticalOffset >= e.ExtentHeight - e.ViewportHeight - 2)
              LoadPage(++_currentPage);
      }
 
      private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
      {
          if (PART_List.SelectedItem is ComboItem item)
         {
              SelectedItem = item;
             Notify(nameof(DisplayText));
             PART_Popup.IsOpen = false;
          }
     }
  }

轉(zhuǎn)換器

 /// <summary>
 /// 下拉彈窗搜索框根據(jù)數(shù)據(jù)顯示專(zhuān)用轉(zhuǎn)換器
 /// 用于將0轉(zhuǎn)換為可見(jiàn)
 /// </summary>
 public class ZeroToVisibleConverter : IValueConverter
  {
      public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
      {
          if (value is int i && i == 0)
             return Visibility.Visible;
         return Visibility.Collapsed;
     }
 
     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         => throw new NotImplementedException();
}

三、視圖頁(yè)面使用示例

xmlns:ctrl="clr-namespace:LazyComboBoxFinalDemo.Controls"
<Grid Margin="10">
    <ctrl:LazyComboBox
        Width="200"
        Height="40"
        ItemsProvider="{Binding MyDataProvider}"
        SelectedItem="{Binding PartSelectedItem, Mode=TwoWay}" />
</Grid>

對(duì)應(yīng)視圖的VM中綁定數(shù)據(jù):

public ILazyDataProvider<ComboItem> MyDataProvider { get; }
    = new ComboItemProvider();

/// <summary>
/// 當(dāng)前選擇值
/// </summary>
[ObservableProperty]
private ComboItem partSelectedItem;

四、效果圖

以上就是WPF封裝實(shí)現(xiàn)懶加載下拉列表控件(支持搜索)的詳細(xì)內(nèi)容,更多關(guān)于WPF下拉列表控件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C#實(shí)現(xiàn)格式化SQL語(yǔ)句的示例代碼

    C#實(shí)現(xiàn)格式化SQL語(yǔ)句的示例代碼

    這篇文章主要為大家詳細(xì)介紹了C#如何實(shí)現(xiàn)格式化SQL語(yǔ)句的功能,文中的示例代碼簡(jiǎn)潔易懂,具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-08-08
  • C#實(shí)現(xiàn)NPOI的Excel導(dǎo)出詳解

    C#實(shí)現(xiàn)NPOI的Excel導(dǎo)出詳解

    這篇文章主要介紹了C#實(shí)現(xiàn)NPOI的Excel導(dǎo)出的示例代碼,文中的實(shí)現(xiàn)過(guò)程講解詳細(xì),對(duì)我們的學(xué)習(xí)或工作有一定的幫助,感興趣的可以跟隨小編一起學(xué)習(xí)一下
    2022-01-01
  • 詳解c# 線程同步

    詳解c# 線程同步

    這篇文章主要介紹了c# 線程同步的相關(guān)資料,文中講解非常細(xì)致,示例代碼幫助大家更好的理解和學(xué)習(xí)c# 多線程,感興趣的朋友可以了解下
    2020-07-07
  • Unity3D實(shí)現(xiàn)人物移動(dòng)示例

    Unity3D實(shí)現(xiàn)人物移動(dòng)示例

    這篇文章主要為大家詳細(xì)介紹了Unity3D實(shí)現(xiàn)人物移動(dòng)示例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-01-01
  • C# 中的IComparable和IComparer的使用及區(qū)別

    C# 中的IComparable和IComparer的使用及區(qū)別

    這篇文章主要介紹了C# 中的IComparable和IComparer的使用及區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • C#監(jiān)控文件夾變化的方法

    C#監(jiān)控文件夾變化的方法

    這篇文章主要介紹了C#監(jiān)控文件夾變化的方法,通過(guò)FileSystemWatcher類(lèi)的方法來(lái)實(shí)現(xiàn)對(duì)文件夾的監(jiān)控,是非常實(shí)用的技巧,需要的朋友可以參考下
    2014-11-11
  • c#數(shù)據(jù)綁定之將datatabel的data添加listView

    c#數(shù)據(jù)綁定之將datatabel的data添加listView

    這篇文章主要介紹了c#將DataTabel的data添加ListView的示例,實(shí)現(xiàn)功能是通過(guò)響應(yīng)UI Textbox 的值向ListView 綁定新添加的紀(jì)錄。 ,需要的朋友可以參考下
    2014-04-04
  • 淺析JAVA中過(guò)濾器、監(jiān)聽(tīng)器、攔截器的區(qū)別

    淺析JAVA中過(guò)濾器、監(jiān)聽(tīng)器、攔截器的區(qū)別

    本文通過(guò)代碼分析和文字說(shuō)明的方式給大家淺析JAVA中過(guò)濾器、監(jiān)聽(tīng)器、攔截器的區(qū)別,感興趣的朋友一起看下吧
    2015-09-09
  • C#開(kāi)啟線程的四種方式小結(jié)

    C#開(kāi)啟線程的四種方式小結(jié)

    在C#中,多線程編程是處理并發(fā)操作、提高程序性能的重要手段,C#提供了多種方式來(lái)創(chuàng)建和管理線程,下面將介紹四種常用的開(kāi)啟線程的方法,并附上相應(yīng)的實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2024-06-06
  • C#模擬瀏覽器實(shí)現(xiàn)自動(dòng)操作

    C#模擬瀏覽器實(shí)現(xiàn)自動(dòng)操作

    這篇文章主要為大家詳細(xì)介紹了如何使用C#實(shí)現(xiàn)模擬瀏覽器實(shí)現(xiàn)自動(dòng)操作,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-11-11

最新評(píng)論