

























public class MultiSelectComboBox : ComboBox { public IList SelectedItems { get { return (IList)GetValue(SelectedItemsProperty); } set { SetValue(SelectedItemsProperty, value); } } // Using a DependencyProperty as the backing store for SelectedItems. This enables animation, styling, binding, etc... public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register( nameof(SelectedItems), typeof(IList), typeof(MultiSelectComboBox), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemsChanged)); private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if(d is MultiSelectComboBox control) { control.UpdateDisplayText(); } } public string DisplayText { get { return (string)GetValue(DisplayTextProperty); } set { SetValue(DisplayTextProperty, value); } } // Using a DependencyProperty as the backing store for DisplayText. This enables animation, styling, binding, etc... public static readonly DependencyProperty DisplayTextProperty = DependencyProperty.Register( nameof(DisplayText), typeof(string), typeof(MultiSelectComboBox), new PropertyMetadata(string.Empty)); public bool KeepDropDownOpen { get { return (bool)GetValue(KeepDropDownOpenProperty); } set { SetValue(KeepDropDownOpenProperty, value); } } // Using a DependencyProperty as the backing store for KeepDropDownOpen. This enables animation, styling, binding, etc... public static readonly DependencyProperty KeepDropDownOpenProperty = DependencyProperty.Register( nameof(KeepDropDownOpen), typeof(bool), typeof(MultiSelectComboBox), new PropertyMetadata(false)); public MultiSelectComboBox() { SelectedItems = new System.Collections.ObjectModel.ObservableCollection<object>(); this.SelectionChanged += OnMultiSelectionChanged; } protected override void OnSelectionChanged(SelectionChangedEventArgs e) { //this.SelectedItem = null; e.Handled = true; } private void OnMultiSelectionChanged(object sender, SelectionChangedEventArgs e) { UpdateDisplayText(); } public void UpdateDisplayText() { if(SelectedItems==null || SelectedItems.Count==0) { DisplayText = "Please select..."; return; } var displayList = new ObservableCollection<string>(); foreach(var item in SelectedItems) { string text = GetItemDisplayText(item); displayList.Add(text); } DisplayText = string.Join(". ", displayList); } private string GetItemDisplayText(object item) { if(item==null) { return string.Empty; } if(!string.IsNullOrWhiteSpace(this.DisplayMemberPath)) { var prop = item.GetType().GetProperty(this.DisplayMemberPath); return prop?.GetValue(item)?.ToString() ?? item.ToString(); } return item.ToString(); } public override void OnApplyTemplate() { base.OnApplyTemplate(); if(GetTemplateChild("PART_Popup") is Popup popup) { popup.Closed += (s, e) => { if (!KeepDropDownOpen) { UpdateDisplayText(); } }; } } } [ValueConversion(typeof(object), typeof(bool))] public class SelectedItemToBoolConverter : MarkupExtension, IMultiValueConverter { private static SelectedItemToBoolConverter _instance; public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Length < 2 || values[0] == null || values[1] == null) { return false; } var item = values[0]; var selectedList = values[1] as IList; if(selectedList==null) { return false; } return selectedList.Contains(item); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { return new object[] { Binding. DoNothing, Binding.DoNothing }; } public override object ProvideValue(IServiceProvider serviceProvider) { return _instance ?? (_instance = new SelectedItemToBoolConverter()); } }
<Window x:Class="WpfApp33.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp33" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.Resources> <Style TargetType="local:MultiSelectComboBox" BasedOn="{StaticResource {x:Type ComboBox}}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:MultiSelectComboBox"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="True"> <Grid> <ToggleButton x:Name="ToggleButton" ClickMode="Press" Focusable="False" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Content="{TemplateBinding DisplayText}" HorizontalContentAlignment="Left" Padding="3,0,0,0"/> <Popup x:Name="PART_Popup" AllowsTransparency="True" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Placement="Bottom" PopupAnimation="Slide"> <Border x:Name="DropDownBorder" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" BorderThickness="1" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{TemplateBinding ActualWidth}"> <ScrollViewer CanContentScroll="True"> <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> </ScrollViewer> </Border> </Popup> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate> <CheckBox Content="{Binding}" Margin="2" Click="CheckBoxItem_Click"> <CheckBox.IsChecked> <MultiBinding Converter="{local:SelectedItemToBoolConverter}" Mode="TwoWay"> <Binding Path="."/> <Binding Path="SelectedItems" RelativeSource="{RelativeSource AncestorType=local:MultiSelectComboBox}"/> </MultiBinding> </CheckBox.IsChecked> </CheckBox> </DataTemplate> </Setter.Value> </Setter> </Style> </Window.Resources> <Window.DataContext> <local:MainVM/> </Window.DataContext> <Grid Margin="20"> <StackPanel VerticalAlignment="Center"> <local:MultiSelectComboBox x:Name="multiCombo" ItemsSource="{Binding BooksCollection}" Height="130" Width="1200" DisplayText="请选择..."/> </StackPanel> </Grid> </Window> using System.Collections; using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; using System.Runtime.CompilerServices; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApp33 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } /// <summary> /// /// </summary> private void CheckBoxItem_Click(object sender, RoutedEventArgs e) { if (sender is CheckBox cb && cb.DataContext != null) { var item = cb.DataContext; var selectedList = multiCombo.SelectedItems; if (cb.IsChecked == true) { if (!selectedList.Contains(item)) selectedList.Add(item); } else { if (selectedList.Contains(item)) selectedList.Remove(item); } multiCombo.UpdateDisplayText(); } } } public class MainVM : INotifyPropertyChanged { public MainVM() { BooksCollection = new ObservableCollection<Book>(); for(int i=1;i<11;i++) { BooksCollection.Add(new Book() { Id = i, Name = $"Name_{i}", ISBN = $"ISBN_{i}", Comment = $"Comment_{i}", Content = $"Content_{i}" }); } } private ObservableCollection<Book> booksCollection; public ObservableCollection<Book> BooksCollection { get { return booksCollection; } set { if(value!=booksCollection) { booksCollection = value; OnPropertyChanged(); } } } public event PropertyChangedEventHandler? PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propName="") { var handler = PropertyChanged; handler?.Invoke(this, new PropertyChangedEventArgs(propName)); } } public class Book { public int Id { get; set; } public string Name { get; set; } public string ISBN { get; set; } public string Comment { get; set; } public string Content { get; set; } public override string ToString() { return $"Id:{Id},Name:{Name},ISBN:{ISBN}"; } } public class MultiSelectComboBox : ComboBox { public IList SelectedItems { get { return (IList)GetValue(SelectedItemsProperty); } set { SetValue(SelectedItemsProperty, value); } } // Using a DependencyProperty as the backing store for SelectedItems. This enables animation, styling, binding, etc... public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register( nameof(SelectedItems), typeof(IList), typeof(MultiSelectComboBox), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemsChanged)); private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if(d is MultiSelectComboBox control) { control.UpdateDisplayText(); } } public string DisplayText { get { return (string)GetValue(DisplayTextProperty); } set { SetValue(DisplayTextProperty, value); } } // Using a DependencyProperty as the backing store for DisplayText. This enables animation, styling, binding, etc... public static readonly DependencyProperty DisplayTextProperty = DependencyProperty.Register( nameof(DisplayText), typeof(string), typeof(MultiSelectComboBox), new PropertyMetadata(string.Empty)); public bool KeepDropDownOpen { get { return (bool)GetValue(KeepDropDownOpenProperty); } set { SetValue(KeepDropDownOpenProperty, value); } } // Using a DependencyProperty as the backing store for KeepDropDownOpen. This enables animation, styling, binding, etc... public static readonly DependencyProperty KeepDropDownOpenProperty = DependencyProperty.Register( nameof(KeepDropDownOpen), typeof(bool), typeof(MultiSelectComboBox), new PropertyMetadata(false)); public MultiSelectComboBox() { SelectedItems = new System.Collections.ObjectModel.ObservableCollection<object>(); this.SelectionChanged += OnMultiSelectionChanged; } protected override void OnSelectionChanged(SelectionChangedEventArgs e) { //this.SelectedItem = null; e.Handled = true; } private void OnMultiSelectionChanged(object sender, SelectionChangedEventArgs e) { UpdateDisplayText(); } public void UpdateDisplayText() { if(SelectedItems==null || SelectedItems.Count==0) { DisplayText = "Please select..."; return; } var displayList = new ObservableCollection<string>(); foreach(var item in SelectedItems) { string text = GetItemDisplayText(item); displayList.Add(text); } DisplayText = string.Join(". ", displayList); } private string GetItemDisplayText(object item) { if(item==null) { return string.Empty; } if(!string.IsNullOrWhiteSpace(this.DisplayMemberPath)) { var prop = item.GetType().GetProperty(this.DisplayMemberPath); return prop?.GetValue(item)?.ToString() ?? item.ToString(); } return item.ToString(); } public override void OnApplyTemplate() { base.OnApplyTemplate(); if(GetTemplateChild("PART_Popup") is Popup popup) { popup.Closed += (s, e) => { if (!KeepDropDownOpen) { UpdateDisplayText(); } }; } } } [ValueConversion(typeof(object), typeof(bool))] public class SelectedItemToBoolConverter : MarkupExtension, IMultiValueConverter { private static SelectedItemToBoolConverter _instance; public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Length < 2 || values[0] == null || values[1] == null) { return false; } var item = values[0]; var selectedList = values[1] as IList; if(selectedList==null) { return false; } return selectedList.Contains(item); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { return new object[] { Binding. DoNothing, Binding.DoNothing }; } public override object ProvideValue(IServiceProvider serviceProvider) { return _instance ?? (_instance = new SelectedItemToBoolConverter()); } } }


此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。