using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; #if NET40 using System.Windows.Threading; #endif using HandyControl.Data; using HandyControl.Interactivity; namespace HandyControl.Controls; [TemplatePart(Name = AutoCompletePanel, Type = typeof(Panel))] [TemplatePart(Name = EditableTextBox, Type = typeof(System.Windows.Controls.TextBox))] [TemplatePart(Name = AutoPopupAutoComplete, Type = typeof(Popup))] public class ComboBox : System.Windows.Controls.ComboBox { #if NET40 private string _searchText; private DispatcherTimer _autoCompleteTimer; #endif private bool _isAutoCompleteAction = true; private Panel _autoCompletePanel; private System.Windows.Controls.TextBox _editableTextBox; private Popup _autoPopupAutoComplete; private const string AutoCompletePanel = "PART_AutoCompletePanel"; private const string AutoPopupAutoComplete = "PART_Popup_AutoComplete"; private const string EditableTextBox = "PART_EditableTextBox"; public ComboBox() { CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) => { if (IsReadOnly) { return; } SetCurrentValue(SelectedValueProperty, null); SetCurrentValue(SelectedItemProperty, null); SetCurrentValue(SelectedIndexProperty, -1); SetCurrentValue(TextProperty, ""); })); } public override void OnApplyTemplate() { if (_editableTextBox != null) { BindingOperations.ClearBinding(_editableTextBox, System.Windows.Controls.TextBox.TextProperty); _editableTextBox.GotFocus -= EditableTextBox_GotFocus; _editableTextBox.LostFocus -= EditableTextBox_LostFocus; } base.OnApplyTemplate(); if (IsEditable) { _editableTextBox = GetTemplateChild(EditableTextBox) as System.Windows.Controls.TextBox; if (_editableTextBox != null) { _editableTextBox.SetBinding(SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this }); #if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472) _editableTextBox.SetBinding(SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this }); #endif _editableTextBox.SetBinding(SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this }); _editableTextBox.SetBinding(CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this }); if (AutoComplete) { #if NET40 _autoCompleteTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(500) }; _autoCompleteTimer.Tick += AutoCompleteTimer_Tick; #endif _autoPopupAutoComplete = GetTemplateChild(AutoPopupAutoComplete) as Popup; _autoCompletePanel = GetTemplateChild(AutoCompletePanel) as Panel; _editableTextBox.SetBinding(System.Windows.Controls.TextBox.TextProperty, new Binding(SearchTextProperty.Name) { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, Mode = BindingMode.OneWayToSource, #if !NET40 Delay = 500, #endif Source = this }); _editableTextBox.GotFocus += EditableTextBox_GotFocus; _editableTextBox.LostFocus += EditableTextBox_LostFocus; } } } } #if NET40 private void AutoCompleteTimer_Tick(object sender, EventArgs e) { UpdateSearchItems(_searchText); _autoCompleteTimer.Stop(); } #endif private void EditableTextBox_LostFocus(object sender, RoutedEventArgs e) { if (_autoPopupAutoComplete != null) { _autoPopupAutoComplete.IsOpen = false; } } #if !NET40 protected override void OnDropDownClosed(EventArgs e) { base.OnDropDownClosed(e); _isAutoCompleteAction = false; } #endif private void EditableTextBox_GotFocus(object sender, RoutedEventArgs e) { if (_autoPopupAutoComplete != null && _editableTextBox != null && !string.IsNullOrEmpty(_editableTextBox.Text)) { _autoPopupAutoComplete.IsOpen = true; } } protected override void OnSelectionChanged(SelectionChangedEventArgs e) { _isAutoCompleteAction = false; base.OnSelectionChanged(e); #if NET40 _isAutoCompleteAction = true; #endif } /// /// 数据搜索委托 /// public Func> SearchFunc { get; set; } /// /// 是否自动完成输入 /// public static readonly DependencyProperty AutoCompleteProperty = DependencyProperty.Register( nameof(AutoComplete), typeof(bool), typeof(ComboBox), new PropertyMetadata(ValueBoxes.FalseBox, OnAutoCompleteChanged)); private static void OnAutoCompleteChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctl = (ComboBox) d; if (ctl._editableTextBox != null) { ctl.UpdateSearchItems(ctl._editableTextBox.Text); } } /// /// 是否自动完成输入 /// public bool AutoComplete { get => (bool) GetValue(AutoCompleteProperty); set => SetValue(AutoCompleteProperty, ValueBoxes.BooleanBox(value)); } /// /// 搜索文本 /// internal static readonly DependencyProperty SearchTextProperty = DependencyProperty.Register( nameof(SearchText), typeof(string), typeof(ComboBox), new PropertyMetadata(default(string), OnSearchTextChanged)); private static void OnSearchTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctl = (ComboBox) d; if (ctl._isAutoCompleteAction) { #if NET40 ctl._searchText = e.NewValue as string; ctl._autoCompleteTimer.Stop(); ctl._autoCompleteTimer.Start(); #else ctl.UpdateSearchItems(e.NewValue as string); #endif } ctl._isAutoCompleteAction = true; } /// /// 搜索文本 /// internal string SearchText { get => (string) GetValue(SearchTextProperty); set => SetValue(SearchTextProperty, value); } public static readonly DependencyProperty SelectionBrushProperty = TextBoxBase.SelectionBrushProperty.AddOwner(typeof(ComboBox)); public Brush SelectionBrush { get => (Brush) GetValue(SelectionBrushProperty); set => SetValue(SelectionBrushProperty, value); } #if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472) public static readonly DependencyProperty SelectionTextBrushProperty = TextBoxBase.SelectionTextBrushProperty.AddOwner(typeof(ComboBox)); public Brush SelectionTextBrush { get => (Brush) GetValue(SelectionTextBrushProperty); set => SetValue(SelectionTextBrushProperty, value); } #endif public static readonly DependencyProperty SelectionOpacityProperty = TextBoxBase.SelectionOpacityProperty.AddOwner(typeof(ComboBox)); public double SelectionOpacity { get => (double) GetValue(SelectionOpacityProperty); set => SetValue(SelectionOpacityProperty, value); } public static readonly DependencyProperty CaretBrushProperty = TextBoxBase.CaretBrushProperty.AddOwner(typeof(ComboBox)); public Brush CaretBrush { get => (Brush) GetValue(CaretBrushProperty); set => SetValue(CaretBrushProperty, value); } /// /// 更新搜索的项目 /// /// private void UpdateSearchItems(string key) { if (_editableTextBox != null && _autoPopupAutoComplete != null) { _autoPopupAutoComplete.IsOpen = !string.IsNullOrEmpty(key); _autoCompletePanel.Children.Clear(); if (SearchFunc == null) { if (!string.IsNullOrEmpty(key)) { foreach (var item in Items) { var content = item?.ToString(); if (content == null) continue; if (!content.Contains(key)) continue; _autoCompletePanel.Children.Add(CreateSearchItem(item)); } } } else { foreach (var item in SearchFunc.Invoke(Items, key)) { _autoCompletePanel.Children.Add(CreateSearchItem(item)); } } } } private ComboBoxItem CreateSearchItem(object content) { var item = new ComboBoxItem { Content = content, Style = ItemContainerStyle, ContentTemplate = ItemTemplate }; item.PreviewMouseLeftButtonDown += AutoCompleteItem_PreviewMouseLeftButtonDown; return item; } private void AutoCompleteItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (sender is ComboBoxItem comboBoxItem) { if (_autoPopupAutoComplete != null) { _autoPopupAutoComplete.IsOpen = false; } _isAutoCompleteAction = false; SelectedValue = comboBoxItem.Content; } } }