123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- 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
- {
- /// <summary>
- /// 带搜索功能的comboBox
- /// </summary>
- /// [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<ListBoxItem>();
- 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 属性相关
- /// <summary>
- /// 关联文本
- /// </summary>
- public static readonly DependencyProperty TextProperty =
- DependencyProperty.Register("Text", typeof(string), typeof(SearchComboBox));
- /// <summary>
- /// 关联文本
- /// </summary>
- public string Text
- {
- get { return (string)GetValue(TextProperty); }
- set { SetValue(TextProperty, value); }
- }
- /// <summary>
- /// 是否下拉打开
- /// </summary>
- public static readonly DependencyProperty IsDropDownOpenProperty =
- DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(SearchComboBox));
- /// <summary>
- /// 是否下拉打开
- /// </summary>
- public bool IsDropDownOpen
- {
- get { return (bool)GetValue(IsDropDownOpenProperty); }
- set { SetValue(IsDropDownOpenProperty, value); }
- }
- /// <summary>
- /// Dependency backing for the MaxDropDownHeight property.
- /// </summary>
- public static readonly DependencyProperty MaxDropDownHeightProperty =
- ComboBox.MaxDropDownHeightProperty.AddOwner(typeof(SearchComboBox));
- /// <summary>
- /// Gets or sets a value indicating the maximium height of the drop down.
- /// </summary>
- public double MaxDropDownHeight
- {
- get { return (double)GetValue(MaxDropDownHeightProperty); }
- set { SetValue(MaxDropDownHeightProperty, value); }
- }
- /// <summary>
- /// 是否下拉打开
- /// </summary>
- public static readonly DependencyProperty IsReadOnlyProperty =
- DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(SearchComboBox));
- /// <summary>
- /// 是否下拉打开
- /// </summary>
- public bool IsReadOnly
- {
- get { return (bool)GetValue(IsReadOnlyProperty); }
- set { SetValue(IsReadOnlyProperty, value); }
- }
- #endregion
- /*
- * 选中项会在数据源发生变化时变化,所以要保证全局的选中项
- */
- /// <summary>
- /// 真实的选中项
- /// </summary>
- 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();
- }
-
- }
- /// <summary>
- /// 同步text显示
- /// </summary>
- 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 选项控制
- /// <summary>
- /// 是否是内部选择
- /// </summary>
- 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();
- }
- }
- }
- /// <summary>
- /// 全局过滤标志,为true 则不进行过滤,初始值为ture
- /// </summary>
- 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;
- }
- }
- }
- }
-
- }
- }
|