实现思路:

  1. 继承自ComboBox类,重写其模板,实现下拉框的样式和行为。

  2. 在模板中添加一个TextBox控件,用于输入筛选条件。

  3. 在模板中添加一个ItemsControl控件,用于显示下拉列表。

  4. 在自定义下拉框类中添加一个ItemsSource属性,用于绑定数据源。

  5. 在自定义下拉框类中添加一个FilterText属性,用于绑定筛选条件。

  6. 在自定义下拉框类中添加一个Filter方法,用于根据筛选条件过滤数据源,并更新下拉列表。

  7. 在自定义下拉框类中添加一个Columns属性,用于设置下拉列表的列数和列宽。

  8. 在模板中使用Grid控件实现下拉列表的分列显示。

  9. 在模板中使用Trigger控件实现下拉列表的弹出和收起。

  10. 在自定义下拉框类中重写OnApplyTemplate方法,获取模板中的控件引用,并绑定事件。

  11. 在自定义下拉框类中重写MeasureOverride和ArrangeOverride方法,实现下拉列表的自适应宽度和高度。

下面是完整的代码实现:

public class MultiColumnComboBox : ComboBox
{
    private TextBox _filterTextBox;
    private ItemsControl _itemsControl;
    private bool _isFiltering = false;

    public static readonly DependencyProperty ColumnsProperty =
        DependencyProperty.Register("Columns", typeof(int), typeof(MultiColumnComboBox), new PropertyMetadata(1));
    public int Columns
    {
        get { return (int)GetValue(ColumnsProperty); }
        set { SetValue(ColumnsProperty, value); }
    }

    public static readonly DependencyProperty ColumnWidthProperty =
        DependencyProperty.Register("ColumnWidth", typeof(double), typeof(MultiColumnComboBox), new PropertyMetadata(100.0));
    public double ColumnWidth
    {
        get { return (double)GetValue(ColumnWidthProperty); }
        set { SetValue(ColumnWidthProperty, value); }
    }

    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(MultiColumnComboBox), new PropertyMetadata(null, OnItemsSourceChanged));
    public new IEnumerable ItemsSource
    {
        get { return (IEnumerable)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public static readonly DependencyProperty FilterTextProperty =
        DependencyProperty.Register("FilterText", typeof(string), typeof(MultiColumnComboBox), new PropertyMetadata("", OnFilterTextChanged));
    public string FilterText
    {
        get { return (string)GetValue(FilterTextProperty); }
        set { SetValue(FilterTextProperty, value); }
    }

    private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var comboBox = (MultiColumnComboBox)d;
        comboBox.Filter();
    }

    private static void OnFilterTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var comboBox = (MultiColumnComboBox)d;
        if (!comboBox._isFiltering)
        {
            comboBox.Filter();
        }
    }

    public MultiColumnComboBox()
    {
        DefaultStyleKey = typeof(MultiColumnComboBox);
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        _filterTextBox = GetTemplateChild("PART_FilterTextBox") as TextBox;
        _itemsControl = GetTemplateChild("PART_ItemsControl") as ItemsControl;
        if (_filterTextBox != null)
        {
            _filterTextBox.TextChanged += OnFilterTextBoxTextChanged;
        }
    }

    protected override Size MeasureOverride(Size constraint)
    {
        var size = base.MeasureOverride(constraint);
        if (_itemsControl != null)
        {
            var width = Columns * ColumnWidth;
            var height = Math.Min(Items.Count, 10) * _itemsControl.ItemContainerGenerator.ContainerFromIndex(0).ActualHeight;
            _itemsControl.Measure(new Size(width, height));
            size.Width = Math.Max(size.Width, width);
            size.Height = Math.Max(size.Height, _itemsControl.DesiredSize.Height + _filterTextBox.DesiredSize.Height);
        }
        return size;
    }

    protected override Size ArrangeOverride(Size arrangeBounds)
    {
        var size = base.ArrangeOverride(arrangeBounds);
        if (_itemsControl != null && IsDropDownOpen)
        {
            var width = Columns * ColumnWidth;
            var height = Math.Min(Items.Count, 10) * _itemsControl.ItemContainerGenerator.ContainerFromIndex(0).ActualHeight;
            var x = 0.0;
            var y = _filterTextBox.ActualHeight;
            _itemsControl.Arrange(new Rect(x, y, width, height));
        }
        return size;
    }

    private void OnFilterTextBoxTextChanged(object sender, TextChangedEventArgs e)
    {
        FilterText = _filterTextBox.Text;
    }

    private void Filter()
    {
        if (ItemsSource == null || _itemsControl == null)
        {
            return;
        }
        _isFiltering = true;
        var view = CollectionViewSource.GetDefaultView(ItemsSource);
        view.Filter = item =>
        {
            if (string.IsNullOrEmpty(FilterText))
            {
                return true;
            }
            var properties = item.GetType().GetProperties();
            foreach (var property in properties)
            {
                var value = property.GetValue(item)?.ToString();
                if (value != null && value.IndexOf(FilterText, StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    return true;
                }
            }
            return false;
        };
        Items.Clear();
        foreach (var item in view)
        {
            Items.Add(item);
        }
        _isFiltering = false;
    }
}

在模板中,我们使用了一个Grid控件来实现下拉列表的分列显示,其中每个单元格都是一个ItemsControl控件,用于显示数据。我们还使用了一个Trigger控件来实现下拉列表的弹出和收起,当IsDropDownOpen属性为true时,显示下拉列表,否则隐藏下拉列表。下面是完整的模板代码:

<Style TargetType="{x:Type local:MultiColumnComboBox}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MultiColumnComboBox}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>
                    <TextBox x:Name="PART_FilterTextBox" Grid.Row="0" Margin="2" VerticalAlignment="Center"/>
                    <Grid x:Name="PART_ItemsGrid" Grid.Row="1" Visibility="Collapsed">
                        <Grid.Style>
                            <Style TargetType="{x:Type Grid}">
                                <Style.Triggers>
                                    <Trigger Property="IsVisible" Value="True">
                                        <Setter Property="Visibility" Value="Visible"/>
                                        <Setter Property="Grid.RowSpan" Value="2"/>
                                    </Trigger>
                                </Style.Triggers>
                            </Style>
                        </Grid.Style>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <ItemsControl x:Name="PART_ItemsControl1" Grid.Column="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="2" BorderThickness="0" ItemsSource="{TemplateBinding ItemsSource}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Property1}" Margin="2"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                        <ItemsControl x:Name="PART_ItemsControl2" Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="2" BorderThickness="0" ItemsSource="{TemplateBinding ItemsSource}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Property2}" Margin="2"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                        <ItemsControl x:Name="PART_ItemsControl3" Grid.Column="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="2" BorderThickness="0" ItemsSource="{TemplateBinding ItemsSource}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Property3}" Margin="2"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                        <ItemsControl x:Name="PART_ItemsControl4" Grid.Column="3" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="2" BorderThickness="0" ItemsSource="{TemplateBinding ItemsSource}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Property4}" Margin="2"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </Grid>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsDropDownOpen" Value="True">
                        <Setter TargetName="PART_ItemsGrid" Property="Visibility" Value="Visible"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
WPF 自定义可分列下拉框:模糊搜索、高性能数据展示

原文地址: https://www.cveoy.top/t/topic/kqHM 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录