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;
}
}
}