using System; using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Threading; using HandyControl.Data; using HandyControl.Interactivity; namespace HandyControl.Controls; [TemplatePart(Name = ElementPanel, Type = typeof(Panel))] [TemplatePart(Name = ElementSelectAll, Type = typeof(CheckComboBoxItem))] public class CheckComboBox : ListBox { private const string ElementPanel = "PART_Panel"; private const string ElementSelectAll = "PART_SelectAll"; private Panel _panel; private CheckComboBoxItem _selectAllItem; private bool _isInternalAction; public static readonly DependencyProperty MaxDropDownHeightProperty = System.Windows.Controls.ComboBox.MaxDropDownHeightProperty.AddOwner(typeof(CheckComboBox), new FrameworkPropertyMetadata(SystemParameters.PrimaryScreenHeight / 3)); [Bindable(true), Category("Layout")] [TypeConverter(typeof(LengthConverter))] public double MaxDropDownHeight { get => (double) GetValue(MaxDropDownHeightProperty); set => SetValue(MaxDropDownHeightProperty, value); } public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register( nameof(IsDropDownOpen), typeof(bool), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.FalseBox, OnIsDropDownOpenChanged)); private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctl = (CheckComboBox) d; if (!(bool) e.NewValue) { ctl.Dispatcher.BeginInvoke(new Action(() => { Mouse.Capture(null); }), DispatcherPriority.Send); } } public bool IsDropDownOpen { get => (bool) GetValue(IsDropDownOpenProperty); set => SetValue(IsDropDownOpenProperty, ValueBoxes.BooleanBox(value)); } public static readonly DependencyProperty TagStyleProperty = DependencyProperty.Register( nameof(TagStyle), typeof(Style), typeof(CheckComboBox), new PropertyMetadata(default(Style))); public Style TagStyle { get => (Style) GetValue(TagStyleProperty); set => SetValue(TagStyleProperty, value); } public static readonly DependencyProperty TagSpacingProperty = DependencyProperty.Register( nameof(TagSpacing), typeof(double), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.Double0Box)); public double TagSpacing { get => (double) GetValue(TagSpacingProperty); set => SetValue(TagSpacingProperty, value); } public static readonly DependencyProperty ShowSelectAllButtonProperty = DependencyProperty.Register( nameof(ShowSelectAllButton), typeof(bool), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.FalseBox)); public bool ShowSelectAllButton { get => (bool) GetValue(ShowSelectAllButtonProperty); set => SetValue(ShowSelectAllButtonProperty, ValueBoxes.BooleanBox(value)); } public CheckComboBox() { AddHandler(Controls.Tag.ClosedEvent, new RoutedEventHandler(Tags_OnClosed)); CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) => { SetCurrentValue(SelectedValueProperty, null); SetCurrentValue(SelectedItemProperty, null); SetCurrentValue(SelectedIndexProperty, -1); SelectedItems.Clear(); })); } public override void OnApplyTemplate() { if (_selectAllItem != null) { _selectAllItem.Selected -= SelectAllItem_Selected; _selectAllItem.Unselected -= SelectAllItem_Unselected; } base.OnApplyTemplate(); _panel = GetTemplateChild(ElementPanel) as Panel; _selectAllItem = GetTemplateChild(ElementSelectAll) as CheckComboBoxItem; if (_selectAllItem != null) { _selectAllItem.Selected += SelectAllItem_Selected; _selectAllItem.Unselected += SelectAllItem_Unselected; } UpdateTags(); } protected override void OnSelectionChanged(SelectionChangedEventArgs e) { UpdateTags(); base.OnSelectionChanged(e); } protected override bool IsItemItsOwnContainerOverride(object item) => item is CheckComboBoxItem; protected override DependencyObject GetContainerForItemOverride() => new CheckComboBoxItem(); protected override void OnDisplayMemberPathChanged(string oldDisplayMemberPath, string newDisplayMemberPath) => UpdateTags(); private void Tags_OnClosed(object sender, RoutedEventArgs e) { if (e.OriginalSource is Tag tag) { SelectedItems.Remove(tag.Tag); _panel.Children.Remove(tag); } } private void SwitchAllItems(bool selected) { if (_isInternalAction) return; _isInternalAction = true; if (!selected) { UnselectAll(); } else { SelectAll(); } _isInternalAction = false; UpdateTags(); } private void SelectAllItem_Selected(object sender, RoutedEventArgs e) => SwitchAllItems(true); private void SelectAllItem_Unselected(object sender, RoutedEventArgs e) => SwitchAllItems(false); private void UpdateTags() { if (_panel == null || _isInternalAction) return; if (_selectAllItem != null) { _isInternalAction = true; _selectAllItem.SetCurrentValue(IsSelectedProperty, Items.Count > 0 && SelectedItems.Count == Items.Count); _isInternalAction = false; } _panel.Children.Clear(); foreach (var item in SelectedItems) { var tag = new Tag { Style = TagStyle, Tag = item }; if (ItemsSource != null) { tag.SetBinding(ContentControl.ContentProperty, new Binding(DisplayMemberPath) { Source = item }); } else { tag.Content = IsItemItsOwnContainerOverride(item) ? ((CheckComboBoxItem) item).Content : item; } _panel.Children.Add(tag); } } }