From 832513af6fe66c34a4bcbaed203683cf73972ce6 Mon Sep 17 00:00:00 2001
From: NaBian <836904362@qq.com>
Date: Sat, 11 May 2019 19:14:55 +0800
Subject: [PATCH 1/2] add AutoComplete for ComboBox
---
.../Themes/Styles/Base/ComboBoxBaseStyle.xaml | 295 ++++++++++++++++++
.../Themes/Styles/Base/ComboBoxBaseStyle.xaml | 281 +++++++++++++++++
.../Themes/Styles/Base/ComboBoxBaseStyle.xaml | 295 ++++++++++++++++++
.../Service/DataService.cs | 2 +-
.../UserControl/Controls/ComboBoxDemoCtl.xaml | 4 +-
.../Controls/Input/ComboBox.cs | 284 ++++++++++++++++-
.../Controls/Panel/SimplePanel.cs | 4 +-
.../Controls/TabControl/TabControl.cs | 4 +-
8 files changed, 1152 insertions(+), 17 deletions(-)
diff --git a/src/Core_30/HandyControl_Core_30/Themes/Styles/Base/ComboBoxBaseStyle.xaml b/src/Core_30/HandyControl_Core_30/Themes/Styles/Base/ComboBoxBaseStyle.xaml
index 90d37234..29c6a7cd 100644
--- a/src/Core_30/HandyControl_Core_30/Themes/Styles/Base/ComboBoxBaseStyle.xaml
+++ b/src/Core_30/HandyControl_Core_30/Themes/Styles/Base/ComboBoxBaseStyle.xaml
@@ -887,6 +887,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1009,6 +1150,144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Net_40/HandyControl_Net_40/Themes/Styles/Base/ComboBoxBaseStyle.xaml b/src/Net_40/HandyControl_Net_40/Themes/Styles/Base/ComboBoxBaseStyle.xaml
index 5bb899f8..52ccd53e 100644
--- a/src/Net_40/HandyControl_Net_40/Themes/Styles/Base/ComboBoxBaseStyle.xaml
+++ b/src/Net_40/HandyControl_Net_40/Themes/Styles/Base/ComboBoxBaseStyle.xaml
@@ -824,6 +824,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -939,6 +1073,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Net_GE45/HandyControl_Net_GE45/Themes/Styles/Base/ComboBoxBaseStyle.xaml b/src/Net_GE45/HandyControl_Net_GE45/Themes/Styles/Base/ComboBoxBaseStyle.xaml
index 90d37234..29c6a7cd 100644
--- a/src/Net_GE45/HandyControl_Net_GE45/Themes/Styles/Base/ComboBoxBaseStyle.xaml
+++ b/src/Net_GE45/HandyControl_Net_GE45/Themes/Styles/Base/ComboBoxBaseStyle.xaml
@@ -887,6 +887,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1009,6 +1150,144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Shared/HandyControlDemo_Shared/Service/DataService.cs b/src/Shared/HandyControlDemo_Shared/Service/DataService.cs
index 0e88b5a7..728a108e 100644
--- a/src/Shared/HandyControlDemo_Shared/Service/DataService.cs
+++ b/src/Shared/HandyControlDemo_Shared/Service/DataService.cs
@@ -72,7 +72,7 @@ namespace HandyControlDemo.Service
var list = new List();
for (var i = 1; i <= 9; i++)
{
- list.Add(converter.Convert(Properties.Langs.Lang.Text, null, i, CultureInfo.CurrentCulture)?.ToString());
+ list.Add($"{converter.Convert(Properties.Langs.Lang.Text, null, i, CultureInfo.CurrentCulture)}{i}");
}
return list;
diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/ComboBoxDemoCtl.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/ComboBoxDemoCtl.xaml
index 045e2025..efd9a0ad 100644
--- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/ComboBoxDemoCtl.xaml
+++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/ComboBoxDemoCtl.xaml
@@ -15,12 +15,12 @@
-
+
-
+
diff --git a/src/Shared/HandyControl_Shared/Controls/Input/ComboBox.cs b/src/Shared/HandyControl_Shared/Controls/Input/ComboBox.cs
index 7d6baf16..73f286b7 100644
--- a/src/Shared/HandyControl_Shared/Controls/Input/ComboBox.cs
+++ b/src/Shared/HandyControl_Shared/Controls/Input/ComboBox.cs
@@ -1,15 +1,42 @@
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.Threading;
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 netle40
+
+ 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) =>
@@ -21,23 +48,125 @@ namespace HandyControl.Controls
}));
}
- protected override void OnSelectionChanged(SelectionChangedEventArgs e)
+ 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 netle40
+ _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 !netle40
+ Delay = 500,
+#endif
+ Source = this
+ });
+ _editableTextBox.GotFocus += EditableTextBox_GotFocus;
+ _editableTextBox.LostFocus += EditableTextBox_LostFocus;
+ }
+ }
+ }
+ }
+
+#if netle40
+
+ private void AutoCompleteTimer_Tick(object sender, EventArgs e)
+ {
+ UpdateSearchItems(_searchText);
+ _autoCompleteTimer.Stop();
+ }
+
+#endif
+
+ private void EditableTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
- base.OnSelectionChanged(e);
VerifyData();
}
+ private void EditableTextBox_LostFocus(object sender, RoutedEventArgs e)
+ {
+ if (_autoPopupAutoComplete != null)
+ {
+ _autoPopupAutoComplete.IsOpen = false;
+ }
+ }
+
+#if !netle40
+ 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 netle40
+ _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);
+ get => (bool)GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
@@ -47,12 +176,18 @@ namespace HandyControl.Controls
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(ComboBox), new PropertyMetadata(default(string)));
+ ///
+ /// 错误提示
+ ///
public string ErrorStr
{
- get => (string) GetValue(ErrorStrProperty);
+ get => (string)GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
+ ///
+ /// 文本类型
+ ///
public static readonly DependencyPropertyKey TextTypePropertyKey =
DependencyProperty.RegisterReadOnly("TextType", typeof(TextType), typeof(ComboBox),
new PropertyMetadata(default(TextType)));
@@ -62,9 +197,12 @@ namespace HandyControl.Controls
///
public static readonly DependencyProperty TextTypeProperty = TextTypePropertyKey.DependencyProperty;
+ ///
+ /// 文本类型
+ ///
public TextType TextType
{
- get => (TextType) GetValue(TextTypeProperty);
+ get => (TextType)GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
@@ -74,23 +212,151 @@ namespace HandyControl.Controls
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(ComboBox), new PropertyMetadata(ValueBoxes.FalseBox));
+ ///
+ /// 是否显示清除按钮
+ ///
public bool ShowClearButton
{
- get => (bool) GetValue(ShowClearButtonProperty);
+ 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 netle40
+ 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(Text);
+ result = VerifyFunc.Invoke(value);
}
else
{
- if (!string.IsNullOrEmpty(Text))
+ if (!string.IsNullOrEmpty(value))
{
result = OperationResult.Success();
}
diff --git a/src/Shared/HandyControl_Shared/Controls/Panel/SimplePanel.cs b/src/Shared/HandyControl_Shared/Controls/Panel/SimplePanel.cs
index 7f774b8b..412bb0e2 100644
--- a/src/Shared/HandyControl_Shared/Controls/Panel/SimplePanel.cs
+++ b/src/Shared/HandyControl_Shared/Controls/Panel/SimplePanel.cs
@@ -15,11 +15,9 @@ namespace HandyControl.Controls
protected override Size MeasureOverride(Size availableSize)
{
var maxSize = new Size();
- var children = InternalChildren;
- for (int i = 0, count = children.Count; i < count; ++i)
+ foreach (UIElement child in InternalChildren)
{
- var child = children[i];
if (child != null)
{
child.Measure(availableSize);
diff --git a/src/Shared/HandyControl_Shared/Controls/TabControl/TabControl.cs b/src/Shared/HandyControl_Shared/Controls/TabControl/TabControl.cs
index 6967947d..d08fd693 100644
--- a/src/Shared/HandyControl_Shared/Controls/TabControl/TabControl.cs
+++ b/src/Shared/HandyControl_Shared/Controls/TabControl/TabControl.cs
@@ -164,11 +164,11 @@ namespace HandyControl.Controls
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
- _headerPanel = Template.FindName(HeaderPanelKey, this) as TabPanel;
+ _headerPanel = GetTemplateChild(HeaderPanelKey) as TabPanel;
if (IsEnableTabFill) return;
- _buttonOverflow = Template.FindName(OverflowButtonKey, this) as ContextMenuToggleButton;
+ _buttonOverflow = GetTemplateChild(OverflowButtonKey) as ContextMenuToggleButton;
if (_buttonOverflow != null)
{
_itemShowCount = (int)(ActualWidth / TabItemWidth);
From d118ef2d4b341072c1aa6cdcb9b467c633733662 Mon Sep 17 00:00:00 2001
From: NaBian <836904362@qq.com>
Date: Sat, 11 May 2019 19:19:33 +0800
Subject: [PATCH 2/2] Update ComboBox.cs
---
src/Shared/HandyControl_Shared/Controls/Input/ComboBox.cs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/Shared/HandyControl_Shared/Controls/Input/ComboBox.cs b/src/Shared/HandyControl_Shared/Controls/Input/ComboBox.cs
index 73f286b7..2f23ffe0 100644
--- a/src/Shared/HandyControl_Shared/Controls/Input/ComboBox.cs
+++ b/src/Shared/HandyControl_Shared/Controls/Input/ComboBox.cs
@@ -5,7 +5,9 @@ using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
+#if netle40
using System.Windows.Threading;
+#endif
using HandyControl.Data;
using HandyControl.Interactivity;