using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Reflection; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; namespace HMI.UI { public class MultiComboBoxItem : ComboBoxItem, INotifyPropertyChanged { #region ... public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #endregion private MultiComboBox parent; public MultiComboBoxItem(MultiComboBox parent) { this.parent = parent; this.DataContextChanged += MultiComboBoxItem_DataContextChanged; } protected override void OnMouseMove(MouseEventArgs e) { this.PreviewMouseUp += MultiComboBoxItem_PreviewMouseUp; base.OnMouseMove(e); } protected override void OnMouseLeave(MouseEventArgs e) { this.PreviewMouseUp -= MultiComboBoxItem_PreviewMouseUp; base.OnMouseLeave(e); } protected override void OnLostFocus(RoutedEventArgs e) { this.PreviewMouseUp -= MultiComboBoxItem_PreviewMouseUp; base.OnLostFocus(e); } private void MultiComboBoxItem_PreviewMouseUp(object sender, MouseButtonEventArgs e) { if (DataContext is MultiComboBoxData data) { data.IsChecked = !data.IsChecked; e.Handled = true; } } private void MultiComboBoxItem_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) { if (e.OldValue != null && e.OldValue is MultiComboBoxData oldData) { oldData.IsCheckedChanged -= DataIsExpandedChange; } if (DataContext != null && DataContext is MultiComboBoxData newData) { newData.IsCheckedChanged += DataIsExpandedChange; } } private void DataIsExpandedChange(object sender, bool IsExpanded) { parent.Item_IsCheckedChanged((MultiComboBoxData)sender, this, IsExpanded); } } public class MultiComboBoxDataConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is IEnumerable items) { return items.Select(item => new MultiComboBoxData(item, "DisplayName", "DataValue")).ToList(); } return null; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return value; } } public class MultiComboBoxData : INotifyPropertyChanged { #region ... public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #endregion public event EventHandler IsCheckedChanged; private bool isChecked; public bool IsChecked { get { return isChecked; } set { isChecked = value; RaisePropertyChanged("IsChecked"); IsCheckedChanged?.Invoke(this, value); } } public string DisplayName { get => showName?.GetValue(Data).ToString(); } public string DataValue { get => valueName?.GetValue(Data).ToString(); } private PropertyInfo showName; private PropertyInfo valueName; public MultiComboBoxData(object data, string showName, string valueName) { this.Data = data; this.showName = data.GetType().GetProperty(showName); this.valueName = data.GetType().GetProperty(valueName); } public object Data { get; private set; } } /// /// MultiCombox.xaml 的交互逻辑 /// public partial class MultiComboBox : ComboBox { protected override DependencyObject GetContainerForItemOverride() { var item = new MultiComboBoxItem(this); return item; } [Bindable(true)] [Category("Appearance")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [Localizability(LocalizationCategory.NeverLocalize)] public object SelectedValues { get { return GetValue(SelectedValuesProperty); } set { SetValue(SelectedValuesProperty, value); } } public static readonly DependencyProperty SelectedValuesProperty = DependencyProperty.RegisterAttached("SelectedValues", typeof(object), typeof(MultiComboBox), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public void Item_IsCheckedChanged(MultiComboBoxData data, MultiComboBoxItem item, bool isChecked) { var selectedValues = SelectedValues?.ToString() ?? ""; var showNames = this.Text?.ToString() ?? ""; List values = string.IsNullOrEmpty(selectedValues) ? new List() : selectedValues.Split(',').ToList(); List names = string.IsNullOrEmpty(showNames) ? new List() : showNames.Split(',').ToList(); if (isChecked) { values.Add(data.DataValue); names.Add(data.DisplayName); } else { values.Remove(data.DataValue); names.Remove(data.DisplayName); } Text = string.Join(",", names); SelectedValues = string.Join(",", values); } } }