基于WPF实现筛选下拉多选控件

2023-05-16 11:05:36 筛选 控件 下拉

WPF 实现筛选下拉多选控件

  • 框架使用.net4 至 .NET6
  • Visual Studio 2022;

创建 MultiSelectionSearchComboBox 继承 Control .

模板中创建两个 ListBox

  • PART_Selector 用来显示所有 Item
  • PART_SearchSelector 用于记录筛选到的 Item

Item 继承 ListBoxItem 并将样式模板修改为 CheckBox

解决 Popup 中 IME 不跟随 TextBox 问题

private static extern IntPtr SetFocus(IntPtr hWnd);

当 TextBox 中 Text 发生改变时通过循环 Item 中的 GetProperty 获取特定 DisplayMemberPath 属性,判断是否包含输入的 SearchText值,如果包含则添加PART_SearchSelector 中,当 SelectionChanged 事件更改时将修改 PART_Selector 中的内容。

实现代码

1)创建 MultiSelectionSearchComboBox.cs 代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Interop;

namespace WPFDevelopers.Controls
{
    [TemplatePart(Name = "PART_TextBox", Type = typeof(TextBox))]
    [TemplatePart(Name = "PART_Popup", Type = typeof(Popup))]
    [TemplatePart(Name = "PART_Selector", Type = typeof(ListBox))]
    [TemplatePart(Name = "PART_SelectAll", Type = typeof(CheckBox))]
    [TemplatePart(Name = "PART_SearchSelector", Type = typeof(ListBox))]
    public class MultiSelectionSearchComboBox : Control
    {
        private const string TextBoxTemplateName = "PART_TextBox";
        private const string PopupTemplateName = "PART_Popup";
        private const string ListBoxTemplateName = "PART_Selector";
        private const string CheckBoxTemplateName = "PART_SelectAll";
        private const string ListBoxTemplateNameSearch = "PART_SearchSelector";

        public static readonly RoutedEvent ClosedEvent =
            EventManager.ReGISterRoutedEvent("Closed",
                RoutingStrategy.Bubble,
                typeof(RoutedEventHandler),
                typeof(MultiSelectionSearchComboBox));

        public static readonly DependencyProperty DisplayMemberPathProperty =
           DependencyProperty.Register("DisplayMemberPath",
               typeof(string),
               typeof(MultiSelectionSearchComboBox),
               new PropertyMetadata(string.Empty));

        public static readonly DependencyProperty SelectedValuePathProperty =
           DependencyProperty.Register("SelectedValuePath",
               typeof(string),
               typeof(MultiSelectionSearchComboBox),
               new PropertyMetadata(string.Empty));

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text",
                typeof(string),
                typeof(MultiSelectionSearchComboBox),
                new PropertyMetadata(string.Empty));

        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(MultiSelectionSearchComboBox),
                new PropertyMetadata());

        public static readonly DependencyProperty ItemsSourceSearchProperty =
            DependencyProperty.Register("ItemsSourceSearch", typeof(IEnumerable), typeof(MultiSelectionSearchComboBox),
                new PropertyMetadata());

        public static readonly DependencyProperty SelectAllContentProperty =
            DependencyProperty.Register("SelectAllContent", typeof(object), typeof(MultiSelectionSearchComboBox),
                new PropertyMetadata("全选"));

        public static readonly DependencyProperty IsSelectAllActiveProperty =
            DependencyProperty.Register("IsSelectAllActive", typeof(bool), typeof(MultiSelectionSearchComboBox),
                new PropertyMetadata(false));

        public static readonly DependencyProperty DelimiterProperty =
            DependencyProperty.Register("Delimiter", typeof(string), typeof(MultiSelectionSearchComboBox),
                new PropertyMetadata(";"));

        public static readonly DependencyProperty IsDropDownOpenProperty =
            DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(MultiSelectionSearchComboBox),
                new PropertyMetadata(false, OnIsDropDownOpenChanged));

        public static readonly DependencyProperty MaxDropDownHeightProperty =
            DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(MultiSelectionSearchComboBox),
                new UIPropertyMetadata(SystemParameters.PrimaryScreenHeight / 3.0, OnMaxDropDownHeightChanged));

        public static readonly DependencyProperty SelectedItemsProperty =
            DependencyProperty.Register("SelectedItems", typeof(IList), typeof(MultiSelectionSearchComboBox),
                new FrameworkPropertyMetadata(null,
                    FrameworkPropertyMetadataoptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
                    OnSelectedItemsChanged));

        public static readonly DependencyProperty SearchWatermarkProperty =
           DependencyProperty.Register("SearchWatermark",
               typeof(string),
               typeof(MultiSelectionSearchComboBox),
               new PropertyMetadata(string.Empty));

        private CheckBox _checkBox;
        private ListBox _listBox;
        private ListBox _listBoxSearch;
        private Popup _popup;
        private TextBox _textBox;
        private List<object> selectedItems;

        private List<object> selectedList;
        private List<object> selectedSearchList;

        private string theLastText;

        static MultiSelectionSearchComboBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiSelectionSearchComboBox),
                new FrameworkPropertyMetadata(typeof(MultiSelectionSearchComboBox)));
        }

        public string Delimiter
        {
            get => (string)GetValue(DelimiterProperty);
            set => SetValue(DelimiterProperty, value);
        }
        public string SelectedValuePath
        {
            get => (string)GetValue(SelectedValuePathProperty);
            set => SetValue(SelectedValuePathProperty, value);
        }
        public string DisplayMemberPath
        {
            get => (string)GetValue(DisplayMemberPathProperty);
            set => SetValue(DisplayMemberPathProperty, value);
        }
        public string Text
        {
            get => (string) GetValue(TextProperty);
            set => SetValue(TextProperty, value);
        }
        public IEnumerable ItemsSource
        {
            get => (IEnumerable)GetValue(ItemsSourceProperty);
            set => SetValue(ItemsSourceProperty, value);
        }
        public IEnumerable ItemsSourceSearch
        {
            get => (IEnumerable) GetValue(ItemsSourceSearchProperty);
            set => SetValue(ItemsSourceSearchProperty, value);
        }

        public object SelectAllContent
        {
            get => GetValue(SelectAllContentProperty);
            set => SetValue(SelectAllContentProperty, value);
        }

        public bool IsSelectAllActive
        {
            get => (bool) GetValue(IsSelectAllActiveProperty);
            set => SetValue(IsSelectAllActiveProperty, value);
        }

        public bool IsDropDownOpen
        {
            get => (bool) GetValue(IsDropDownOpenProperty);
            set => SetValue(IsDropDownOpenProperty, value);
        }

        public double MaxDropDownHeight
        {
            get => (double) GetValue(MaxDropDownHeightProperty);
            set => SetValue(MaxDropDownHeightProperty, value);
        }

        public IList SelectedItems
        {
            get => (IList) GetValue(SelectedItemsProperty);
            set => SetValue(SelectedItemsProperty, value);
        }
        public string SearchWatermark
        {
            get => (string)GetValue(SearchWatermarkProperty);
            set => SetValue(SearchWatermarkProperty, value);
        }

        [DllImport("User32.dll")]
        private static extern IntPtr SetFocus(IntPtr hWnd);

        public event RoutedEventHandler Closed
        {
            add => AddHandler(ClosedEvent, value);
            remove => RemoveHandler(ClosedEvent, value);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            selectedList = new List<object>();
            selectedSearchList = new List<object>();
            selectedItems = new List<object>();
            _textBox = GetTemplateChild(TextBoxTemplateName) as TextBox;
            _popup = GetTemplateChild(PopupTemplateName) as Popup;
            if (_popup != null)
                _popup.GotFocus += _popup_GotFocus;
            _listBox = GetTemplateChild(ListBoxTemplateName) as ListBox;

            _checkBox = GetTemplateChild(CheckBoxTemplateName) as CheckBox;
            _listBoxSearch = GetTemplateChild(ListBoxTemplateNameSearch) as ListBox;
            if (_textBox != null)
            {
                _textBox.TextChanged -= _textbox_TextChanged;
                _textBox.TextChanged += _textbox_TextChanged;
            }

            if (_checkBox != null)
            {
                _checkBox.Checked -= _checkBox_Checked;
                _checkBox.Unchecked -= _checkBox_Unchecked;
                _checkBox.Checked += _checkBox_Checked;
                _checkBox.Unchecked += _checkBox_Unchecked;
            }

            if (_listBox != null)
            {
                _listBox.IsVisibleChanged -= _listBox_IsVisibleChanged;
                _listBox.IsVisibleChanged += _listBox_IsVisibleChanged;
                _listBox.SelectionChanged -= _listBox_SelectionChanged;
                _listBox.SelectionChanged += _listBox_SelectionChanged;
            }

            if (_listBoxSearch != null)
            {
                _listBoxSearch.IsVisibleChanged -= _listBoxSearch_IsVisibleChanged;
                _listBoxSearch.IsVisibleChanged += _listBoxSearch_IsVisibleChanged;
                _listBoxSearch.SelectionChanged -= _listBoxSearch_SelectionChanged;
                _listBoxSearch.SelectionChanged += _listBoxSearch_SelectionChanged;
            }
        }


        private void _listBoxSearch_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if ((bool) e.NewValue)
                UpdateIsChecked(_listBoxSearch);
        }

        private void _listBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if ((bool) e.NewValue)
            {
                foreach (var item in selectedSearchList)
                    if (!_listBox.SelectedItems.Contains(item))
                        _listBox.SelectedItems.Add(item);
                UpdateIsChecked(_listBox);
            }
        }

        private void UpdateIsChecked(ListBox listBox)
        {
            _checkBox.Checked -= _checkBox_Checked;
            if (listBox.Items.Count > 0 && listBox.Items.Count == listBox.SelectedItems.Count)
            {
                if (_checkBox.IsChecked != true)
                    _checkBox.IsChecked = true;
            }
            else
            {
                if (listBox.SelectedItems.Count == 0)
                    _checkBox.IsChecked = false;
                else
                    _checkBox.IsChecked = null;
            }

            _checkBox.Checked += _checkBox_Checked;
        }

        private void _popup_GotFocus(object sender, RoutedEventArgs e)
        {
            var source = (HwndSource) PresentationSource.FromVisual(_popup.Child);
            if (source != null)
            {
                SetFocus(source.Handle);
                _textBox.Focus();
            }
        }

        private void _checkBox_Unchecked(object sender, RoutedEventArgs e)
        {
            if (_listBoxSearch.Visibility == Visibility.Visible)
                _listBoxSearch.UnselectAll();
            else
                _listBox.UnselectAll();
        }

        private void _checkBox_Checked(object sender, RoutedEventArgs e)
        {
            if (_listBoxSearch.Visibility == Visibility.Visible)
                _listBoxSearch.SelectAll();
            else
                _listBox.SelectAll();
        }

        private void Combination()
        {
            var seletedName = new List<string>();
            foreach (var item in _listBox.SelectedItems)
            {
                var name = GetDisplayText(item);
                seletedName.Add(name);
            }

            foreach (var item in _listBoxSearch.SelectedItems)
            {
                if (_listBox.SelectedItems.Contains(item))
                    continue;
                var name = GetDisplayText(item);
                seletedName.Add(name);
            }

            Text = string.Join(Delimiter, seletedName.ToArray());
        }

        private void _listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.RemovedItems.Count > 0)
                foreach (var item in e.RemovedItems)
                    if (_checkBox.IsChecked == true)
                    {
                        _checkBox.Unchecked -= _checkBox_Unchecked;
                        if (_listBox.Items.Count == 1)
                            _checkBox.IsChecked = false;
                        else
                            _checkBox.IsChecked = null;
                        _checkBox.Unchecked += _checkBox_Unchecked;
                    }

            if (e.AddedItems.Count > 0)
                SelectionChecked(_listBox);
            Combination();
            SelectedItems = _listBox.SelectedItems;
        }

        private void _listBoxSearch_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (!_listBoxSearch.IsVisible) return;
            if (e.RemovedItems.Count > 0)
            {
                foreach (var item in e.RemovedItems)
                    if (selectedSearchList.Contains(item))
                        selectedSearchList.Remove(item);
                Combination();
                SelectionChecked(_listBoxSearch);
            }

            if (e.AddedItems.Count > 0)
            {
                foreach (var item in e.AddedItems)
                    if (!selectedSearchList.Contains(item))
                        selectedSearchList.Add(item);
                Combination();
                SelectionChecked(_listBoxSearch);
            }
        }

        private void SelectionChecked(ListBox listbox)
        {
            if (listbox.SelectedItems.Count > 0
                &&
                listbox.Items.Count == listbox.SelectedItems.Count)
            {
                _checkBox.Checked -= _checkBox_Checked;
                _checkBox.IsChecked = true;
                _checkBox.Checked += _checkBox_Checked;
            }
            else
            {
                _checkBox.Checked -= _checkBox_Checked;
                if (listbox.SelectedItems.Count > 0
                    && 
                    listbox.Items.Count == listbox.SelectedItems.Count)
                {
                    if (_checkBox.IsChecked != true)
                        _checkBox.IsChecked = true;
                }
                else
                {
                    if (listbox.SelectedItems.Count == 0)
                        _checkBox.IsChecked = false;
                    else
                        _checkBox.IsChecked = null;
                }

                _checkBox.Checked += _checkBox_Checked;
            }
        }

        private string GetDisplayText(object dataItem, string path = null)
        {
            if (dataItem == null) return string.Empty;
            return GetPropertyValue(dataItem);
        }

        private void _textbox_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(theLastText)) theLastText = _textBox.Text;
            SearchText(_textBox.Text);
        }

        private void SearchText(string _text)
        {
            var text = _text;
            if (string.IsNullOrWhiteSpace(text))
            {
                if (_listBoxSearch.Visibility != Visibility.Collapsed)
                    _listBoxSearch.Visibility = Visibility.Collapsed;
                if (_listBox.Visibility != Visibility.Visible)
                    _listBox.Visibility = Visibility.Visible;
            }
            else
            {
                if(_listBoxSearch.Visibility != Visibility.Visible)
                    _listBoxSearch.Visibility = Visibility.Visible;
                if(_listBox.Visibility != Visibility.Collapsed)
                    _listBox.Visibility = Visibility.Collapsed;
                var listSearch = new List<object>();
                foreach (var item in _listBox.Items)
                {
                    var str = GetPropertyValue(item);
                    if (!string.IsNullOrWhiteSpace(str))
                        if (str.Contains(text.ToUpperInvariant()))
                            listSearch.Add(item);
                }

                foreach (var item in selectedList)
                    if (!listSearch.Contains(item))
                        listSearch.Add(item);

                var lastItem = ItemsSourceSearch;
                ItemsSourceSearch = listSearch;
                SelectionChecked(_listBoxSearch);
                selectedItems.Clear();
                foreach (var item in _listBoxSearch.Items)
                    if (_listBox.SelectedItems.Contains(item))
                        if (!_listBoxSearch.SelectedItems.Contains(item))
                            _listBoxSearch.SelectedItems.Add(item);
            }
        }

        private string GetPropertyValue(object item)
        {
            var result = string.Empty;
            var nameParts = DisplayMemberPath.Split('.');
            if (nameParts.Length == 1)
            {
                var property = item.GetType().GetProperty(DisplayMemberPath);
                if (property != null)
                    return (property.GetValue(item, null) ?? string.Empty).ToString();
            }

            return result.ToUpperInvariant();
        }

        private static void OnIsDropDownOpenChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            var multiSelectionSearchComboBox = o as MultiSelectionSearchComboBox;
            if (multiSelectionSearchComboBox != null)
                multiSelectionSearchComboBox.OnIsOpenChanged((bool) e.OldValue, (bool) e.NewValue);
        }

        protected virtual void OnIsOpenChanged(bool oldValue, bool newValue)
        {
            if (!newValue)
                RaiseRoutedEvent(ClosedEvent);
        }

        private void RaiseRoutedEvent(RoutedEvent routedEvent)
        {
            var args = new RoutedEventArgs(routedEvent, this);
            RaiseEvent(args);
        }

        private static void OnMaxDropDownHeightChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            var comboBox = o as MultiSelectionSearchComboBox;
            if (comboBox != null)
                comboBox.OnMaxDropDownHeightChanged((double) e.OldValue, (double) e.NewValue);
        }

        protected virtual void OnMaxDropDownHeightChanged(double oldValue, double newValue)
        {
        }

        private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var mltiSelectionSearchComboBox = d as MultiSelectionSearchComboBox;

            if (e.NewValue != null)
            {
                var collection = e.NewValue as IList;
                if (collection.Count <= 0) return;
                if (e.OldValue != null && mltiSelectionSearchComboBox._listBox != null)
                {
                    mltiSelectionSearchComboBox._listBox.SelectionChanged -=
                        mltiSelectionSearchComboBox._listBox_SelectionChanged;
                    mltiSelectionSearchComboBox._listBox.SelectedItems.Clear();
                }

                foreach (var item in collection)
                {
                    var name = mltiSelectionSearchComboBox.GetPropertyValue(item);
                    var model = mltiSelectionSearchComboBox._listBox.ItemsSource.OfType<object>().FirstOrDefault(h =>
                        mltiSelectionSearchComboBox.GetPropertyValue(h).ToUpperInvariant()
                            .Equals(name));
                    if (model != null && !mltiSelectionSearchComboBox._listBox.SelectedItems.Contains(item))
                        mltiSelectionSearchComboBox._listBox.SelectedItems.Add(model);

                    if (e.OldValue != null && mltiSelectionSearchComboBox._listBox != null)
                        mltiSelectionSearchComboBox._listBox.SelectionChanged +=
                            mltiSelectionSearchComboBox._listBox_SelectionChanged;
                    mltiSelectionSearchComboBox.Combination();
                }
            }
        }
    }
}

2)创建 MultiSelectionListBox.xaml 代码如下:

using System.Windows;
using System.Windows.Controls;

namespace WPFDevelopers.Controls
{
    public class MultiSelectionListBox:ListBox
    {
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is MultiSelectComboBoxItem;
        }

        protected override DependencyObject GetContainerForItemOverride()
        {
            return new MultiSelectComboBoxItem();
        }

    }
}

3)创建 MultiSelectionSearchComboBox.xaml 代码如下:

<ResourceDictionary xmlns="Http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:controls="clr-namespace:WPFDevelopers.Controls"
                    xmlns:helpers="clr-namespace:WPFDevelopers.Helpers">
    
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Basic/ControlBasic.xaml"/>
        <ResourceDictionary Source="MultiSelectionComboBox.xaml"/>
    </ResourceDictionary.MergedDictionaries>
    
    <Style TargetType="{x:Type controls:MultiSelectionSearchComboBox}">
  <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
  <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
  <Setter Property="ScrollViewer.CanContentScroll" Value="True" />
  <Setter Property="MinWidth" Value="120" />
  <Setter Property="MinHeight" Value="{StaticResource MinHeight}" />
        <Setter Property="Height" Value="{StaticResource MinHeight}" />
  <Setter Property="HorizontalContentAlignment" Value="Left" />
  <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="BorderBrush" Value="{DynamicResource BaseSolidColorBrush}"/>
        <Setter Property="BorderThickness" Value="1"/>
  <Setter Property="Background" Value="{DynamicResource BackgroundSolidColorBrush}"/>
  <Setter Property="Padding" Value="14.5,3,30,3"/>
        <Setter Property="Template">
   <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:MultiSelectionSearchComboBox}">
     <ControlTemplate.Resources>
      <Storyboard x:Key="OpenStoryboard">
       <DoubleAnimation Storyboard.TargetName="PART_DropDown"
             Storyboard.TargetProperty="(Grid.RenderTransfORM).(ScaleTransform.ScaleY)"
             To="1" Duration="00:00:.2"
             EasingFunction="{StaticResource ExponentialEaseOut}"/>
      </Storyboard>
      <Storyboard x:Key="CloseStoryboard">
       <DoubleAnimation Storyboard.TargetName="PART_DropDown"
             Storyboard.TargetProperty="(Grid.RenderTransform).(ScaleTransform.ScaleY)"
             To="0" Duration="00:00:.2"
             EasingFunction="{StaticResource ExponentialEaseOut}"/>
      </Storyboard>
     </ControlTemplate.Resources>
                    <controls:SmallPanel SnapsToDevicePixels="True">
      <Border Name="PART_Border"
        Background="{TemplateBinding Background}"
        BorderBrush="{TemplateBinding BorderBrush}"
        BorderThickness="{TemplateBinding BorderThickness}"
        SnapsToDevicePixels="True"
        CornerRadius="{Binding Path=(helpers:ElementHelper.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}" />
      <ToggleButton x:Name="PART_ToggleButton"
           Template="{StaticResource ComboBoxToggleButton}"
           Style="{x:Null}"
           Focusable="False"
           ClickMode="Release"
           IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/>
      <TextBox Name="PART_EditableTextBox"
         Template="{StaticResource ComboBoxTextBox}"
         HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
         VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
         Margin="{TemplateBinding Padding}"
         Focusable="True"
                                 Foreground="{DynamicResource PrimaryTextSolidColorBrush}"
         Text="{Binding Text,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
         Background="{TemplateBinding Background}"
         SelectionBrush="{DynamicResource WindowBorderBrushSolidColorBrush}"
         IsReadOnly="True" Style="{x:Null}" />
                        <TextBlock x:Name="PART_Watermark"
                                   Text="{Binding Path=(helpers:ElementHelper.Watermark),RelativeSource={RelativeSource TemplatedParent}}"
                                   Foreground="{DynamicResource RegularTextSolidColorBrush}"
                                   HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                   VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                   FontSize="{StaticResource NormalFontSize}"
                                   Margin="{TemplateBinding Padding}" 
                                   Background="Transparent"
                                   TextTrimming="CharacterEllipsis"
                                   IsHitTestVisible="False"
                                   Padding="1,0" 
                                   Visibility="Collapsed"/>
                        <Popup x:Name="PART_Popup"
        AllowsTransparency="True"
        PlacementTarget="{Binding ElementName=PART_ToggleButton}"
        IsOpen="{Binding IsDropDownOpen,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
        Placement="Bottom" StaysOpen="False">
                            <controls:SmallPanel x:Name="PART_DropDown"
          MinWidth="{TemplateBinding FrameworkElement.ActualWidth}"
        Margin="24,2,24,24"
        MaxHeight="{TemplateBinding MaxDropDownHeight}"
        RenderTransformOrigin=".5,0"
        SnapsToDevicePixels="True">
                                <controls:SmallPanel.RenderTransform>
         <ScaleTransform ScaleY="0"/>
        </controls:SmallPanel.RenderTransform>
        <Border
         Name="PART_DropDownBorder"
         Background="{TemplateBinding Background}"
         BorderBrush="{TemplateBinding BorderBrush}"
         BorderThickness="{TemplateBinding BorderThickness}"
         SnapsToDevicePixels="True"
                                 CornerRadius="{Binding Path=(helpers:ElementHelper.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}"
         UseLayoutRounding="True"
         Effect="{StaticResource PopupShadowDepth}"/>
        <Grid ClipToBounds="False"
           Margin="0,8" >
         <Grid.RowDefinitions>
          <RowDefinition Height="Auto"/>
                                        <RowDefinition Height="Auto"/>
          <RowDefinition/>
         </Grid.RowDefinitions>
                                    <TextBox x:Name="PART_TextBox"
                                             Margin="6,0"
                                             helpers:ElementHelper.Watermark="{Binding SearchWatermark,RelativeSource={RelativeSource TemplatedParent}}"/>
                                    <CheckBox x:Name="PART_SelectAll"
                                              Grid.Row="1"
                                              Margin="5,4"
                                              Visibility="{TemplateBinding IsSelectAllActive,Converter={StaticResource bool2VisibilityConverter}}"
                                              Content="{TemplateBinding SelectAllContent}"/>
                                    <controls:MultiSelectListBox x:Name="PART_Selector"
                                             Grid.Row="2"
                                             BorderThickness="1,0,1,0"
                                             DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
                                             SelectedValuePath="{TemplateBinding SelectedValuePath}"
                                             MinHeight="{TemplateBinding MinHeight}"
            ItemsSource="{TemplateBinding ItemsSource}"
                                             SelectionMode="Multiple"
                                             VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                             HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                             ItemContainerStyle="{StaticResource DefaultMultiSelectComboBoxItem}"
                                             ScrollViewer.HorizontalScrollBarVisibility="{Binding ScrollViewer.HorizontalScrollBarVisibility, RelativeSource={RelativeSource TemplatedParent}}"
                                             ScrollViewer.CanContentScroll="{Binding ScrollViewer.CanContentScroll, RelativeSource={RelativeSource TemplatedParent}}"
                                             BorderBrush="{TemplateBinding BorderBrush}">
                                    </controls:MultiSelectListBox>
                                    <controls:MultiSelectListBox x:Name="PART_SearchSelector"
                                             Grid.Row="2"
                                             BorderThickness="1,0,1,0"
                                             BorderBrush="{TemplateBinding BorderBrush}"
                                             DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
                                             SelectedValuePath="{TemplateBinding SelectedValuePath}"
                                             MinHeight="{TemplateBinding MinHeight}"
            ItemsSource="{TemplateBinding ItemsSourceSearch}"
                                             SelectionMode="Multiple"
                                             VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                             HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                             ItemContainerStyle="{StaticResource DefaultMultiSelectComboBoxItem}"
                                             ScrollViewer.HorizontalScrollBarVisibility="{Binding ScrollViewer.HorizontalScrollBarVisibility, RelativeSource={RelativeSource TemplatedParent}}"
                                             ScrollViewer.CanContentScroll="{Binding ScrollViewer.CanContentScroll, RelativeSource={RelativeSource TemplatedParent}}"
                                             Visibility="Collapsed">
                                    </controls:MultiSelectListBox>
                                </Grid>
       </controls:SmallPanel>
      </Popup>
     </controls:SmallPanel>
     <ControlTemplate.Triggers>
      <Trigger SourceName="PART_ToggleButton" Property="IsChecked" Value="True">
       <Trigger.EnterActions>
        <BeginStoryboard x:Name="BeginStoryboardOpenStoryboard" Storyboard="{StaticResource OpenStoryboard}" />
       </Trigger.EnterActions>
       <Trigger.ExitActions>
        <StopStoryboard BeginStoryboardName="BeginStoryboardOpenStoryboard" />
       </Trigger.ExitActions>
      </Trigger>
      <Trigger SourceName="PART_ToggleButton" Property="IsChecked" Value="False">
       <Trigger.EnterActions>
        <BeginStoryboard x:Name="BeginStoryboardCloseStoryboard" Storyboard="{StaticResource CloseStoryboard}" />
       </Trigger.EnterActions>
       <Trigger.ExitActions>
        <StopStoryboard BeginStoryboardName="BeginStoryboardCloseStoryboard" />
       </Trigger.ExitActions>
      </Trigger>
      <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="BorderBrush" TargetName="PART_Border" Value="{DynamicResource PrimaryNormalSolidColorBrush}"/>
      </Trigger>
      <Trigger SourceName="PART_Popup" Property="AllowsTransparency" Value="True">
       <Setter TargetName="PART_DropDownBorder"  Property="Margin" Value="0,2,0,0" />
      </Trigger>
                        <Trigger Property="Text" Value="">
                            <Setter Property="Visibility" TargetName="PART_Watermark" Value="Visible"/>
                        </Trigger>
                        <Trigger Property="Text" Value="{x:Null}">
                            <Setter Property="Visibility" TargetName="PART_Watermark" Value="Visible"/>
                        </Trigger>
     </ControlTemplate.Triggers>
    </ControlTemplate>
   </Setter.Value>
  </Setter>
 </Style>
</ResourceDictionary>

4)创建 MultiSelectSearchComboBoxExample.xaml 代码如下:

 <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Button Content="获取选中" 
                    VerticalAlignment="Bottom"
                    HorizontalAlignment="Center"
                    Click="Button_Click"
                    Margin="0,20,0,0"
                    Style="{StaticResource SuccessPrimaryButton}"/>
            <UniformGrid Columns="2" Rows="2" Grid.Row="1">
                <wd:MultiSelectionSearchComboBox
                VerticalContentAlignment="Center" 
                HorizontalAlignment="Center"
                    ItemsSource="{Binding Drawings}" 
                DisplayMemberPath="Number"
                SelectedValuePath="Index"
                Width="200" Delimiter=","
                IsSelectAllActive="True">
                    <wd:MultiSelectionSearchComboBox.DataContext>
                        <vm:DrawingExampleVM/>
                    </wd:MultiSelectionSearchComboBox.DataContext>
                </wd:MultiSelectionSearchComboBox>
                <wd:MultiSelectionSearchComboBox
                    Name="MyMultiSelectionSearchComboBox2"
                VerticalContentAlignment="Center" 
                HorizontalAlignment="Center"
                ItemsSource="{Binding Drawings}" 
                DisplayMemberPath="Number"
                SelectedValuePath="Index"
                Width="200" Delimiter="^"
                IsSelectAllActive="True"
                wd:ElementHelper.Watermark="MultiSelectionSearchComboBox"
                SearchWatermark="请输入搜索内容">
                    <wd:MultiSelectionSearchComboBox.DataContext>
                        <vm:DrawingExampleVM/>
                    </wd:MultiSelectionSearchComboBox.DataContext>
                </wd:MultiSelectionSearchComboBox>
            </UniformGrid>
        </Grid>

5)创建 MultiSelectSearchComboBoxExample.xaml.cs 代码如下:

using System.Windows;
using System.Windows.Controls;

namespace WPFDevelopers.Samples.ExampleViews
{
    /// <summary>
    /// MultiSelectComboBoxExample.xaml 的交互逻辑
    /// </summary>
    public partial class MultiSelectSearchComboBoxExample : UserControl
    {
        public MultiSelectSearchComboBoxExample()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            WPFDevelopers.Controls.MessageBox.Show($"{MyMultiSelectionSearchComboBox2.Text} \r\n总共选中:{MyMultiSelectionSearchComboBox2.SelectedItems.Count} 条","选中内容",MessageBoxButton.OK,MessageBoxImage.Information);      
        }
    }
}

效果图

以上就是基于WPF实现筛选下拉多选控件的详细内容,更多关于WPF筛选下拉多选控件的资料请关注其它相关文章!

相关文章