This commit is contained in:
Stephan Maier
2024-08-27 08:10:27 +02:00
parent eb5c2fa502
commit 647f938eee
617 changed files with 73086 additions and 7137 deletions

View File

@@ -0,0 +1,612 @@
using System;
using System.Collections;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace AutoCompleteTextBox.Editors
{
[TemplatePart(Name = PartEditor, Type = typeof(TextBox))]
[TemplatePart(Name = PartPopup, Type = typeof(Popup))]
[TemplatePart(Name = PartSelector, Type = typeof(Selector))]
[TemplatePart(Name = PartExpander, Type = typeof(Expander))]
public class AutoCompleteComboBox : Control
{
#region "Fields"
public const string PartEditor = "PART_Editor";
public const string PartPopup = "PART_Popup";
public const string PartSelector = "PART_Selector";
public const string PartExpander = "PART_Expander";
public static readonly DependencyProperty DelayProperty = DependencyProperty.Register("Delay", typeof(int), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(200));
public static readonly DependencyProperty DisplayMemberProperty = DependencyProperty.Register("DisplayMember", typeof(string), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(string.Empty));
public static readonly DependencyProperty IconPlacementProperty = DependencyProperty.Register("IconPlacement", typeof(IconPlacement), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(IconPlacement.Left));
public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(object), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty IconVisibilityProperty = DependencyProperty.Register("IconVisibility", typeof(Visibility), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(Visibility.Visible));
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsLoadingProperty = DependencyProperty.Register("IsLoading", typeof(bool), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty ItemTemplateSelectorProperty = DependencyProperty.Register("ItemTemplateSelector", typeof(DataTemplateSelector), typeof(AutoCompleteComboBox));
public static readonly DependencyProperty LoadingContentProperty = DependencyProperty.Register("LoadingContent", typeof(object), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty ProviderProperty = DependencyProperty.Register("Provider", typeof(IComboSuggestionProvider), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(null, OnSelectedItemChanged));
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(string.Empty, propertyChangedCallback: null, coerceValueCallback: null, isAnimationProhibited: false, defaultUpdateSourceTrigger: UpdateSourceTrigger.LostFocus, flags: FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public static readonly DependencyProperty FilterProperty = DependencyProperty.Register("Filter", typeof(string), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(string.Empty));
public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register("MaxLength", typeof(int), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(0));
public static readonly DependencyProperty CharacterCasingProperty = DependencyProperty.Register("CharacterCasing", typeof(CharacterCasing), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(CharacterCasing.Normal));
public static readonly DependencyProperty MaxPopUpHeightProperty = DependencyProperty.Register("MaxPopUpHeight", typeof(int), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(600));
public static readonly DependencyProperty MaxPopUpWidthProperty = DependencyProperty.Register("MaxPopUpWidth", typeof(int), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(2000));
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register("Watermark", typeof(string), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(string.Empty));
public static readonly DependencyProperty SuggestionBackgroundProperty = DependencyProperty.Register("SuggestionBackground", typeof(Brush), typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(Brushes.White));
private bool _isUpdatingText;
private bool _selectionCancelled;
private SuggestionsAdapter _suggestionsAdapter;
#endregion
#region "Constructors"
static AutoCompleteComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AutoCompleteComboBox), new FrameworkPropertyMetadata(typeof(AutoCompleteComboBox)));
}
#endregion
#region "Properties"
public int MaxPopupHeight
{
get => (int)GetValue(MaxPopUpHeightProperty);
set => SetValue(MaxPopUpHeightProperty, value);
}
public int MaxPopupWidth
{
get => (int)GetValue(MaxPopUpWidthProperty);
set => SetValue(MaxPopUpWidthProperty, value);
}
public BindingEvaluator BindingEvaluator { get; set; }
public CharacterCasing CharacterCasing
{
get => (CharacterCasing)GetValue(CharacterCasingProperty);
set => SetValue(CharacterCasingProperty, value);
}
public int MaxLength
{
get => (int)GetValue(MaxLengthProperty);
set => SetValue(MaxLengthProperty, value);
}
public int Delay
{
get => (int)GetValue(DelayProperty);
set => SetValue(DelayProperty, value);
}
public string DisplayMember
{
get => (string)GetValue(DisplayMemberProperty);
set => SetValue(DisplayMemberProperty, value);
}
public TextBox Editor { get; set; }
public Expander Expander { get; set; }
public DispatcherTimer FetchTimer { get; set; }
public string Filter
{
get => (string)GetValue(FilterProperty);
set => SetValue(FilterProperty, value);
}
public object Icon
{
get => GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
public IconPlacement IconPlacement
{
get => (IconPlacement)GetValue(IconPlacementProperty);
set => SetValue(IconPlacementProperty, value);
}
public Visibility IconVisibility
{
get => (Visibility)GetValue(IconVisibilityProperty);
set => SetValue(IconVisibilityProperty, value);
}
public bool IsDropDownOpen
{
get => (bool)GetValue(IsDropDownOpenProperty);
set
{
this.Expander.IsExpanded = value;
SetValue(IsDropDownOpenProperty, value);
}
}
public bool IsLoading
{
get => (bool)GetValue(IsLoadingProperty);
set => SetValue(IsLoadingProperty, value);
}
public bool IsReadOnly
{
get => (bool)GetValue(IsReadOnlyProperty);
set => SetValue(IsReadOnlyProperty, value);
}
public Selector ItemsSelector { get; set; }
public DataTemplate ItemTemplate
{
get => (DataTemplate)GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
}
public DataTemplateSelector ItemTemplateSelector
{
get => ((DataTemplateSelector)(GetValue(ItemTemplateSelectorProperty)));
set => SetValue(ItemTemplateSelectorProperty, value);
}
public object LoadingContent
{
get => GetValue(LoadingContentProperty);
set => SetValue(LoadingContentProperty, value);
}
public Popup Popup { get; set; }
public IComboSuggestionProvider Provider
{
get => (IComboSuggestionProvider)GetValue(ProviderProperty);
set => SetValue(ProviderProperty, value);
}
public object SelectedItem
{
get => GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}
public SelectionAdapter SelectionAdapter { get; set; }
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public string Watermark
{
get => (string)GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
public Brush SuggestionBackground
{
get => (Brush)GetValue(SuggestionBackgroundProperty);
set => SetValue(SuggestionBackgroundProperty, value);
}
#endregion
#region "Methods"
public static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
AutoCompleteComboBox act = null;
act = d as AutoCompleteComboBox;
if (act != null)
{
if (act.Editor != null & !act._isUpdatingText)
{
act._isUpdatingText = true;
act.Editor.Text = act.BindingEvaluator.Evaluate(e.NewValue);
act._isUpdatingText = false;
}
}
}
private void ScrollToSelectedItem()
{
if (ItemsSelector is ListBox listBox && listBox.SelectedItem != null)
listBox.ScrollIntoView(listBox.SelectedItem);
}
public new BindingExpressionBase SetBinding(DependencyProperty dp, BindingBase binding){
var res = base.SetBinding(dp, binding);
CheckForParentTextBindingChange();
return res;
}
public new BindingExpressionBase SetBinding(DependencyProperty dp, String path) {
var res = base.SetBinding(dp, path);
CheckForParentTextBindingChange();
return res;
}
public new void ClearValue(DependencyPropertyKey key) {
base.ClearValue(key);
CheckForParentTextBindingChange();
}
public new void ClearValue(DependencyProperty dp) {
base.ClearValue(dp);
CheckForParentTextBindingChange();
}
private void CheckForParentTextBindingChange(bool force=false) {
var CurrentBindingMode = BindingOperations.GetBinding(this, TextProperty)?.UpdateSourceTrigger ?? UpdateSourceTrigger.Default;
if (CurrentBindingMode != UpdateSourceTrigger.PropertyChanged)//preventing going any less frequent than property changed
CurrentBindingMode = UpdateSourceTrigger.Default;
if (CurrentBindingMode == CurrentTextboxTextBindingUpdateMode && force == false)
return;
var binding = new Binding {
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = CurrentBindingMode,
Path = new PropertyPath(nameof(Text)),
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
};
CurrentTextboxTextBindingUpdateMode = CurrentBindingMode;
Editor?.SetBinding(TextBox.TextProperty, binding);
}
private UpdateSourceTrigger CurrentTextboxTextBindingUpdateMode;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Editor = Template.FindName(PartEditor, this) as TextBox;
Popup = Template.FindName(PartPopup, this) as Popup;
ItemsSelector = Template.FindName(PartSelector, this) as Selector;
Expander = Template.FindName(PartExpander, this) as Expander;
BindingEvaluator = new BindingEvaluator(new Binding(DisplayMember));
if (Editor != null)
{
Editor.TextChanged += OnEditorTextChanged;
Editor.PreviewKeyDown += OnEditorKeyDown;
Editor.LostFocus += OnEditorLostFocus;
CheckForParentTextBindingChange(true);
if (SelectedItem != null)
{
_isUpdatingText = true;
Editor.Text = BindingEvaluator.Evaluate(SelectedItem);
_isUpdatingText = false;
}
}
if (Expander != null)
{
Expander.IsExpanded = false;
Expander.Collapsed += Expander_Expanded;
Expander.Expanded += Expander_Expanded;
}
GotFocus += AutoCompleteComboBox_GotFocus;
if (Popup != null)
{
Popup.StaysOpen = false;
Popup.Opened += OnPopupOpened;
Popup.Closed += OnPopupClosed;
}
if (ItemsSelector != null)
{
SelectionAdapter = new SelectionAdapter(ItemsSelector);
SelectionAdapter.Commit += OnSelectionAdapterCommit;
SelectionAdapter.Cancel += OnSelectionAdapterCancel;
SelectionAdapter.SelectionChanged += OnSelectionAdapterSelectionChanged;
ItemsSelector.PreviewMouseDown += ItemsSelector_PreviewMouseDown;
}
}
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
this.IsDropDownOpen = Expander.IsExpanded;
if (!this.IsDropDownOpen)
{
return;
}
if (_suggestionsAdapter == null)
{
_suggestionsAdapter = new SuggestionsAdapter(this);
}
if (SelectedItem != null || String.IsNullOrWhiteSpace(Editor.Text))
_suggestionsAdapter.ShowFullCollection();
}
private void ItemsSelector_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if ((e.OriginalSource as FrameworkElement)?.DataContext == null)
return;
if (!ItemsSelector.Items.Contains(((FrameworkElement)e.OriginalSource)?.DataContext))
return;
ItemsSelector.SelectedItem = ((FrameworkElement)e.OriginalSource)?.DataContext;
OnSelectionAdapterCommit(SelectionAdapter.EventCause.ItemClicked);
e.Handled = true;
}
private void AutoCompleteComboBox_GotFocus(object sender, RoutedEventArgs e)
{
Editor?.Focus();
}
private string GetDisplayText(object dataItem)
{
if (BindingEvaluator == null)
{
BindingEvaluator = new BindingEvaluator(new Binding(DisplayMember));
}
if (dataItem == null)
{
return string.Empty;
}
if (string.IsNullOrEmpty(DisplayMember))
{
return dataItem.ToString();
}
return BindingEvaluator.Evaluate(dataItem);
}
private void OnEditorKeyDown(object sender, KeyEventArgs e)
{
if (SelectionAdapter != null)
{
if (IsDropDownOpen)
SelectionAdapter.HandleKeyDown(e);
else
IsDropDownOpen = e.Key == Key.Down || e.Key == Key.Up;
}
}
private void OnEditorLostFocus(object sender, RoutedEventArgs e)
{
if (!IsKeyboardFocusWithin)
{
IsDropDownOpen = false;
}
}
private void OnEditorTextChanged(object sender, TextChangedEventArgs e)
{
Text = Editor.Text;
if (_isUpdatingText)
return;
if (FetchTimer == null)
{
FetchTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(Delay) };
FetchTimer.Tick += OnFetchTimerTick;
}
FetchTimer.IsEnabled = false;
FetchTimer.Stop();
SetSelectedItem(null);
if (Editor.Text.Length > 0)
{
FetchTimer.IsEnabled = true;
FetchTimer.Start();
}
else
{
IsDropDownOpen = false;
}
}
private void OnFetchTimerTick(object sender, EventArgs e)
{
FetchTimer.IsEnabled = false;
FetchTimer.Stop();
if (Provider != null && ItemsSelector != null)
{
Filter = Editor.Text;
if (_suggestionsAdapter == null)
{
_suggestionsAdapter = new SuggestionsAdapter(this);
}
_suggestionsAdapter.GetSuggestions(Filter);
}
}
private void OnPopupClosed(object sender, EventArgs e)
{
if (!_selectionCancelled)
{
OnSelectionAdapterCommit(SelectionAdapter.EventCause.PopupClosed);
}
}
private void OnPopupOpened(object sender, EventArgs e)
{
_selectionCancelled = false;
ItemsSelector.SelectedItem = SelectedItem;
}
public event EventHandler<SelectionAdapter.PreSelectionAdapterFinishArgs> PreSelectionAdapterFinish;
private bool PreSelectionEventSomeoneHandled(SelectionAdapter.EventCause cause, bool is_cancel) {
if (PreSelectionAdapterFinish == null)
return false;
var args = new SelectionAdapter.PreSelectionAdapterFinishArgs { cause = cause, is_cancel = is_cancel };
PreSelectionAdapterFinish?.Invoke(this, args);
return args.handled;
}
private void OnSelectionAdapterCancel(SelectionAdapter.EventCause cause)
{
if (PreSelectionEventSomeoneHandled(cause, true))
return;
_isUpdatingText = true;
Editor.Text = SelectedItem == null ? Filter : GetDisplayText(SelectedItem);
Editor.SelectionStart = Editor.Text.Length;
Editor.SelectionLength = 0;
_isUpdatingText = false;
IsDropDownOpen = false;
_selectionCancelled = true;
}
private void OnSelectionAdapterCommit(SelectionAdapter.EventCause cause)
{
if (PreSelectionEventSomeoneHandled(cause, false))
return;
if (ItemsSelector.SelectedItem != null)
{
SelectedItem = ItemsSelector.SelectedItem;
_isUpdatingText = true;
Editor.Text = GetDisplayText(ItemsSelector.SelectedItem);
SetSelectedItem(ItemsSelector.SelectedItem);
_isUpdatingText = false;
IsDropDownOpen = false;
}
}
private void OnSelectionAdapterSelectionChanged()
{
_isUpdatingText = true;
Editor.Text = ItemsSelector.SelectedItem == null ? Filter : GetDisplayText(ItemsSelector.SelectedItem);
Editor.SelectionStart = Editor.Text.Length;
Editor.SelectionLength = 0;
ScrollToSelectedItem();
_isUpdatingText = false;
}
private void SetSelectedItem(object item)
{
_isUpdatingText = true;
SelectedItem = item;
_isUpdatingText = false;
}
#endregion
#region "Nested Types"
private class SuggestionsAdapter
{
#region "Fields"
private readonly AutoCompleteComboBox _actb;
private string _filter;
#endregion
#region "Constructors"
public SuggestionsAdapter(AutoCompleteComboBox actb)
{
_actb = actb;
}
#endregion
#region "Methods"
public void GetSuggestions(string searchText)
{
_actb.IsLoading = true;
// Do not open drop down if control is not focused
if (_actb.IsKeyboardFocusWithin)
_actb.IsDropDownOpen = true;
_actb.ItemsSelector.ItemsSource = null;
ParameterizedThreadStart thInfo = GetSuggestionsAsync;
Thread th = new Thread(thInfo);
_filter = searchText;
th.Start(new object[] { searchText, _actb.Provider });
}
public void ShowFullCollection()
{
_filter = string.Empty;
_actb.IsLoading = true;
// Do not open drop down if control is not focused
if (_actb.IsKeyboardFocusWithin)
_actb.IsDropDownOpen = true;
_actb.ItemsSelector.ItemsSource = null;
ParameterizedThreadStart thInfo = GetFullCollectionAsync;
Thread th = new Thread(thInfo);
th.Start(_actb.Provider);
}
private void DisplaySuggestions(IEnumerable suggestions, string filter)
{
if (_filter != filter)
{
return;
}
_actb.IsLoading = false;
_actb.ItemsSelector.ItemsSource = suggestions;
// Close drop down if there are no items
if (_actb.IsDropDownOpen)
{
_actb.IsDropDownOpen = _actb.ItemsSelector.HasItems;
}
}
private void GetSuggestionsAsync(object param)
{
if (param is object[] args)
{
string searchText = Convert.ToString(args[0]);
if (args[1] is IComboSuggestionProvider provider)
{
IEnumerable list = provider.GetSuggestions(searchText);
_actb.Dispatcher.BeginInvoke(new Action<IEnumerable, string>(DisplaySuggestions), DispatcherPriority.Background, list, searchText);
}
}
}
private void GetFullCollectionAsync(object param)
{
if (param is IComboSuggestionProvider provider)
{
IEnumerable list = provider.GetFullCollection();
_actb.Dispatcher.BeginInvoke(new Action<IEnumerable, string>(DisplaySuggestions), DispatcherPriority.Background, list, string.Empty);
}
}
#endregion
}
#endregion
}
}

View File

@@ -0,0 +1,569 @@
using System;
using System.Collections;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace AutoCompleteTextBox.Editors
{
[TemplatePart(Name = PartEditor, Type = typeof(TextBox))]
[TemplatePart(Name = PartPopup, Type = typeof(Popup))]
[TemplatePart(Name = PartSelector, Type = typeof(Selector))]
public class AutoCompleteTextBox : Control
{
#region "Fields"
public const string PartEditor = "PART_Editor";
public const string PartPopup = "PART_Popup";
public const string PartSelector = "PART_Selector";
public static readonly DependencyProperty DelayProperty = DependencyProperty.Register("Delay", typeof(int), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(200));
public static readonly DependencyProperty DisplayMemberProperty = DependencyProperty.Register("DisplayMember", typeof(string), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(string.Empty));
public static readonly DependencyProperty IconPlacementProperty = DependencyProperty.Register("IconPlacement", typeof(IconPlacement), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(IconPlacement.Left));
public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(object), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty IconVisibilityProperty = DependencyProperty.Register("IconVisibility", typeof(Visibility), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(Visibility.Visible));
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsLoadingProperty = DependencyProperty.Register("IsLoading", typeof(bool), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty ItemTemplateSelectorProperty = DependencyProperty.Register("ItemTemplateSelector", typeof(DataTemplateSelector), typeof(AutoCompleteTextBox));
public static readonly DependencyProperty LoadingContentProperty = DependencyProperty.Register("LoadingContent", typeof(object), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty ProviderProperty = DependencyProperty.Register("Provider", typeof(ISuggestionProvider), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(null, OnSelectedItemChanged));
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(string.Empty, propertyChangedCallback:null,coerceValueCallback:null, isAnimationProhibited:false, defaultUpdateSourceTrigger: UpdateSourceTrigger.LostFocus, flags: FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public static readonly DependencyProperty FilterProperty = DependencyProperty.Register("Filter", typeof(string), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(string.Empty));
public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register("MaxLength", typeof(int), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(0));
public static readonly DependencyProperty CharacterCasingProperty = DependencyProperty.Register("CharacterCasing", typeof(CharacterCasing), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(CharacterCasing.Normal));
public static readonly DependencyProperty MaxPopUpHeightProperty = DependencyProperty.Register("MaxPopUpHeight", typeof(int), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(600));
public static readonly DependencyProperty MaxPopUpWidthProperty = DependencyProperty.Register("MaxPopUpWidth", typeof(int), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(2000));
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register("Watermark", typeof(string), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(string.Empty));
public static readonly DependencyProperty SuggestionBackgroundProperty = DependencyProperty.Register("SuggestionBackground", typeof(Brush), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(Brushes.White));
private bool _isUpdatingText;
private bool _selectionCancelled;
private SuggestionsAdapter _suggestionsAdapter;
#endregion
#region "Constructors"
static AutoCompleteTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(typeof(AutoCompleteTextBox)));
FocusableProperty.OverrideMetadata(typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(true));
}
#endregion
#region "Properties"
public int MaxPopupHeight
{
get => (int)GetValue(MaxPopUpHeightProperty);
set => SetValue(MaxPopUpHeightProperty, value);
}
public int MaxPopupWidth
{
get => (int)GetValue(MaxPopUpWidthProperty);
set => SetValue(MaxPopUpWidthProperty, value);
}
public BindingEvaluator BindingEvaluator { get; set; }
public CharacterCasing CharacterCasing
{
get => (CharacterCasing)GetValue(CharacterCasingProperty);
set => SetValue(CharacterCasingProperty, value);
}
public int MaxLength
{
get => (int)GetValue(MaxLengthProperty);
set => SetValue(MaxLengthProperty, value);
}
public int Delay
{
get => (int)GetValue(DelayProperty);
set => SetValue(DelayProperty, value);
}
public string DisplayMember
{
get => (string)GetValue(DisplayMemberProperty);
set => SetValue(DisplayMemberProperty, value);
}
public TextBox Editor { get; set; }
public DispatcherTimer FetchTimer { get; set; }
public string Filter
{
get => (string)GetValue(FilterProperty);
set => SetValue(FilterProperty, value);
}
public object Icon
{
get => GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
public IconPlacement IconPlacement
{
get => (IconPlacement)GetValue(IconPlacementProperty);
set => SetValue(IconPlacementProperty, value);
}
public Visibility IconVisibility
{
get => (Visibility)GetValue(IconVisibilityProperty);
set => SetValue(IconVisibilityProperty, value);
}
public bool IsDropDownOpen
{
get => (bool)GetValue(IsDropDownOpenProperty);
set => SetValue(IsDropDownOpenProperty, value);
}
public bool IsLoading
{
get => (bool)GetValue(IsLoadingProperty);
set => SetValue(IsLoadingProperty, value);
}
public bool IsReadOnly
{
get => (bool)GetValue(IsReadOnlyProperty);
set => SetValue(IsReadOnlyProperty, value);
}
public Selector ItemsSelector { get; set; }
public DataTemplate ItemTemplate
{
get => (DataTemplate)GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
}
public DataTemplateSelector ItemTemplateSelector
{
get => ((DataTemplateSelector)(GetValue(ItemTemplateSelectorProperty)));
set => SetValue(ItemTemplateSelectorProperty, value);
}
public object LoadingContent
{
get => GetValue(LoadingContentProperty);
set => SetValue(LoadingContentProperty, value);
}
public Popup Popup { get; set; }
public ISuggestionProvider Provider
{
get => (ISuggestionProvider)GetValue(ProviderProperty);
set => SetValue(ProviderProperty, value);
}
public object SelectedItem
{
get => GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}
public SelectionAdapter SelectionAdapter { get; set; }
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public string Watermark
{
get => (string)GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
public Brush SuggestionBackground
{
get => (Brush)GetValue(SuggestionBackgroundProperty);
set => SetValue(SuggestionBackgroundProperty, value);
}
#endregion
#region "Methods"
public static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
AutoCompleteTextBox act = null;
act = d as AutoCompleteTextBox;
if (act != null)
{
if (act.Editor != null & !act._isUpdatingText)
{
act._isUpdatingText = true;
act.Editor.Text = act.BindingEvaluator.Evaluate(e.NewValue);
act._isUpdatingText = false;
}
}
}
private void ScrollToSelectedItem()
{
if (ItemsSelector is ListBox listBox && listBox.SelectedItem != null)
listBox.ScrollIntoView(listBox.SelectedItem);
}
public new BindingExpressionBase SetBinding(DependencyProperty dp, BindingBase binding){
var res = base.SetBinding(dp, binding);
CheckForParentTextBindingChange();
return res;
}
public new BindingExpressionBase SetBinding(DependencyProperty dp, String path) {
var res = base.SetBinding(dp, path);
CheckForParentTextBindingChange();
return res;
}
public new void ClearValue(DependencyPropertyKey key) {
base.ClearValue(key);
CheckForParentTextBindingChange();
}
public new void ClearValue(DependencyProperty dp) {
base.ClearValue(dp);
CheckForParentTextBindingChange();
}
private void CheckForParentTextBindingChange(bool force=false) {
var CurrentBindingMode = BindingOperations.GetBinding(this, TextProperty)?.UpdateSourceTrigger ?? UpdateSourceTrigger.Default;
if (CurrentBindingMode != UpdateSourceTrigger.PropertyChanged)//preventing going any less frequent than property changed
CurrentBindingMode = UpdateSourceTrigger.Default;
if (CurrentBindingMode == CurrentTextboxTextBindingUpdateMode && force == false)
return;
var binding = new Binding {
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = CurrentBindingMode,
Path = new PropertyPath(nameof(Text)),
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
};
CurrentTextboxTextBindingUpdateMode = CurrentBindingMode;
Editor?.SetBinding(TextBox.TextProperty, binding);
}
private UpdateSourceTrigger CurrentTextboxTextBindingUpdateMode;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Editor = Template.FindName(PartEditor, this) as TextBox;
Editor.Focus();
Popup = Template.FindName(PartPopup, this) as Popup;
ItemsSelector = Template.FindName(PartSelector, this) as Selector;
BindingEvaluator = new BindingEvaluator(new Binding(DisplayMember));
if (Editor != null)
{
Editor.TextChanged += OnEditorTextChanged;
Editor.PreviewKeyDown += OnEditorKeyDown;
Editor.LostFocus += OnEditorLostFocus;
CheckForParentTextBindingChange(true);
if (SelectedItem != null)
{
_isUpdatingText = true;
Editor.Text = BindingEvaluator.Evaluate(SelectedItem);
_isUpdatingText = false;
}
}
GotFocus += AutoCompleteTextBox_GotFocus;
GotKeyboardFocus += AutoCompleteTextBox_GotKeyboardFocus;
if (Popup != null)
{
Popup.StaysOpen = false;
Popup.Opened += OnPopupOpened;
Popup.Closed += OnPopupClosed;
}
if (ItemsSelector != null)
{
SelectionAdapter = new SelectionAdapter(ItemsSelector);
SelectionAdapter.Commit += OnSelectionAdapterCommit;
SelectionAdapter.Cancel += OnSelectionAdapterCancel;
SelectionAdapter.SelectionChanged += OnSelectionAdapterSelectionChanged;
ItemsSelector.PreviewMouseDown += ItemsSelector_PreviewMouseDown;
}
}
private void ItemsSelector_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if ((e.OriginalSource as FrameworkElement)?.DataContext == null)
return;
if (!ItemsSelector.Items.Contains(((FrameworkElement)e.OriginalSource)?.DataContext))
return;
ItemsSelector.SelectedItem = ((FrameworkElement)e.OriginalSource)?.DataContext;
OnSelectionAdapterCommit(SelectionAdapter.EventCause.MouseDown);
e.Handled = true;
}
private void AutoCompleteTextBox_GotFocus(object sender, RoutedEventArgs e)
{
Editor?.Focus();
}
private void AutoCompleteTextBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) {
if (e.NewFocus != this)
return;
if (e.OldFocus == Editor)
MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous));
}
private string GetDisplayText(object dataItem)
{
if (BindingEvaluator == null)
{
BindingEvaluator = new BindingEvaluator(new Binding(DisplayMember));
}
if (dataItem == null)
{
return string.Empty;
}
if (string.IsNullOrEmpty(DisplayMember))
{
return dataItem.ToString();
}
return BindingEvaluator.Evaluate(dataItem);
}
private void OnEditorKeyDown(object sender, KeyEventArgs e)
{
if (SelectionAdapter != null)
{
if (IsDropDownOpen)
SelectionAdapter.HandleKeyDown(e);
else
IsDropDownOpen = e.Key == Key.Down || e.Key == Key.Up;
}
}
private void OnEditorLostFocus(object sender, RoutedEventArgs e)
{
if (!IsKeyboardFocusWithin)
{
IsDropDownOpen = false;
}
}
private void OnEditorTextChanged(object sender, TextChangedEventArgs e)
{
Text = Editor.Text;
if (_isUpdatingText)
return;
if (FetchTimer == null)
{
FetchTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(Delay) };
FetchTimer.Tick += OnFetchTimerTick;
}
FetchTimer.IsEnabled = false;
FetchTimer.Stop();
SetSelectedItem(null);
if (Editor.Text.Length > 0)
{
FetchTimer.IsEnabled = true;
FetchTimer.Start();
}
else
{
IsDropDownOpen = false;
}
}
private void OnFetchTimerTick(object sender, EventArgs e)
{
FetchTimer.IsEnabled = false;
FetchTimer.Stop();
if (Provider != null && ItemsSelector != null)
{
Filter = Editor.Text;
if (_suggestionsAdapter == null)
{
_suggestionsAdapter = new SuggestionsAdapter(this);
}
_suggestionsAdapter.GetSuggestions(Filter);
}
}
private void OnPopupClosed(object sender, EventArgs e)
{
if (!_selectionCancelled)
{
OnSelectionAdapterCommit(SelectionAdapter.EventCause.PopupClosed);
}
}
private void OnPopupOpened(object sender, EventArgs e)
{
_selectionCancelled = false;
ItemsSelector.SelectedItem = SelectedItem;
}
private void OnSelectionAdapterCancel(SelectionAdapter.EventCause cause)
{
if (PreSelectionEventSomeoneHandled(cause, true))
return;
_isUpdatingText = true;
Editor.Text = SelectedItem == null ? Filter : GetDisplayText(SelectedItem);
Editor.SelectionStart = Editor.Text.Length;
Editor.SelectionLength = 0;
_isUpdatingText = false;
IsDropDownOpen = false;
_selectionCancelled = true;
}
public event EventHandler<SelectionAdapter.PreSelectionAdapterFinishArgs> PreSelectionAdapterFinish;
private bool PreSelectionEventSomeoneHandled(SelectionAdapter.EventCause cause, bool is_cancel) {
if (PreSelectionAdapterFinish == null)
return false;
var args = new SelectionAdapter.PreSelectionAdapterFinishArgs { cause = cause, is_cancel = is_cancel };
PreSelectionAdapterFinish?.Invoke(this, args);
return args.handled;
}
private void OnSelectionAdapterCommit(SelectionAdapter.EventCause cause)
{
if (PreSelectionEventSomeoneHandled(cause, false))
return;
if (ItemsSelector.SelectedItem != null)
{
SelectedItem = ItemsSelector.SelectedItem;
_isUpdatingText = true;
Editor.Text = GetDisplayText(ItemsSelector.SelectedItem);
SetSelectedItem(ItemsSelector.SelectedItem);
_isUpdatingText = false;
IsDropDownOpen = false;
}
}
private void OnSelectionAdapterSelectionChanged()
{
_isUpdatingText = true;
Editor.Text = ItemsSelector.SelectedItem == null ? Filter : GetDisplayText(ItemsSelector.SelectedItem);
Editor.SelectionStart = Editor.Text.Length;
Editor.SelectionLength = 0;
ScrollToSelectedItem();
_isUpdatingText = false;
}
private void SetSelectedItem(object item)
{
_isUpdatingText = true;
SelectedItem = item;
_isUpdatingText = false;
}
#endregion
#region "Nested Types"
private class SuggestionsAdapter
{
#region "Fields"
private readonly AutoCompleteTextBox _actb;
private string _filter;
#endregion
#region "Constructors"
public SuggestionsAdapter(AutoCompleteTextBox actb)
{
_actb = actb;
}
#endregion
#region "Methods"
public void GetSuggestions(string searchText)
{
_filter = searchText;
_actb.IsLoading = true;
// Do not open drop down if control is not focused
if (_actb.IsKeyboardFocusWithin)
_actb.IsDropDownOpen = true;
_actb.ItemsSelector.ItemsSource = null;
ParameterizedThreadStart thInfo = GetSuggestionsAsync;
Thread th = new Thread(thInfo);
th.Start(new object[] { searchText, _actb.Provider });
}
private void DisplaySuggestions(IEnumerable suggestions, string filter)
{
if (_filter != filter)
{
return;
}
_actb.IsLoading = false;
_actb.ItemsSelector.ItemsSource = suggestions;
// Close drop down if there are no items
if (_actb.IsDropDownOpen)
{
_actb.IsDropDownOpen = _actb.ItemsSelector.HasItems;
}
}
private void GetSuggestionsAsync(object param)
{
if (param is object[] args)
{
string searchText = Convert.ToString(args[0]);
if (args[1] is ISuggestionProvider provider)
{
IEnumerable list = provider.GetSuggestions(searchText);
_actb.Dispatcher.BeginInvoke(new Action<IEnumerable, string>(DisplaySuggestions), DispatcherPriority.Background, list, searchText);
}
}
}
#endregion
}
#endregion
}
}

View File

@@ -0,0 +1,16 @@
using System.Collections;
namespace AutoCompleteTextBox.Editors
{
public interface IComboSuggestionProvider
{
#region Public Methods
IEnumerable GetSuggestions(string filter);
IEnumerable GetFullCollection();
#endregion Public Methods
}
}

View File

@@ -0,0 +1,15 @@
using System.Collections;
namespace AutoCompleteTextBox.Editors
{
public interface ISuggestionProvider
{
#region Public Methods
IEnumerable GetSuggestions(string filter);
#endregion Public Methods
}
}

View File

@@ -0,0 +1,122 @@
using System.Diagnostics;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace AutoCompleteTextBox.Editors
{
public class SelectionAdapter
{
public class PreSelectionAdapterFinishArgs {
public EventCause cause;
public bool is_cancel;
public bool handled;
}
#region "Fields"
#endregion
#region "Constructors"
public SelectionAdapter(Selector selector)
{
SelectorControl = selector;
SelectorControl.PreviewMouseUp += OnSelectorMouseDown;
}
#endregion
#region "Events"
public enum EventCause { Other, PopupClosed, ItemClicked, EnterPressed, EscapePressed, TabPressed, MouseDown}
public delegate void CancelEventHandler(EventCause cause);
public delegate void CommitEventHandler(EventCause cause);
public delegate void SelectionChangedEventHandler();
public event CancelEventHandler Cancel;
public event CommitEventHandler Commit;
public event SelectionChangedEventHandler SelectionChanged;
#endregion
#region "Properties"
public Selector SelectorControl { get; set; }
#endregion
#region "Methods"
public void HandleKeyDown(KeyEventArgs key)
{
switch (key.Key)
{
case Key.Down:
IncrementSelection();
break;
case Key.Up:
DecrementSelection();
break;
case Key.Enter:
Commit?.Invoke(EventCause.EnterPressed);
break;
case Key.Escape:
Cancel?.Invoke(EventCause.EscapePressed);
break;
case Key.Tab:
Commit?.Invoke(EventCause.TabPressed);
break;
default:
return;
}
key.Handled = true;
}
private void DecrementSelection()
{
if (SelectorControl.SelectedIndex == -1)
{
SelectorControl.SelectedIndex = SelectorControl.Items.Count - 1;
}
else
{
SelectorControl.SelectedIndex -= 1;
}
SelectionChanged?.Invoke();
}
private void IncrementSelection()
{
if (SelectorControl.SelectedIndex == SelectorControl.Items.Count - 1)
{
SelectorControl.SelectedIndex = -1;
}
else
{
SelectorControl.SelectedIndex += 1;
}
SelectionChanged?.Invoke();
}
private void OnSelectorMouseDown(object sender, MouseButtonEventArgs e)
{
// If sender is the RepeatButton from the scrollbar we need to
// to skip this event otherwise focus get stuck in the RepeatButton
// and list is scrolled up or down til the end.
if (e.OriginalSource.GetType() != typeof(RepeatButton))
{
Commit?.Invoke(EventCause.MouseDown);
e.Handled = true;
}
}
#endregion
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections;
namespace AutoCompleteTextBox.Editors
{
public class SuggestionProvider : ISuggestionProvider
{
#region Private Fields
private readonly Func<string, IEnumerable> _method;
#endregion Private Fields
#region Public Constructors
public SuggestionProvider(Func<string, IEnumerable> method)
{
_method = method ?? throw new ArgumentNullException(nameof(method));
}
#endregion Public Constructors
#region Public Methods
public IEnumerable GetSuggestions(string filter)
{
return _method(filter);
}
#endregion Public Methods
}
}

View File

@@ -0,0 +1,268 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:editors="clr-namespace:AutoCompleteTextBox.Editors">
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
<Style x:Key="TextBoxSuggestionItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="ContentBorder" Background="{Binding Path=SuggestionBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=editors:AutoCompleteTextBox}, Mode=OneWay}">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="ContentBorder" Property="Background" Value="{x:Static SystemColors.HighlightBrush}" />
<Setter Property="TextElement.Foreground" Value="{x:Static SystemColors.HighlightTextBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ComboBoxSuggestionItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="ContentBorder" Background="{Binding Path=SuggestionBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=editors:AutoCompleteComboBox}, Mode=OneWay}">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="ContentBorder" Property="Background" Value="{x:Static SystemColors.HighlightBrush}" />
<Setter Property="TextElement.Foreground" Value="{x:Static SystemColors.HighlightTextBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TransparentTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<ScrollViewer
x:Name="PART_ContentHost"
Background="Transparent"
CanContentScroll="True"
Focusable="True"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type editors:AutoCompleteTextBox}">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="#FFABADB3" />
<Setter Property="SuggestionBackground" Value="White" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="AllowDrop" Value="true" />
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type editors:AutoCompleteTextBox}">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="0">
<Grid>
<DockPanel>
<ContentPresenter
x:Name="PART_Icon"
ContentSource="Icon"
Visibility="{TemplateBinding IconVisibility}" />
<Grid>
<TextBlock
x:Name="PART_Watermark"
Margin="3,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Focusable="False"
Foreground="Gray"
Text="{TemplateBinding Watermark}"
Visibility="Collapsed" />
<TextBox
x:Name="PART_Editor"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
CharacterCasing="{Binding Path=CharacterCasing, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}"
Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=OneWay}"
MaxLength="{Binding Path=MaxLength, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}"
Style="{StaticResource ResourceKey=TransparentTextBoxStyle}" />
</Grid>
</DockPanel>
<Popup
x:Name="PART_Popup"
MinWidth="{TemplateBinding ActualWidth}"
MinHeight="25"
MaxHeight="600"
AllowsTransparency="True"
Focusable="False"
HorizontalOffset="0"
IsOpen="{Binding Path=IsDropDownOpen, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}"
PopupAnimation="Slide">
<Border
Padding="2"
Background="{Binding Path=SuggestionBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=editors:AutoCompleteTextBox}, Mode=OneWay}"
BorderBrush="Gray"
BorderThickness="1"
CornerRadius="5">
<Grid Background="{Binding Path=SuggestionBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=editors:AutoCompleteTextBox}, Mode=OneWay}">
<ListBox
x:Name="PART_Selector"
MaxWidth="{Binding Path=MaxPopupWidth, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}"
MaxHeight="{Binding Path=MaxPopupHeight, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}"
Background="{Binding Path=SuggestionBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=editors:AutoCompleteTextBox}, Mode=OneWay}"
BorderThickness="0"
Focusable="False"
ItemContainerStyle="{StaticResource ResourceKey=TextBoxSuggestionItemStyle}"
ItemTemplate="{TemplateBinding ItemTemplate}"
ItemTemplateSelector="{TemplateBinding ItemTemplateSelector}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto" />
<Border Visibility="{Binding Path=IsLoading, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource ResourceKey=BoolToVisConverter}}">
<ContentPresenter ContentSource="LoadingContent" />
</Border>
</Grid>
</Border>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger SourceName="PART_Editor" Property="Text" Value="">
<Setter TargetName="PART_Watermark" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IconPlacement" Value="Left">
<Setter TargetName="PART_Icon" Property="DockPanel.Dock" Value="Left" />
</Trigger>
<Trigger Property="IconPlacement" Value="Right">
<Setter TargetName="PART_Icon" Property="DockPanel.Dock" Value="Right" />
</Trigger>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="editors:AutoCompleteComboBox">
<Setter Property="Focusable" Value="True" />
<Setter Property="SuggestionBackground" Value="White" />
<Setter Property="BorderThickness" Value="0,0,0,1" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="AllowDrop" Value="true" />
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type editors:AutoCompleteComboBox}">
<Grid>
<DockPanel>
<ContentPresenter
x:Name="PART_Icon"
ContentSource="Icon"
Visibility="{TemplateBinding IconVisibility}" />
<Grid>
<TextBlock
x:Name="PART_Watermark"
HorizontalAlignment="Left"
VerticalAlignment="Center"
DockPanel.Dock="Left"
Focusable="False"
Foreground="Gray"
Text="{TemplateBinding Watermark}"
Visibility="Collapsed" />
<DockPanel Margin="3,0">
<Expander x:Name="PART_Expander" DockPanel.Dock="Right" />
<TextBox
x:Name="PART_Editor"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
CharacterCasing="{Binding Path=CharacterCasing, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}"
Focusable="True"
MaxLength="{Binding Path=MaxLength, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}" />
</DockPanel>
</Grid>
</DockPanel>
<Popup
x:Name="PART_Popup"
MinWidth="{TemplateBinding ActualWidth}"
MinHeight="25"
MaxHeight="600"
AllowsTransparency="True"
Focusable="False"
HorizontalOffset="0"
IsOpen="{Binding Path=IsDropDownOpen, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}"
PopupAnimation="Slide">
<Border
Padding="2"
Background="{Binding Path=SuggestionBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=editors:AutoCompleteComboBox}, Mode=OneWay}"
BorderBrush="Gray"
BorderThickness="1"
CornerRadius="5">
<Grid>
<ListBox
x:Name="PART_Selector"
MaxWidth="{Binding Path=MaxPopupWidth, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}"
MaxHeight="{Binding Path=MaxPopupHeight, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}"
Background="{Binding Path=SuggestionBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=editors:AutoCompleteComboBox}, Mode=OneWay}"
BorderThickness="0"
Focusable="False"
ItemContainerStyle="{StaticResource ResourceKey=ComboBoxSuggestionItemStyle}"
ItemTemplate="{TemplateBinding ItemTemplate}"
ItemTemplateSelector="{TemplateBinding ItemTemplateSelector}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto" />
<Border Visibility="{Binding Path=IsLoading, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource ResourceKey=BoolToVisConverter}}">
<ContentPresenter ContentSource="LoadingContent" />
</Border>
</Grid>
</Border>
</Popup>
</Grid>
<!--</Border>-->
<ControlTemplate.Triggers>
<Trigger SourceName="PART_Editor" Property="Text" Value="">
<Setter TargetName="PART_Watermark" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IconPlacement" Value="Left">
<Setter TargetName="PART_Icon" Property="DockPanel.Dock" Value="Left" />
</Trigger>
<Trigger Property="IconPlacement" Value="Right">
<Setter TargetName="PART_Icon" Property="DockPanel.Dock" Value="Right" />
</Trigger>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>