基于WPF实现多选下拉控件的示例代码

Gitana ·
更新时间:2024-09-20
· 1643 次阅读

WPF 实现多选下拉控件

框架使用.NET40

Visual Studio 2022;

创建控件 MultiSelectComboBox 继承 ListBox 。

依赖属性 IsSelectAllActive 是否支持显示全选,默认 false 。

依赖属性 SelectAllContent 全选控件的 Content,默认显示 全选 。

依赖属性 Delimiter 分隔符,默认显示 ; 。

依赖属性 Text 显示选择的所有 Item 。

其他与ComboBox 依赖属性一致。

实现代码

1) MultiSelectComboBox.xaml 代码如下:

using System; using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Threading; namespace WPFDevelopers.Controls {     public class MultiSelectComboBox : ListBox     {         private const string PART_Popup = "PART_Popup";         private const string PART_CheckBoxAll = "PART_CheckBoxAll";         public static readonly DependencyProperty IsDropDownOpenProperty =             DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(MultiSelectComboBox),                 new PropertyMetadata(false));         public static readonly DependencyProperty MaxDropDownHeightProperty             = DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(MultiSelectComboBox),                 new PropertyMetadata(SystemParameters.PrimaryScreenHeight / 3));         public static readonly DependencyProperty SelectAllContentProperty =             DependencyProperty.Register("SelectAllContent", typeof(object), typeof(MultiSelectComboBox),                 new PropertyMetadata("全选"));         public static readonly DependencyProperty IsSelectAllActiveProperty =             DependencyProperty.Register("IsSelectAllActive", typeof(bool), typeof(MultiSelectComboBox),                 new PropertyMetadata(false));         public static readonly DependencyProperty DelimiterProperty =             DependencyProperty.Register("Delimiter", typeof(string), typeof(MultiSelectComboBox),                 new PropertyMetadata(";"));         public static readonly DependencyProperty TextProperty =             DependencyProperty.Register("Text", typeof(string), typeof(MultiSelectComboBox),                 new PropertyMetadata(string.Empty, OnTextChanged));         private bool _ignoreTextValueChanged;         private MultiSelectComboBoxItem _multiSelectComboBoxItem;         private Popup _popup;         public bool IsDropDownOpen         {             get => (bool)GetValue(IsDropDownOpenProperty);             set => SetValue(IsDropDownOpenProperty, value);         }         [Bindable(true)]         [Category("Layout")]         [TypeConverter(typeof(LengthConverter))]         public double MaxDropDownHeight         {             get => (double)GetValue(MaxDropDownHeightProperty);             set => SetValue(MaxDropDownHeightProperty, value);         }         public object SelectAllContent         {             get => GetValue(SelectAllContentProperty);             set => SetValue(SelectAllContentProperty, value);         }         public bool IsSelectAllActive         {             get => (bool)GetValue(IsSelectAllActiveProperty);             set => SetValue(IsSelectAllActiveProperty, value);         }         public string Delimiter         {             get => (string)GetValue(DelimiterProperty);             set => SetValue(DelimiterProperty, value);         }         public string Text         {             get => (string)GetValue(TextProperty);             set => SetValue(TextProperty, value);         }         private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)         {             var MultiSelectComboBox = (MultiSelectComboBox)d;             if (!(bool)e.NewValue)                 MultiSelectComboBox.Dispatcher.BeginInvoke(new Action(() => { Mouse.Capture(null); }),                     DispatcherPriority.Send);         }         private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)         {         }         protected override bool IsItemItsOwnContainerOverride(object item)         {             return item is MultiSelectComboBoxItem;         }         protected override DependencyObject GetContainerForItemOverride()         {             return new MultiSelectComboBoxItem();         }         protected override void OnSelectionChanged(SelectionChangedEventArgs e)         {             UpdateText();             base.OnSelectionChanged(e);         }         public override void OnApplyTemplate()         {             base.OnApplyTemplate();             _popup = GetTemplateChild(PART_Popup) as Popup;             _multiSelectComboBoxItem = GetTemplateChild(PART_CheckBoxAll) as MultiSelectComboBoxItem;             _multiSelectComboBoxItem.Selected += _MultiSelectComboBoxItem_Selected;             _multiSelectComboBoxItem.Unselected += _MultiSelectComboBoxItem_Unselected;         }         private void _MultiSelectComboBoxItem_Unselected(object sender, RoutedEventArgs e)         {             if (_ignoreTextValueChanged) return;             _ignoreTextValueChanged = true;             UnselectAll();             _ignoreTextValueChanged = false;             UpdateText();         }         private void _MultiSelectComboBoxItem_Selected(object sender, RoutedEventArgs e)         {             if (_ignoreTextValueChanged) return;             _ignoreTextValueChanged = true;             SelectAll();             _ignoreTextValueChanged = false;             UpdateText();         }         protected virtual void UpdateText()         {             if (_ignoreTextValueChanged) return;             var newValue = string.Join(Delimiter, SelectedItems.Cast<object>().Select(x => GetItemDisplayValue(x)));             if (string.IsNullOrWhiteSpace(Text) || !Text.Equals(newValue))             {                 _ignoreTextValueChanged = true;                 if (_multiSelectComboBoxItem != null)                     _multiSelectComboBoxItem.SetCurrentValue(IsSelectedProperty, SelectedItems.Count == Items.Count);                 SetCurrentValue(TextProperty, newValue);                 _ignoreTextValueChanged = false;             }         }         protected object GetItemDisplayValue(object item)         {             if (string.IsNullOrWhiteSpace(DisplayMemberPath))             {                 var property = item.GetType().GetProperty("Content");                 if (property != null)                     return property.GetValue(item, null);             }             var nameParts = DisplayMemberPath.Split('.');             if (nameParts.Length == 1)             {                 var property = item.GetType().GetProperty(DisplayMemberPath);                 if (property != null)                     return property.GetValue(item, null);             }             return item;         }     } }

2) MultiSelectComboBoxItem.cs 代码如下:

using System.Windows.Controls; namespace WPFDevelopers.Controls {     public class MultiSelectComboBoxItem : ListBoxItem     {     } }

3) MultiSelectComboBox.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.MergedDictionaries>   <BooleanToVisibilityConverter x:Key="bool2VisibilityConverter" />  <Style x:Key="DefaultMultiSelectComboBoxItem" TargetType="{x:Type controls:MultiSelectComboBoxItem}">   <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>   <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>   <Setter Property="SnapsToDevicePixels" Value="True"/>   <Setter Property="Background" Value="Transparent"/>   <Setter Property="BorderBrush" Value="Transparent"/>   <Setter Property="Foreground" Value="{DynamicResource RegularTextSolidColorBrush}"/>   <Setter Property="BorderThickness" Value="0"/>   <Setter Property="Height" Value="34" />   <Setter Property="Margin" Value="1,0" />   <Setter Property="Padding" Value="6,0"/>   <Setter Property="Cursor" Value="Hand" />   <Setter Property="Template">    <Setter.Value>     <ControlTemplate TargetType="{x:Type controls:MultiSelectComboBoxItem}">      <Border x:Name="PART_Border"        BorderBrush="{TemplateBinding BorderBrush}"        BorderThickness="{TemplateBinding BorderThickness}"        Background="{TemplateBinding Background}"        SnapsToDevicePixels="true"        Padding="{TemplateBinding Padding}">       <CheckBox Foreground="{TemplateBinding Foreground}"           HorizontalAlignment="Stretch"           VerticalAlignment="{TemplateBinding VerticalContentAlignment}"           MinHeight="{TemplateBinding MinHeight}"           Padding="{TemplateBinding Padding}"           IsChecked="{Binding IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}">        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"           VerticalAlignment="{TemplateBinding VerticalContentAlignment}"           x:Name="PART_ContentPresenter"           SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"           TextElement.Foreground="{TemplateBinding Foreground}"/>       </CheckBox>      </Border>      <ControlTemplate.Triggers>       <Trigger Property="IsMouseOver" Value="True">        <Setter Property="Background" Value="{DynamicResource DefaultBackgroundSolidColorBrush}"/>       </Trigger>      </ControlTemplate.Triggers>     </ControlTemplate>    </Setter.Value>   </Setter>  </Style>  <Style TargetType="{x:Type controls:MultiSelectComboBox}">   <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />   <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />   <Setter Property="ScrollViewer.CanContentScroll" Value="True" />   <Setter Property="SelectionMode" Value="Multiple"/>   <Setter Property="MinWidth" Value="120" />   <Setter Property="MinHeight" Value="{StaticResource MinHeight}" />         <Setter Property="Height" Value="{StaticResource MinHeight}" />   <Setter Property="ItemContainerStyle" Value="{StaticResource DefaultMultiSelectComboBoxItem}"/>   <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:MultiSelectComboBox}">      <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"          Text="{Binding Text,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"          Background="{TemplateBinding Background}"          SelectionBrush="{DynamicResource WindowBorderBrushSolidColorBrush}"          IsReadOnly="True" Style="{x:Null}" />       <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/>          </Grid.RowDefinitions>          <controls:MultiSelectComboBoxItem x:Name="PART_CheckBoxAll"                     Visibility="{TemplateBinding IsSelectAllActive,Converter={StaticResource bool2VisibilityConverter}}"                     Style="{TemplateBinding ItemContainerStyle}"                     Content="{TemplateBinding SelectAllContent}"/>          <ScrollViewer x:Name="DropDownScrollViewer" Grid.Row="1"                ScrollViewer.VerticalScrollBarVisibility="Auto">           <ItemsPresenter x:Name="ItemsPresenter"                KeyboardNavigation.DirectionalNavigation="Contained"                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>          </ScrollViewer>         </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>      </ControlTemplate.Triggers>     </ControlTemplate>    </Setter.Value>   </Setter>  </Style> </ResourceDictionary>

4) MultiSelectComboBoxExample.xaml 代码如下:

<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.MultiSelectComboBoxExample"              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"               xmlns:d="http://schemas.microsoft.com/expression/blend/2008"               xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"              xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"              xmlns:controls="clr-namespace:WPFDevelopers.Samples.Controls"              xmlns:model="clr-namespace:WPFDevelopers.Sample.Models"              mc:Ignorable="d"               d:DesignHeight="450" d:DesignWidth="800">     <UserControl.Resources>         <model:HospitalList x:Key="myHospitalList"/>     </UserControl.Resources>     <controls:CodeViewer>         <UniformGrid Columns="2">             <wpfdev:MultiSelectComboBox                 VerticalContentAlignment="Center"                  HorizontalAlignment="Center"                 Delimiter="^" Width="200">                 <wpfdev:MultiSelectComboBoxItem>Option 1</wpfdev:MultiSelectComboBoxItem>                 <wpfdev:MultiSelectComboBoxItem>Option 2</wpfdev:MultiSelectComboBoxItem>                 <wpfdev:MultiSelectComboBoxItem>Option 3</wpfdev:MultiSelectComboBoxItem>                 <wpfdev:MultiSelectComboBoxItem>Option 4</wpfdev:MultiSelectComboBoxItem>                 <wpfdev:MultiSelectComboBoxItem>Option 5</wpfdev:MultiSelectComboBoxItem>             </wpfdev:MultiSelectComboBox>             <wpfdev:MultiSelectComboBox VerticalContentAlignment="Center"                                           HorizontalAlignment="Center"                                          IsSelectAllActive="True"                              ItemsSource="{Binding Source={StaticResource myHospitalList}}"                              DisplayMemberPath="DoctorName"                              SelectedValuePath="ID" Width="200">             </wpfdev:MultiSelectComboBox>         </UniformGrid>         <controls:CodeViewer.SourceCodes>             <controls:SourceCodeModel                  CodeSource="/WPFDevelopers.SamplesCode;component/ExampleViews/MultiSelectComboBoxExample.xaml"                  CodeType="Xaml"/>             <controls:SourceCodeModel                  CodeSource="/WPFDevelopers.SamplesCode;component/ExampleViews/MultiSelectComboBoxExample.xaml.cs"                  CodeType="CSharp"/>         </controls:CodeViewer.SourceCodes>     </controls:CodeViewer> </UserControl>

效果图

以上就是基于WPF实现多选下拉控件的示例代码的详细内容,更多关于WPF多选下拉控件的资料请关注软件开发网其它相关文章!



示例 wpf

需要 登录 后方可回复, 如果你还没有账号请 注册新账号