using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; namespace FWindSoft.Wpf.Controls { /// /// 带搜索功能的comboBox /// /// [TemplatePart(Name = "PART_textBoxNewItem", Type=typeof(TextBox))] [TemplatePart(Name = "PART_SearchText", Type = typeof(TextBox))] [TemplatePart(Name = "PART_Popup", Type = typeof(Popup))] [TemplatePart(Name = "PART_Button", Type = typeof(Button))] public class SearchComboBox : ListBox { static SearchComboBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(SearchComboBox), new FrameworkPropertyMetadata(typeof(SearchComboBox))); } public SearchComboBox() { Keyboard.AddPreviewKeyDownHandler(this, OnKeyDown); Keyboard.AddPreviewKeyUpHandler(this, OnKeyUp); this.AddHandler(ListBoxItem.PreviewMouseDownEvent,new MouseButtonEventHandler(MouseButtonEventHandler)); this.IsTextSearchCaseSensitive = false; this.IsTextSearchEnabled = false; this.InEdit = false; this.SetValue(KeyboardNavigation.DirectionalNavigationProperty, KeyboardNavigationMode.Contained); } private void MouseButtonEventHandler(object sender, MouseButtonEventArgs e) { var element = e.OriginalSource as UIElement; if (element == null) return; var listItem = element.GetParentTypeSelf(); if (listItem == null) return; if (listItem.DataContext == this.SelectedItem) { SyncSelectedItemText(); } } private void OnKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter && IsDropDownOpen) { this.IsDropDownOpen = false; if (this.SelectedItem != null) { SyncSelectedItemText(); } return; } if (!(Key.Up == e.Key || Key.Down == e.Key)) { //EditableTextBox.Focus(); } else { UIElement item = ItemContainerGenerator.ContainerFromItem(SelectedItem) as UIElement; if ((item == null) && (Items.Count > 0)) item = ItemContainerGenerator.ContainerFromItem(Items[0]) as UIElement; if (item != null) item.Focus(); } } private void OnKeyUp(object sender, KeyEventArgs e) { EditableTextBox.Focus(); } #region 属性相关 /// /// 关联文本 /// public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(SearchComboBox)); /// /// 关联文本 /// public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } } /// /// 是否下拉打开 /// public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(SearchComboBox)); /// /// 是否下拉打开 /// public bool IsDropDownOpen { get { return (bool)GetValue(IsDropDownOpenProperty); } set { SetValue(IsDropDownOpenProperty, value); } } /// /// Dependency backing for the MaxDropDownHeight property. /// public static readonly DependencyProperty MaxDropDownHeightProperty = ComboBox.MaxDropDownHeightProperty.AddOwner(typeof(SearchComboBox)); /// /// Gets or sets a value indicating the maximium height of the drop down. /// public double MaxDropDownHeight { get { return (double)GetValue(MaxDropDownHeightProperty); } set { SetValue(MaxDropDownHeightProperty, value); } } /// /// 是否下拉打开 /// public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(SearchComboBox)); /// /// 是否下拉打开 /// public bool IsReadOnly { get { return (bool)GetValue(IsReadOnlyProperty); } set { SetValue(IsReadOnlyProperty, value); } } #endregion /* * 选中项会在数据源发生变化时变化,所以要保证全局的选中项 */ /// /// 真实的选中项 /// private object RealSelectedItem { get; set; } #region 内部编辑 private bool InEdit { get; set; } public void BeginInEdit() { InEdit = true; } public void EndInEdit() { InEdit = false; } #endregion #region 相关 /* * 1、过滤过程中,对SelectedItem赋值,不要引起Text的变化 * 2、内部引发的Text事件,不触发过滤事件 * 3、编辑完成是离开控件 */ #endregion private static readonly DependencyProperty m_DisplayProperty = DependencyProperty.Register("m_DisplayProperty", typeof(object), typeof(SearchComboBox)); private string GetDisplay(object item) { BindingOperations.SetBinding(this, m_DisplayProperty, new Binding(DisplayMemberPath) { Source = item }); var propertyValue = GetValue(m_DisplayProperty); BindingOperations.ClearBinding(this, m_DisplayProperty); return propertyValue?.ToString() ?? string.Empty; } private Popup Popup { get; set; } protected TextBox EditableTextBox { get; set; } public override void OnApplyTemplate() { base.OnApplyTemplate(); Popup = (Popup)this.Template.FindName("PART_Popup", this); EditableTextBox = (TextBox) this.Template.FindName("PART_SearchText", this); this.EditableTextBox.TextChanged += EditableTextBox_TextChanged; this.EditableTextBox.LostFocus += EditableTextBox_LostFocus; var toggle = (Button)this.Template.FindName("PART_Button", this); toggle.Click += Toggle_Click; this.PreviewMouseLeftButtonDown += Toggle_PreviewMouseLeftButtonDown; } private void Toggle_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { m_IsOpen = this.IsDropDownOpen; } private bool m_IsOpen; private void Toggle_Click(object sender, RoutedEventArgs e) { if (this.ItemsSource == null) return; GlobalFlag = true; RefreshFilter(); SetSelectedItem(); this.IsDropDownOpen = !m_IsOpen; //Popup.StaysOpen = false; } private void EditableTextBox_LostFocus(object sender, RoutedEventArgs e) { GlobalFlag = true; RefreshFilter(); SetSelectedItem(); } private void SetSelectedItem() { if(this.RealSelectedItem==null) { if (this.ItemsSource != null) { foreach (var item in ItemsSource) { this.RealSelectedItem = item;//.FirstOrDefault(); break; } } } this.SelectedItem = this.RealSelectedItem; try { BeginInEdit(); this.Text = GetDisplay(this.SelectedItem); } finally { EndInEdit(); } } private void EditableTextBox_TextChanged(object sender, TextChangedEventArgs e) { TextChanged(); } protected override void OnSelectionChanged(SelectionChangedEventArgs e) { try { BeginInEdit(); base.OnSelectionChanged(e); if (!InSelected) { this.Text = GetDisplay(this.SelectedItem); this.EditableTextBox.Select(this.Text.Length, 0); this.RealSelectedItem = this.SelectedItem; } } finally { EndInEdit(); } } /// /// 同步text显示 /// private void SyncSelectedItemText() { try { BeginInEdit(); if (!InSelected) { var text= GetDisplay(this.SelectedItem); //this.Text = this.SelectedItem?.ToString() ?? string.Empty; this.EditableTextBox.Text = text; this.EditableTextBox.Select(text.Length, 0); this.RealSelectedItem = this.SelectedItem; } } finally { EndInEdit(); } } #region 选项控制 /// /// 是否是内部选择 /// private bool InSelected { get; set; } = false; private void SetSelectedItem(object item) { try { InSelected = true; this.SelectedItem = item; } finally { InSelected = false; } } #endregion #region 过滤相关 private void RefreshFilter() { if (this.ItemsSource != null) { try { BeginInEdit(); InSelected = true; ICollectionView view = CollectionViewSource.GetDefaultView(this.ItemsSource); view.Refresh(); } finally { InSelected = false; EndInEdit(); } } } /// /// 全局过滤标志,为true 则不进行过滤,初始值为ture /// private bool GlobalFlag { get; set; } = true; private bool FilterPredicate(object value) { if (GlobalFlag) return true; if (value == null) return false; var text = this.EditableTextBox.Text; if (text.Length== 0) return true; string prefix = text; return GetDisplay(value).Contains(prefix); } #endregion protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) { if (newValue != null) { ICollectionView view = CollectionViewSource.GetDefaultView(newValue); view.Filter += this.FilterPredicate; } if (oldValue != null) { ICollectionView view = CollectionViewSource.GetDefaultView(oldValue); view.Filter -= this.FilterPredicate; } base.OnItemsSourceChanged(oldValue, newValue); } private void TextChanged() { if (!this.IsTextSearchEnabled && !this.InEdit) { var text = this.EditableTextBox.Text??string.Empty; if (true||text.Length > 0) { GlobalFlag = false; this.RefreshFilter(); //Poupup.IsOpen = true; IsDropDownOpen = true; foreach (object item in CollectionViewSource.GetDefaultView(this.ItemsSource)) { string display = item.ToString(); SetSelectedItem(item); if (display == text) { this.RealSelectedItem = item; } break; } } } } } }