CheckComboBox.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. using System;
  2. using System.ComponentModel;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Data;
  6. using System.Windows.Input;
  7. using System.Windows.Threading;
  8. using HandyControl.Data;
  9. using HandyControl.Interactivity;
  10. namespace HandyControl.Controls;
  11. [TemplatePart(Name = ElementPanel, Type = typeof(Panel))]
  12. [TemplatePart(Name = ElementSelectAll, Type = typeof(CheckComboBoxItem))]
  13. public class CheckComboBox : ListBox
  14. {
  15. private const string ElementPanel = "PART_Panel";
  16. private const string ElementSelectAll = "PART_SelectAll";
  17. private Panel _panel;
  18. private CheckComboBoxItem _selectAllItem;
  19. private bool _isInternalAction;
  20. public static readonly DependencyProperty MaxDropDownHeightProperty =
  21. System.Windows.Controls.ComboBox.MaxDropDownHeightProperty.AddOwner(typeof(CheckComboBox),
  22. new FrameworkPropertyMetadata(SystemParameters.PrimaryScreenHeight / 3));
  23. [Bindable(true), Category("Layout")]
  24. [TypeConverter(typeof(LengthConverter))]
  25. public double MaxDropDownHeight
  26. {
  27. get => (double) GetValue(MaxDropDownHeightProperty);
  28. set => SetValue(MaxDropDownHeightProperty, value);
  29. }
  30. public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
  31. nameof(IsDropDownOpen), typeof(bool), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.FalseBox, OnIsDropDownOpenChanged));
  32. private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  33. {
  34. var ctl = (CheckComboBox) d;
  35. if (!(bool) e.NewValue)
  36. {
  37. ctl.Dispatcher.BeginInvoke(new Action(() =>
  38. {
  39. Mouse.Capture(null);
  40. }), DispatcherPriority.Send);
  41. }
  42. }
  43. public bool IsDropDownOpen
  44. {
  45. get => (bool) GetValue(IsDropDownOpenProperty);
  46. set => SetValue(IsDropDownOpenProperty, ValueBoxes.BooleanBox(value));
  47. }
  48. public static readonly DependencyProperty TagStyleProperty = DependencyProperty.Register(
  49. nameof(TagStyle), typeof(Style), typeof(CheckComboBox), new PropertyMetadata(default(Style)));
  50. public Style TagStyle
  51. {
  52. get => (Style) GetValue(TagStyleProperty);
  53. set => SetValue(TagStyleProperty, value);
  54. }
  55. public static readonly DependencyProperty TagSpacingProperty = DependencyProperty.Register(
  56. nameof(TagSpacing), typeof(double), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.Double0Box));
  57. public double TagSpacing
  58. {
  59. get => (double) GetValue(TagSpacingProperty);
  60. set => SetValue(TagSpacingProperty, value);
  61. }
  62. public static readonly DependencyProperty ShowSelectAllButtonProperty = DependencyProperty.Register(
  63. nameof(ShowSelectAllButton), typeof(bool), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.FalseBox));
  64. public bool ShowSelectAllButton
  65. {
  66. get => (bool) GetValue(ShowSelectAllButtonProperty);
  67. set => SetValue(ShowSelectAllButtonProperty, ValueBoxes.BooleanBox(value));
  68. }
  69. public CheckComboBox()
  70. {
  71. AddHandler(Controls.Tag.ClosedEvent, new RoutedEventHandler(Tags_OnClosed));
  72. CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
  73. {
  74. SetCurrentValue(SelectedValueProperty, null);
  75. SetCurrentValue(SelectedItemProperty, null);
  76. SetCurrentValue(SelectedIndexProperty, -1);
  77. SelectedItems.Clear();
  78. }));
  79. }
  80. public override void OnApplyTemplate()
  81. {
  82. if (_selectAllItem != null)
  83. {
  84. _selectAllItem.Selected -= SelectAllItem_Selected;
  85. _selectAllItem.Unselected -= SelectAllItem_Unselected;
  86. }
  87. base.OnApplyTemplate();
  88. _panel = GetTemplateChild(ElementPanel) as Panel;
  89. _selectAllItem = GetTemplateChild(ElementSelectAll) as CheckComboBoxItem;
  90. if (_selectAllItem != null)
  91. {
  92. _selectAllItem.Selected += SelectAllItem_Selected;
  93. _selectAllItem.Unselected += SelectAllItem_Unselected;
  94. }
  95. UpdateTags();
  96. }
  97. protected override void OnSelectionChanged(SelectionChangedEventArgs e)
  98. {
  99. UpdateTags();
  100. base.OnSelectionChanged(e);
  101. }
  102. protected override bool IsItemItsOwnContainerOverride(object item) => item is CheckComboBoxItem;
  103. protected override DependencyObject GetContainerForItemOverride() => new CheckComboBoxItem();
  104. protected override void OnDisplayMemberPathChanged(string oldDisplayMemberPath, string newDisplayMemberPath) => UpdateTags();
  105. private void Tags_OnClosed(object sender, RoutedEventArgs e)
  106. {
  107. if (e.OriginalSource is Tag tag)
  108. {
  109. SelectedItems.Remove(tag.Tag);
  110. _panel.Children.Remove(tag);
  111. }
  112. }
  113. private void SwitchAllItems(bool selected)
  114. {
  115. if (_isInternalAction) return;
  116. _isInternalAction = true;
  117. if (!selected)
  118. {
  119. UnselectAll();
  120. }
  121. else
  122. {
  123. SelectAll();
  124. }
  125. _isInternalAction = false;
  126. UpdateTags();
  127. }
  128. private void SelectAllItem_Selected(object sender, RoutedEventArgs e) => SwitchAllItems(true);
  129. private void SelectAllItem_Unselected(object sender, RoutedEventArgs e) => SwitchAllItems(false);
  130. private void UpdateTags()
  131. {
  132. if (_panel == null || _isInternalAction) return;
  133. if (_selectAllItem != null)
  134. {
  135. _isInternalAction = true;
  136. _selectAllItem.SetCurrentValue(IsSelectedProperty, Items.Count > 0 && SelectedItems.Count == Items.Count);
  137. _isInternalAction = false;
  138. }
  139. _panel.Children.Clear();
  140. foreach (var item in SelectedItems)
  141. {
  142. var tag = new Tag
  143. {
  144. Style = TagStyle,
  145. Tag = item
  146. };
  147. if (ItemsSource != null)
  148. {
  149. tag.SetBinding(ContentControl.ContentProperty, new Binding(DisplayMemberPath) { Source = item });
  150. }
  151. else
  152. {
  153. tag.Content = IsItemItsOwnContainerOverride(item) ? ((CheckComboBoxItem) item).Content : item;
  154. }
  155. _panel.Children.Add(tag);
  156. }
  157. }
  158. }