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; #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, IDataInput { #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) => { 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.TextChanged += EditableTextBox_TextChanged; 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_TextChanged(object sender, TextChangedEventArgs e) { VerifyData(); } 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); VerifyData(); #if NET40 _isAutoCompleteAction = true; #endif } /// /// 数据验证委托 /// public Func> VerifyFunc { get; set; } /// /// 数据搜索委托 /// public Func> SearchFunc { get; set; } /// /// 数据是否错误 /// public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register( "IsError", typeof(bool), typeof(ComboBox), new PropertyMetadata(ValueBoxes.FalseBox)); /// /// 数据是否错误 /// public bool IsError { get => (bool)GetValue(IsErrorProperty); set => SetValue(IsErrorProperty, value); } /// /// 错误提示 /// public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register( "ErrorStr", typeof(string), typeof(ComboBox), new PropertyMetadata(default(string))); /// /// 错误提示 /// public string ErrorStr { get => (string)GetValue(ErrorStrProperty); set => SetValue(ErrorStrProperty, value); } /// /// 文本类型 /// public static readonly DependencyPropertyKey TextTypePropertyKey = DependencyProperty.RegisterReadOnly("TextType", typeof(TextType), typeof(ComboBox), new PropertyMetadata(default(TextType))); /// /// 文本类型 /// public static readonly DependencyProperty TextTypeProperty = TextTypePropertyKey.DependencyProperty; /// /// 文本类型 /// public TextType TextType { get => (TextType)GetValue(TextTypeProperty); set => SetValue(TextTypeProperty, value); } /// /// 是否显示清除按钮 /// public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register( "ShowClearButton", typeof(bool), typeof(ComboBox), new PropertyMetadata(ValueBoxes.FalseBox)); /// /// 是否显示清除按钮 /// public bool ShowClearButton { get => (bool)GetValue(ShowClearButtonProperty); set => SetValue(ShowClearButtonProperty, value); } /// /// 是否自动完成输入 /// public static readonly DependencyProperty AutoCompleteProperty = DependencyProperty.Register( "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); } } /// /// 更新搜索的项目 /// /// 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; } } /// /// 搜索文本 /// internal static readonly DependencyProperty SearchTextProperty = DependencyProperty.Register( "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 bool AutoComplete { get => (bool)GetValue(AutoCompleteProperty); set => SetValue(AutoCompleteProperty, value); } /// /// 验证数据 /// /// public virtual bool VerifyData() { OperationResult result; var value = _editableTextBox == null ? Text : _editableTextBox.Text; if (VerifyFunc != null) { result = VerifyFunc.Invoke(value); } else { if (!string.IsNullOrEmpty(value)) { 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; } } }