using System; using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; 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, IDataInput { 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 IsErrorProperty = DependencyProperty.Register( "IsError", typeof(bool), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.FalseBox)); public bool IsError { get => (bool) GetValue(IsErrorProperty); set => SetValue(IsErrorProperty, ValueBoxes.BooleanBox(value)); } public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register( "ErrorStr", typeof(string), typeof(CheckComboBox), new PropertyMetadata(default(string))); public string ErrorStr { get => (string) GetValue(ErrorStrProperty); set => SetValue(ErrorStrProperty, value); } public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register( "TextType", typeof(TextType), typeof(CheckComboBox), new PropertyMetadata(default(TextType))); public TextType TextType { get => (TextType) GetValue(TextTypeProperty); set => SetValue(TextTypeProperty, value); } public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register( "ShowClearButton", typeof(bool), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.FalseBox)); public bool ShowClearButton { get => (bool) GetValue(ShowClearButtonProperty); set => SetValue(ShowClearButtonProperty, ValueBoxes.BooleanBox(value)); } 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( "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( "TagStyle", typeof(Style), typeof(CheckComboBox), new PropertyMetadata(default(Style))); public Style TagStyle { get => (Style) GetValue(TagStyleProperty); set => SetValue(TagStyleProperty, value); } public static readonly DependencyProperty ShowSelectAllButtonProperty = DependencyProperty.Register( "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) => SelectedItems.Clear())); ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; } private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) { if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) { UpdateTags(); } } 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; } Dispatcher.BeginInvoke(new Action(() => { IsDropDownOpen = true; IsDropDownOpen = false; }), DispatcherPriority.DataBind); } public bool VerifyData() { OperationResult result; if (VerifyFunc != null) { result = VerifyFunc.Invoke(null); } else { if (SelectedItems.Count > 0) { result = OperationResult.Success(); } else if (InfoElement.GetNecessary(this)) { result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary); } else { result = OperationResult.Success(); } } IsError = !result.Data; ErrorStr = result.Message; return result.Data; } public Func> VerifyFunc { get; set; } protected override void OnSelectionChanged(SelectionChangedEventArgs e) { UpdateTags(); VerifyData(); 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 && tag.Tag is CheckComboBoxItem checkComboBoxItem) { checkComboBoxItem.SetCurrentValue(IsSelectedProperty, false); } } private void SwitchAllItems(bool selected) { if (_isInternalAction) return; _isInternalAction = true; foreach (var item in Items) { if (ItemContainerGenerator.ContainerFromItem(item) is CheckComboBoxItem checkComboBoxItem) { checkComboBoxItem.SetCurrentValue(ListBoxItem.IsSelectedProperty, selected); } } _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, SelectedItems.Count == Items.Count); _isInternalAction = false; } _panel.Children.Clear(); foreach (var item in SelectedItems) { if (ItemContainerGenerator.ContainerFromItem(item) is CheckComboBoxItem checkComboBoxItem) { var tag = new Tag { Style = TagStyle, Tag = checkComboBoxItem }; if (ItemsSource != null) { tag.SetBinding(ContentControl.ContentProperty, new Binding(DisplayMemberPath) { Source = item }); } else { tag.Content = checkComboBoxItem.Content; } _panel.Children.Add(tag); } } } } }