From 2a90e3c590e8c708743af88f6f4b3dc4c4c11c8b Mon Sep 17 00:00:00 2001 From: Abderrahmane Ahmam Date: Sat, 20 May 2023 19:09:58 +0100 Subject: [PATCH 01/14] Implement the AutoSuggestion in TextBox by using TextFieldAssist.AutoSuggestionEnabled & ItemsSource --- MainDemo.Wpf/Domain/FieldsViewModel.cs | 100 ++++++- MainDemo.Wpf/Fields.xaml | 79 ++++++ MaterialDesignThemes.Wpf/TextFieldAssist.cs | 258 ++++++++++++++++-- .../Themes/MaterialDesignTheme.TextBox.xaml | 21 ++ 4 files changed, 431 insertions(+), 27 deletions(-) diff --git a/MainDemo.Wpf/Domain/FieldsViewModel.cs b/MainDemo.Wpf/Domain/FieldsViewModel.cs index 699ca7f65e..a8a8342e00 100644 --- a/MainDemo.Wpf/Domain/FieldsViewModel.cs +++ b/MainDemo.Wpf/Domain/FieldsViewModel.cs @@ -1,6 +1,8 @@ -namespace MaterialDesignDemo.Domain; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Windows.Media; -public class FieldsViewModel : ViewModelBase +namespace MaterialDesignDemo.Domain { private string? _name; private string? _name2; @@ -13,9 +15,16 @@ public class FieldsViewModel : ViewModelBase public string? Name { - get => _name; - set => SetProperty(ref _name, value); - } + private string? _name; + private string? _name2; + private string? _password1 = string.Empty; + private string? _password2 = "pre-filled"; + private string? _password1Validated = "pre-filled"; + private string? _password2Validated = "pre-filled"; + private string? _text1; + private string? _text2; + private string? _autoSuggestionTextBox1 = string.Empty; + private string? _autoSuggestionTextBox2 = string.Empty; public string? Name2 { @@ -71,13 +80,82 @@ public string? Password2Validated public FieldsTestObject TestObject => new() { Name = "Mr. Test" }; - public ICommand SetPassword1FromViewModelCommand { get; } - public ICommand SetPassword2FromViewModelCommand { get; } - public FieldsViewModel() - { - SetPassword1FromViewModelCommand = new AnotherCommandImplementation(_ => Password1 = "Set from ViewModel!"); - SetPassword2FromViewModelCommand = new AnotherCommandImplementation(_ => Password2 = "Set from ViewModel!"); + public string? AutoSuggestionTextBox1 + { + get { return _autoSuggestionTextBox1; } + set { SetProperty(ref _autoSuggestionTextBox1, value); } + } + + + public string? AutoSuggestionTextBox2 + { + get { return _autoSuggestionTextBox2; } + set { SetProperty(ref _autoSuggestionTextBox2, value); } + } + + + private ObservableCollection listSuggestion; + + public ObservableCollection ListSuggestion + { + get { return listSuggestion; } + set { SetProperty(ref listSuggestion, value); } + } + + + private ObservableCollection baseListSuggestion; + + public ObservableCollection BaseListSuggestion + { + get { return baseListSuggestion; } + set { SetProperty(ref baseListSuggestion, value); } + } + + private ObservableCollection> listColors; + + public ObservableCollection> ListColors + { + get { return listColors; } + set { SetProperty(ref listColors, value); } + } + + public FieldsTestObject TestObject => new() { Name = "Mr. Test" }; + + public ICommand AutoSuggestionTextBox1ChangedCommand { get; } + public ICommand AutoSuggestionTextBox2ChangedCommand { get; } + public ICommand SetPassword1FromViewModelCommand { get; } + public ICommand SetPassword2FromViewModelCommand { get; } + + public FieldsViewModel() + { + SetPassword1FromViewModelCommand = new AnotherCommandImplementation(_ => Password1 = "Set from ViewModel!"); + SetPassword2FromViewModelCommand = new AnotherCommandImplementation(_ => Password2 = "Set from ViewModel!"); + BaseListSuggestion = new ObservableCollection() + { + "Burger", "Fries", "Shake", "Lettuce" + }; + ListColors = new ObservableCollection>(GetColors()); + ListSuggestion = BaseListSuggestion; + AutoSuggestionTextBox1ChangedCommand = new AnotherCommandImplementation(_ => + { + ListSuggestion = new ObservableCollection(BaseListSuggestion.Where(s => s.ToLower().Contains(AutoSuggestionTextBox1.ToLower()))); + }); + AutoSuggestionTextBox2ChangedCommand = new AnotherCommandImplementation(_ => + { + ListColors = new ObservableCollection>(GetColors().Where(s => s.Key.StartsWith(AutoSuggestionTextBox2))); + }); + } + + private IEnumerable> GetColors() + { + return typeof(Colors) + .GetProperties() + .Where(prop => + typeof(Color).IsAssignableFrom(prop.PropertyType)) + .Select(prop => + new KeyValuePair(prop.Name, new SolidColorBrush((Color)prop.GetValue(null)))); + } } } diff --git a/MainDemo.Wpf/Fields.xaml b/MainDemo.Wpf/Fields.xaml index 6ee2767681..857a48eb71 100644 --- a/MainDemo.Wpf/Fields.xaml +++ b/MainDemo.Wpf/Fields.xaml @@ -4,6 +4,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:domain="clr-namespace:MaterialDesignDemo.Domain" xmlns:domain1="clr-namespace:MaterialDesignDemo.Domain" + xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:smtx="clr-namespace:ShowMeTheXAML;assembly=ShowMeTheXAML" @@ -757,5 +758,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MaterialDesignThemes.Wpf/TextFieldAssist.cs b/MaterialDesignThemes.Wpf/TextFieldAssist.cs index e40bf7fb94..777c6177d2 100644 --- a/MaterialDesignThemes.Wpf/TextFieldAssist.cs +++ b/MaterialDesignThemes.Wpf/TextFieldAssist.cs @@ -1,4 +1,9 @@ +using System.Collections; +using System.Collections.Generic; +using System.IO.Packaging; +using System.Windows.Controls; using System.Windows.Documents; +using System.Windows.Input; using System.Windows.Media; namespace MaterialDesignThemes.Wpf; @@ -11,11 +16,19 @@ public static class TextFieldAssist /// /// The text box view margin property /// - public static readonly DependencyProperty TextBoxViewMarginProperty = DependencyProperty.RegisterAttached( - "TextBoxViewMargin", - typeof(Thickness), - typeof(TextFieldAssist), - new FrameworkPropertyMetadata(new Thickness(double.NegativeInfinity), FrameworkPropertyMetadataOptions.Inherits, TextBoxViewMarginPropertyChangedCallback)); + public static class TextFieldAssist + { + private const string AutoSuggestionListBoxName = "AutoSuggestionListBox"; + private const string AutoSuggestionPart = "PART_AutoSuggestion"; + + /// + /// The text box view margin property + /// + public static readonly DependencyProperty TextBoxViewMarginProperty = DependencyProperty.RegisterAttached( + "TextBoxViewMargin", + typeof(Thickness), + typeof(TextFieldAssist), + new FrameworkPropertyMetadata(new Thickness(double.NegativeInfinity), FrameworkPropertyMetadataOptions.Inherits, TextBoxViewMarginPropertyChangedCallback)); /// /// Sets the text box view margin. @@ -284,12 +297,96 @@ private static void PasswordBoxOnPasswordChanged(object sender, RoutedEventArgs SetPasswordBoxCharacterCount(passwordBox, passwordBox.SecurePassword.Length); } - internal static readonly DependencyProperty PasswordBoxCharacterCountProperty = DependencyProperty.RegisterAttached( - "PasswordBoxCharacterCount", typeof(int), typeof(TextFieldAssist), new PropertyMetadata(default(int))); - internal static void SetPasswordBoxCharacterCount(DependencyObject element, int value) => element.SetValue(PasswordBoxCharacterCountProperty, value); - internal static int GetPasswordBoxCharacterCount(DependencyObject element) => (int) element.GetValue(PasswordBoxCharacterCountProperty); + internal static readonly DependencyProperty PasswordBoxCharacterCountProperty = DependencyProperty.RegisterAttached( + "PasswordBoxCharacterCount", typeof(int), typeof(TextFieldAssist), new PropertyMetadata(default(int))); + internal static void SetPasswordBoxCharacterCount(DependencyObject element, int value) => element.SetValue(PasswordBoxCharacterCountProperty, value); + internal static int GetPasswordBoxCharacterCount(DependencyObject element) => (int)element.GetValue(PasswordBoxCharacterCountProperty); + + /// + /// List AutoSuggestion + /// + public static readonly DependencyProperty AutoSuggestionItemsSourceProperty = DependencyProperty.RegisterAttached( + "AutoSuggestionItemsSource", typeof(object), typeof(TextFieldAssist), new PropertyMetadata(null, AutoSuggestionItemsSourceChanged)); + + private static void AutoSuggestionItemsSourceChanged(DependencyObject element, DependencyPropertyChangedEventArgs e) + { + if (element is TextBox textBox && textBox.Template.FindName(AutoSuggestionPart, textBox) is Popup popup && e.NewValue is ICollection items) + { + if ((textBox.Text.Length == 0 || items.Count == 0) && popup.IsOpen) + popup.IsOpen = false; + else if (textBox.Text.Length > 0 && !popup.IsOpen && textBox.IsFocused && items.Count > 0) + popup.IsOpen = true; + + } + } + public static void SetAutoSuggestionItemsSource(DependencyObject element, object value) + => element.SetValue(AutoSuggestionItemsSourceProperty, value); + + public static object GetAutoSuggestionItemsSource(DependencyObject element) + => element.GetValue(AutoSuggestionItemsSourceProperty); + + /// + /// Controls the AutoSuggestion for a TextBox + /// + public static readonly DependencyProperty AutoSuggestionEnabledProperty = DependencyProperty.RegisterAttached( + "AutoSuggestionEnabled", typeof(bool), typeof(TextFieldAssist), new PropertyMetadata(default(bool), AutoSuggestionEnabledChanged)); + + public static void SetAutoSuggestionEnabled(DependencyObject element, bool value) + => element.SetValue(AutoSuggestionEnabledProperty, value); + + public static bool GetAutoSuggestionEnabled(DependencyObject element) + => (bool)element.GetValue(AutoSuggestionEnabledProperty); + + /// + /// Controls the AutoSuggestion item template + /// + public static readonly DependencyProperty AutoSuggestionItemTemplateProperty = DependencyProperty.RegisterAttached( + "AutoSuggestionItemTemplate", typeof(DataTemplate), typeof(TextFieldAssist), new PropertyMetadata(null)); + + public static void SetAutoSuggestionItemTemplate(DependencyObject element, DataTemplate value) + => element.SetValue(AutoSuggestionItemTemplateProperty, value); + + public static DataTemplate GetAutoSuggestionItemTemplate(DependencyObject element) + => (DataTemplate)element.GetValue(AutoSuggestionItemTemplateProperty); + - #region Methods + /// + /// Controls the AutoSuggestion Item template selector + /// + public static readonly DependencyProperty AutoSuggestionItemTemplateSelectorProperty = DependencyProperty.RegisterAttached( + "AutoSuggestionItemTemplateSelector", typeof(DataTemplate), typeof(TextFieldAssist), new PropertyMetadata(null)); + + public static void SetAutoSuggestionItemTemplateSelector(DependencyObject element, DataTemplate value) + => element.SetValue(AutoSuggestionItemTemplateSelectorProperty, value); + + public static DataTemplate GetAutoSuggestionItemTemplateSelector(DependencyObject element) + => (DataTemplate)element.GetValue(AutoSuggestionItemTemplateSelectorProperty); + + /// + /// Controls the AutoSuggestion value member + /// + public static readonly DependencyProperty AutoSuggestionValueMemberProperty = DependencyProperty.RegisterAttached( + "AutoSuggestionValueMember", typeof(string), typeof(TextFieldAssist), new PropertyMetadata(default(string))); + + public static void SetAutoSuggestionValueMember(DependencyObject element, string value) + => element.SetValue(AutoSuggestionValueMemberProperty, value); + + public static string GetAutoSuggestionValueMember(DependencyObject element) + => (string)element.GetValue(AutoSuggestionValueMemberProperty); + + /// + /// Controls the AutoSuggestion display member + /// + public static readonly DependencyProperty AutoSuggestionDisplayMemberProperty = DependencyProperty.RegisterAttached( + "AutoSuggestionDisplayMember", typeof(string), typeof(TextFieldAssist), new PropertyMetadata(default(string))); + + public static void SetAutoSuggestionDisplayMember(DependencyObject element, string value) + => element.SetValue(AutoSuggestionDisplayMemberProperty, value); + + public static string GetAutoSuggestionDisplayMember(DependencyObject element) + => (string)element.GetValue(AutoSuggestionDisplayMemberProperty); + + #region Methods private static void IncludeSpellingSuggestionsChanged(DependencyObject element, DependencyPropertyChangedEventArgs e) { @@ -446,17 +543,146 @@ private static void TextBoxViewMarginPropertyChangedCallback( { return; } + /// + /// The AutoSuggestion enabled property changed callback. + /// + /// The dependency object. + /// The dependency property changed event args. + private static void AutoSuggestionEnabledChanged(DependencyObject element, DependencyPropertyChangedEventArgs e) + { + if (element is TextBox textBox) + { + if ((bool)e.NewValue) + { + textBox.LostFocus += AutoSuggestionTextBox_LostFocus; + textBox.PreviewKeyDown += AutoSuggestionTextBox_KeyDown; + textBox.Loaded += AutoSuggestionTextBox_Loaded; + } + else + { + textBox.LostFocus -= AutoSuggestionTextBox_LostFocus; + textBox.PreviewKeyDown -= AutoSuggestionTextBox_KeyDown; + textBox.Loaded -= AutoSuggestionTextBox_Loaded; + if (textBox.Template != null && textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox) + { + listBox.PreviewMouseDown -= AutoSuggestionListBox_PreviewMouseDown; + } + } + } + } + + private static void AutoSuggestionTextBox_Loaded(object sender, RoutedEventArgs e) + { + if (sender is TextBox textBox && textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox) + { + listBox.PreviewMouseDown += AutoSuggestionListBox_PreviewMouseDown; + } + } + + private static void AutoSuggestionListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) + { + if (sender is ListBox listBox && listBox.TemplatedParent is TextBox textBox) + { + if ((e.OriginalSource as FrameworkElement)?.DataContext == null) + return; + if (!listBox.Items.Contains(((FrameworkElement)e.OriginalSource)?.DataContext)) + return; + listBox.SelectedItem = ((FrameworkElement)e.OriginalSource)?.DataContext; + CommitValueSelection(textBox); + } + } + + private static void AutoSuggestionTextBox_KeyDown(object sender, KeyEventArgs e) + { + if (sender is TextBox textBox) + { + switch (e.Key) + { + case Key.Down: + IncrementSelection(textBox); + break; + case Key.Up: + DecrementSelection(textBox); + break; + case Key.Enter: + CommitValueSelection(textBox); + break; + case Key.Escape: + CloseAutoSuggestionPopUp(textBox); + break; + case Key.Tab: + CommitValueSelection(textBox); + break; + default: + return; + } + } + e.Handled = true; + } + + private static void AutoSuggestionTextBox_LostFocus(object sender, RoutedEventArgs e) + { + if (sender is TextBox textBox) + { + CloseAutoSuggestionPopUp(textBox); + } + } - if (box.IsLoaded) + /// + /// Close AutoSuggestion Popup + /// + /// TextBox used + private static void CloseAutoSuggestionPopUp(TextBox textBox) { - ApplyTextBoxViewMargin(box, (Thickness)dependencyPropertyChangedEventArgs.NewValue); + if (textBox.Template.FindName(AutoSuggestionPart, textBox) is Popup popup && popup.IsOpen) + popup.IsOpen = false; + } + + /// + /// Validate value + /// + /// + private static void CommitValueSelection(TextBox textBox) + { + if (textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox) + { + textBox.Text = listBox.SelectedValue.ToString(); + textBox.CaretIndex = textBox.Text.Length; + CloseAutoSuggestionPopUp(textBox); + } } - box.Loaded += (sender, args) => + /// + /// The Keyup navigation for the ListBox. + /// + /// The current TextBox. + private static void DecrementSelection(TextBox textBox) + { + if (textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox) + { + if (listBox.SelectedIndex == 0) + listBox.SelectedIndex = listBox.Items.Count - 1; + else + listBox.SelectedIndex -= 1; + listBox.ScrollIntoView(listBox.SelectedItem); + } + } + /// + /// The Keydown navigation for the ListBox + /// + /// The current TextBox. + private static void IncrementSelection(TextBox textBox) { - var textBox = (Control)sender; - ApplyTextBoxViewMargin(textBox, GetTextBoxViewMargin(textBox)); - }; + if (textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox) + { + if (listBox.SelectedIndex == listBox.Items.Count - 1) + listBox.SelectedIndex = 0; + else + listBox.SelectedIndex += 1; + listBox.ScrollIntoView(listBox.SelectedItem); + } + } + #endregion } #endregion diff --git a/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TextBox.xaml b/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TextBox.xaml index fd63482248..b225ccfde0 100644 --- a/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TextBox.xaml +++ b/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TextBox.xaml @@ -182,6 +182,10 @@ + + + + + + + Date: Sun, 21 May 2023 11:32:14 +0100 Subject: [PATCH 02/14] Fixing the code warnings about the null values Add -1 possibility in DecrementSelection Add Enabled check in the ItemsSource callback method in TextFieldAssist --- MainDemo.Wpf/Domain/FieldsViewModel.cs | 16 ++++---- MaterialDesignThemes.Wpf/TextFieldAssist.cs | 45 ++++++++++++--------- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/MainDemo.Wpf/Domain/FieldsViewModel.cs b/MainDemo.Wpf/Domain/FieldsViewModel.cs index a8a8342e00..f49a6ff484 100644 --- a/MainDemo.Wpf/Domain/FieldsViewModel.cs +++ b/MainDemo.Wpf/Domain/FieldsViewModel.cs @@ -23,8 +23,8 @@ public string? Name private string? _password2Validated = "pre-filled"; private string? _text1; private string? _text2; - private string? _autoSuggestionTextBox1 = string.Empty; - private string? _autoSuggestionTextBox2 = string.Empty; + private string _autoSuggestionTextBox1 = string.Empty; + private string _autoSuggestionTextBox2 = string.Empty; public string? Name2 { @@ -81,21 +81,21 @@ public string? Password2Validated public FieldsTestObject TestObject => new() { Name = "Mr. Test" }; - public string? AutoSuggestionTextBox1 + public string AutoSuggestionTextBox1 { get { return _autoSuggestionTextBox1; } set { SetProperty(ref _autoSuggestionTextBox1, value); } - } + } - public string? AutoSuggestionTextBox2 + public string AutoSuggestionTextBox2 { get { return _autoSuggestionTextBox2; } set { SetProperty(ref _autoSuggestionTextBox2, value); } } - private ObservableCollection listSuggestion; + private ObservableCollection listSuggestion = new ObservableCollection(); public ObservableCollection ListSuggestion { @@ -104,7 +104,7 @@ public ObservableCollection ListSuggestion } - private ObservableCollection baseListSuggestion; + private ObservableCollection baseListSuggestion = new ObservableCollection(); public ObservableCollection BaseListSuggestion { @@ -112,7 +112,7 @@ public ObservableCollection BaseListSuggestion set { SetProperty(ref baseListSuggestion, value); } } - private ObservableCollection> listColors; + private ObservableCollection> listColors = new ObservableCollection>(); public ObservableCollection> ListColors { diff --git a/MaterialDesignThemes.Wpf/TextFieldAssist.cs b/MaterialDesignThemes.Wpf/TextFieldAssist.cs index 777c6177d2..41421cb5a2 100644 --- a/MaterialDesignThemes.Wpf/TextFieldAssist.cs +++ b/MaterialDesignThemes.Wpf/TextFieldAssist.cs @@ -308,17 +308,6 @@ private static void PasswordBoxOnPasswordChanged(object sender, RoutedEventArgs public static readonly DependencyProperty AutoSuggestionItemsSourceProperty = DependencyProperty.RegisterAttached( "AutoSuggestionItemsSource", typeof(object), typeof(TextFieldAssist), new PropertyMetadata(null, AutoSuggestionItemsSourceChanged)); - private static void AutoSuggestionItemsSourceChanged(DependencyObject element, DependencyPropertyChangedEventArgs e) - { - if (element is TextBox textBox && textBox.Template.FindName(AutoSuggestionPart, textBox) is Popup popup && e.NewValue is ICollection items) - { - if ((textBox.Text.Length == 0 || items.Count == 0) && popup.IsOpen) - popup.IsOpen = false; - else if (textBox.Text.Length > 0 && !popup.IsOpen && textBox.IsFocused && items.Count > 0) - popup.IsOpen = true; - - } - } public static void SetAutoSuggestionItemsSource(DependencyObject element, object value) => element.SetValue(AutoSuggestionItemsSourceProperty, value); @@ -571,6 +560,23 @@ private static void AutoSuggestionEnabledChanged(DependencyObject element, Depen } } + /// + /// The AutoSuggestion ItemsSource property changed callback. + /// + /// The dependency object. + /// The dependency property changed event args. + private static void AutoSuggestionItemsSourceChanged(DependencyObject element, DependencyPropertyChangedEventArgs e) + { + if (element is TextBox textBox && GetAutoSuggestionEnabled(textBox) && textBox.Template.FindName(AutoSuggestionPart, textBox) is Popup popup && e.NewValue is ICollection items) + { + if ((textBox.Text.Length == 0 || items.Count == 0) && popup.IsOpen) + popup.IsOpen = false; + else if (textBox.Text.Length > 0 && !popup.IsOpen && textBox.IsFocused && items.Count > 0) + popup.IsOpen = true; + + } + } + private static void AutoSuggestionTextBox_Loaded(object sender, RoutedEventArgs e) { if (sender is TextBox textBox && textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox) @@ -581,13 +587,11 @@ private static void AutoSuggestionTextBox_Loaded(object sender, RoutedEventArgs private static void AutoSuggestionListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) { - if (sender is ListBox listBox && listBox.TemplatedParent is TextBox textBox) + if (sender is ListBox listBox && listBox.TemplatedParent is TextBox textBox && e.OriginalSource is FrameworkElement element) { - if ((e.OriginalSource as FrameworkElement)?.DataContext == null) - return; - if (!listBox.Items.Contains(((FrameworkElement)e.OriginalSource)?.DataContext)) + if (!listBox.Items.Contains(element.DataContext)) return; - listBox.SelectedItem = ((FrameworkElement)e.OriginalSource)?.DataContext; + listBox.SelectedItem = element.DataContext; CommitValueSelection(textBox); } } @@ -639,15 +643,16 @@ private static void CloseAutoSuggestionPopUp(TextBox textBox) } /// - /// Validate value + /// Commit the selected value /// /// private static void CommitValueSelection(TextBox textBox) { - if (textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox) + if (textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox && listBox.SelectedValue != null) { textBox.Text = listBox.SelectedValue.ToString(); - textBox.CaretIndex = textBox.Text.Length; + if (textBox.Text != null) + textBox.CaretIndex = textBox.Text.Length; CloseAutoSuggestionPopUp(textBox); } } @@ -660,7 +665,7 @@ private static void DecrementSelection(TextBox textBox) { if (textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox) { - if (listBox.SelectedIndex == 0) + if (listBox.SelectedIndex == 0 || listBox.SelectedIndex == -1) listBox.SelectedIndex = listBox.Items.Count - 1; else listBox.SelectedIndex -= 1; From 1e7552da4ce207b0523c319231f8c0f13df383f7 Mon Sep 17 00:00:00 2001 From: Abderrahmane Ahmam Date: Mon, 22 May 2023 00:25:18 +0100 Subject: [PATCH 03/14] Fix warnings in the FieldsViewModel 0 Warning(s) --- MainDemo.Wpf/Domain/FieldsViewModel.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/MainDemo.Wpf/Domain/FieldsViewModel.cs b/MainDemo.Wpf/Domain/FieldsViewModel.cs index f49a6ff484..fcfdb662b2 100644 --- a/MainDemo.Wpf/Domain/FieldsViewModel.cs +++ b/MainDemo.Wpf/Domain/FieldsViewModel.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using System.Windows.Media; namespace MaterialDesignDemo.Domain @@ -154,7 +155,14 @@ private IEnumerable> GetColors() .Where(prop => typeof(Color).IsAssignableFrom(prop.PropertyType)) .Select(prop => - new KeyValuePair(prop.Name, new SolidColorBrush((Color)prop.GetValue(null)))); + new KeyValuePair(prop.Name, GenerateColorBrush(prop.GetValue(null)))); + } + + private SolidColorBrush GenerateColorBrush(object? prop) + { + if (prop is Color color) + return new SolidColorBrush(color); + return new SolidColorBrush(Colors.White); } } } From 0f1bb1cd0c16bd802b36652ba3353f4d704eab01 Mon Sep 17 00:00:00 2001 From: Abderrahmane Ahmam Date: Mon, 22 May 2023 09:59:15 +0100 Subject: [PATCH 04/14] Add underscore prefix to the private properties --- MainDemo.Wpf/Domain/FieldsViewModel.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/MainDemo.Wpf/Domain/FieldsViewModel.cs b/MainDemo.Wpf/Domain/FieldsViewModel.cs index fcfdb662b2..769e5e53f3 100644 --- a/MainDemo.Wpf/Domain/FieldsViewModel.cs +++ b/MainDemo.Wpf/Domain/FieldsViewModel.cs @@ -26,6 +26,9 @@ public string? Name private string? _text2; private string _autoSuggestionTextBox1 = string.Empty; private string _autoSuggestionTextBox2 = string.Empty; + private ObservableCollection _listSuggestion = new ObservableCollection(); + private ObservableCollection _baseListSuggestion = new ObservableCollection(); + private ObservableCollection> _listColors = new ObservableCollection>(); public string? Name2 { @@ -96,29 +99,26 @@ public string AutoSuggestionTextBox2 } - private ObservableCollection listSuggestion = new ObservableCollection(); public ObservableCollection ListSuggestion { - get { return listSuggestion; } - set { SetProperty(ref listSuggestion, value); } + get { return _listSuggestion; } + set { SetProperty(ref _listSuggestion, value); } } - private ObservableCollection baseListSuggestion = new ObservableCollection(); public ObservableCollection BaseListSuggestion { - get { return baseListSuggestion; } - set { SetProperty(ref baseListSuggestion, value); } + get { return _baseListSuggestion; } + set { SetProperty(ref _baseListSuggestion, value); } } - private ObservableCollection> listColors = new ObservableCollection>(); public ObservableCollection> ListColors { - get { return listColors; } - set { SetProperty(ref listColors, value); } + get { return _listColors; } + set { SetProperty(ref _listColors, value); } } public FieldsTestObject TestObject => new() { Name = "Mr. Test" }; From 6d41235072f0077b8986252ef06c5c01ef295b49 Mon Sep 17 00:00:00 2001 From: Abderrahmane Ahmam Date: Sat, 27 May 2023 18:23:02 +0100 Subject: [PATCH 05/14] Reset changes of the AutoSuggestion in the TextFieldAssist --- MainDemo.Wpf/Domain/FieldsViewModel.cs | 80 ------ MainDemo.Wpf/Fields.xaml | 79 ------ MaterialDesignThemes.Wpf/TextFieldAssist.cs | 237 +----------------- .../Themes/MaterialDesignTheme.TextBox.xaml | 119 +++------ 4 files changed, 31 insertions(+), 484 deletions(-) diff --git a/MainDemo.Wpf/Domain/FieldsViewModel.cs b/MainDemo.Wpf/Domain/FieldsViewModel.cs index 769e5e53f3..bf2d5dad0b 100644 --- a/MainDemo.Wpf/Domain/FieldsViewModel.cs +++ b/MainDemo.Wpf/Domain/FieldsViewModel.cs @@ -1,8 +1,3 @@ -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Windows.Media; - namespace MaterialDesignDemo.Domain { private string? _name; @@ -24,11 +19,6 @@ public string? Name private string? _password2Validated = "pre-filled"; private string? _text1; private string? _text2; - private string _autoSuggestionTextBox1 = string.Empty; - private string _autoSuggestionTextBox2 = string.Empty; - private ObservableCollection _listSuggestion = new ObservableCollection(); - private ObservableCollection _baseListSuggestion = new ObservableCollection(); - private ObservableCollection> _listColors = new ObservableCollection>(); public string? Name2 { @@ -84,47 +74,8 @@ public string? Password2Validated public FieldsTestObject TestObject => new() { Name = "Mr. Test" }; - - public string AutoSuggestionTextBox1 - { - get { return _autoSuggestionTextBox1; } - set { SetProperty(ref _autoSuggestionTextBox1, value); } - } - - - public string AutoSuggestionTextBox2 - { - get { return _autoSuggestionTextBox2; } - set { SetProperty(ref _autoSuggestionTextBox2, value); } - } - - - - public ObservableCollection ListSuggestion - { - get { return _listSuggestion; } - set { SetProperty(ref _listSuggestion, value); } - } - - - - public ObservableCollection BaseListSuggestion - { - get { return _baseListSuggestion; } - set { SetProperty(ref _baseListSuggestion, value); } - } - - - public ObservableCollection> ListColors - { - get { return _listColors; } - set { SetProperty(ref _listColors, value); } - } - public FieldsTestObject TestObject => new() { Name = "Mr. Test" }; - public ICommand AutoSuggestionTextBox1ChangedCommand { get; } - public ICommand AutoSuggestionTextBox2ChangedCommand { get; } public ICommand SetPassword1FromViewModelCommand { get; } public ICommand SetPassword2FromViewModelCommand { get; } @@ -132,37 +83,6 @@ public FieldsViewModel() { SetPassword1FromViewModelCommand = new AnotherCommandImplementation(_ => Password1 = "Set from ViewModel!"); SetPassword2FromViewModelCommand = new AnotherCommandImplementation(_ => Password2 = "Set from ViewModel!"); - BaseListSuggestion = new ObservableCollection() - { - "Burger", "Fries", "Shake", "Lettuce" - }; - ListColors = new ObservableCollection>(GetColors()); - ListSuggestion = BaseListSuggestion; - AutoSuggestionTextBox1ChangedCommand = new AnotherCommandImplementation(_ => - { - ListSuggestion = new ObservableCollection(BaseListSuggestion.Where(s => s.ToLower().Contains(AutoSuggestionTextBox1.ToLower()))); - }); - AutoSuggestionTextBox2ChangedCommand = new AnotherCommandImplementation(_ => - { - ListColors = new ObservableCollection>(GetColors().Where(s => s.Key.StartsWith(AutoSuggestionTextBox2))); - }); - } - - private IEnumerable> GetColors() - { - return typeof(Colors) - .GetProperties() - .Where(prop => - typeof(Color).IsAssignableFrom(prop.PropertyType)) - .Select(prop => - new KeyValuePair(prop.Name, GenerateColorBrush(prop.GetValue(null)))); - } - - private SolidColorBrush GenerateColorBrush(object? prop) - { - if (prop is Color color) - return new SolidColorBrush(color); - return new SolidColorBrush(Colors.White); } } } diff --git a/MainDemo.Wpf/Fields.xaml b/MainDemo.Wpf/Fields.xaml index 857a48eb71..6ee2767681 100644 --- a/MainDemo.Wpf/Fields.xaml +++ b/MainDemo.Wpf/Fields.xaml @@ -4,7 +4,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:domain="clr-namespace:MaterialDesignDemo.Domain" xmlns:domain1="clr-namespace:MaterialDesignDemo.Domain" - xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:smtx="clr-namespace:ShowMeTheXAML;assembly=ShowMeTheXAML" @@ -758,83 +757,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MaterialDesignThemes.Wpf/TextFieldAssist.cs b/MaterialDesignThemes.Wpf/TextFieldAssist.cs index 41421cb5a2..63bc8d7dd0 100644 --- a/MaterialDesignThemes.Wpf/TextFieldAssist.cs +++ b/MaterialDesignThemes.Wpf/TextFieldAssist.cs @@ -1,9 +1,4 @@ -using System.Collections; -using System.Collections.Generic; -using System.IO.Packaging; -using System.Windows.Controls; using System.Windows.Documents; -using System.Windows.Input; using System.Windows.Media; namespace MaterialDesignThemes.Wpf; @@ -18,9 +13,6 @@ public static class TextFieldAssist /// public static class TextFieldAssist { - private const string AutoSuggestionListBoxName = "AutoSuggestionListBox"; - private const string AutoSuggestionPart = "PART_AutoSuggestion"; - /// /// The text box view margin property /// @@ -300,80 +292,7 @@ private static void PasswordBoxOnPasswordChanged(object sender, RoutedEventArgs internal static readonly DependencyProperty PasswordBoxCharacterCountProperty = DependencyProperty.RegisterAttached( "PasswordBoxCharacterCount", typeof(int), typeof(TextFieldAssist), new PropertyMetadata(default(int))); internal static void SetPasswordBoxCharacterCount(DependencyObject element, int value) => element.SetValue(PasswordBoxCharacterCountProperty, value); - internal static int GetPasswordBoxCharacterCount(DependencyObject element) => (int)element.GetValue(PasswordBoxCharacterCountProperty); - - /// - /// List AutoSuggestion - /// - public static readonly DependencyProperty AutoSuggestionItemsSourceProperty = DependencyProperty.RegisterAttached( - "AutoSuggestionItemsSource", typeof(object), typeof(TextFieldAssist), new PropertyMetadata(null, AutoSuggestionItemsSourceChanged)); - - public static void SetAutoSuggestionItemsSource(DependencyObject element, object value) - => element.SetValue(AutoSuggestionItemsSourceProperty, value); - - public static object GetAutoSuggestionItemsSource(DependencyObject element) - => element.GetValue(AutoSuggestionItemsSourceProperty); - - /// - /// Controls the AutoSuggestion for a TextBox - /// - public static readonly DependencyProperty AutoSuggestionEnabledProperty = DependencyProperty.RegisterAttached( - "AutoSuggestionEnabled", typeof(bool), typeof(TextFieldAssist), new PropertyMetadata(default(bool), AutoSuggestionEnabledChanged)); - - public static void SetAutoSuggestionEnabled(DependencyObject element, bool value) - => element.SetValue(AutoSuggestionEnabledProperty, value); - - public static bool GetAutoSuggestionEnabled(DependencyObject element) - => (bool)element.GetValue(AutoSuggestionEnabledProperty); - - /// - /// Controls the AutoSuggestion item template - /// - public static readonly DependencyProperty AutoSuggestionItemTemplateProperty = DependencyProperty.RegisterAttached( - "AutoSuggestionItemTemplate", typeof(DataTemplate), typeof(TextFieldAssist), new PropertyMetadata(null)); - - public static void SetAutoSuggestionItemTemplate(DependencyObject element, DataTemplate value) - => element.SetValue(AutoSuggestionItemTemplateProperty, value); - - public static DataTemplate GetAutoSuggestionItemTemplate(DependencyObject element) - => (DataTemplate)element.GetValue(AutoSuggestionItemTemplateProperty); - - - /// - /// Controls the AutoSuggestion Item template selector - /// - public static readonly DependencyProperty AutoSuggestionItemTemplateSelectorProperty = DependencyProperty.RegisterAttached( - "AutoSuggestionItemTemplateSelector", typeof(DataTemplate), typeof(TextFieldAssist), new PropertyMetadata(null)); - - public static void SetAutoSuggestionItemTemplateSelector(DependencyObject element, DataTemplate value) - => element.SetValue(AutoSuggestionItemTemplateSelectorProperty, value); - - public static DataTemplate GetAutoSuggestionItemTemplateSelector(DependencyObject element) - => (DataTemplate)element.GetValue(AutoSuggestionItemTemplateSelectorProperty); - - /// - /// Controls the AutoSuggestion value member - /// - public static readonly DependencyProperty AutoSuggestionValueMemberProperty = DependencyProperty.RegisterAttached( - "AutoSuggestionValueMember", typeof(string), typeof(TextFieldAssist), new PropertyMetadata(default(string))); - - public static void SetAutoSuggestionValueMember(DependencyObject element, string value) - => element.SetValue(AutoSuggestionValueMemberProperty, value); - - public static string GetAutoSuggestionValueMember(DependencyObject element) - => (string)element.GetValue(AutoSuggestionValueMemberProperty); - - /// - /// Controls the AutoSuggestion display member - /// - public static readonly DependencyProperty AutoSuggestionDisplayMemberProperty = DependencyProperty.RegisterAttached( - "AutoSuggestionDisplayMember", typeof(string), typeof(TextFieldAssist), new PropertyMetadata(default(string))); - - public static void SetAutoSuggestionDisplayMember(DependencyObject element, string value) - => element.SetValue(AutoSuggestionDisplayMemberProperty, value); - - public static string GetAutoSuggestionDisplayMember(DependencyObject element) - => (string)element.GetValue(AutoSuggestionDisplayMemberProperty); + internal static int GetPasswordBoxCharacterCount(DependencyObject element) => (int) element.GetValue(PasswordBoxCharacterCountProperty); #region Methods @@ -532,161 +451,7 @@ private static void TextBoxViewMarginPropertyChangedCallback( { return; } - /// - /// The AutoSuggestion enabled property changed callback. - /// - /// The dependency object. - /// The dependency property changed event args. - private static void AutoSuggestionEnabledChanged(DependencyObject element, DependencyPropertyChangedEventArgs e) - { - if (element is TextBox textBox) - { - if ((bool)e.NewValue) - { - textBox.LostFocus += AutoSuggestionTextBox_LostFocus; - textBox.PreviewKeyDown += AutoSuggestionTextBox_KeyDown; - textBox.Loaded += AutoSuggestionTextBox_Loaded; - } - else - { - textBox.LostFocus -= AutoSuggestionTextBox_LostFocus; - textBox.PreviewKeyDown -= AutoSuggestionTextBox_KeyDown; - textBox.Loaded -= AutoSuggestionTextBox_Loaded; - if (textBox.Template != null && textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox) - { - listBox.PreviewMouseDown -= AutoSuggestionListBox_PreviewMouseDown; - } - } - } - } - /// - /// The AutoSuggestion ItemsSource property changed callback. - /// - /// The dependency object. - /// The dependency property changed event args. - private static void AutoSuggestionItemsSourceChanged(DependencyObject element, DependencyPropertyChangedEventArgs e) - { - if (element is TextBox textBox && GetAutoSuggestionEnabled(textBox) && textBox.Template.FindName(AutoSuggestionPart, textBox) is Popup popup && e.NewValue is ICollection items) - { - if ((textBox.Text.Length == 0 || items.Count == 0) && popup.IsOpen) - popup.IsOpen = false; - else if (textBox.Text.Length > 0 && !popup.IsOpen && textBox.IsFocused && items.Count > 0) - popup.IsOpen = true; - - } - } - - private static void AutoSuggestionTextBox_Loaded(object sender, RoutedEventArgs e) - { - if (sender is TextBox textBox && textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox) - { - listBox.PreviewMouseDown += AutoSuggestionListBox_PreviewMouseDown; - } - } - - private static void AutoSuggestionListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) - { - if (sender is ListBox listBox && listBox.TemplatedParent is TextBox textBox && e.OriginalSource is FrameworkElement element) - { - if (!listBox.Items.Contains(element.DataContext)) - return; - listBox.SelectedItem = element.DataContext; - CommitValueSelection(textBox); - } - } - - private static void AutoSuggestionTextBox_KeyDown(object sender, KeyEventArgs e) - { - if (sender is TextBox textBox) - { - switch (e.Key) - { - case Key.Down: - IncrementSelection(textBox); - break; - case Key.Up: - DecrementSelection(textBox); - break; - case Key.Enter: - CommitValueSelection(textBox); - break; - case Key.Escape: - CloseAutoSuggestionPopUp(textBox); - break; - case Key.Tab: - CommitValueSelection(textBox); - break; - default: - return; - } - } - e.Handled = true; - } - - private static void AutoSuggestionTextBox_LostFocus(object sender, RoutedEventArgs e) - { - if (sender is TextBox textBox) - { - CloseAutoSuggestionPopUp(textBox); - } - } - - /// - /// Close AutoSuggestion Popup - /// - /// TextBox used - private static void CloseAutoSuggestionPopUp(TextBox textBox) - { - if (textBox.Template.FindName(AutoSuggestionPart, textBox) is Popup popup && popup.IsOpen) - popup.IsOpen = false; - } - - /// - /// Commit the selected value - /// - /// - private static void CommitValueSelection(TextBox textBox) - { - if (textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox && listBox.SelectedValue != null) - { - textBox.Text = listBox.SelectedValue.ToString(); - if (textBox.Text != null) - textBox.CaretIndex = textBox.Text.Length; - CloseAutoSuggestionPopUp(textBox); - } - } - - /// - /// The Keyup navigation for the ListBox. - /// - /// The current TextBox. - private static void DecrementSelection(TextBox textBox) - { - if (textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox) - { - if (listBox.SelectedIndex == 0 || listBox.SelectedIndex == -1) - listBox.SelectedIndex = listBox.Items.Count - 1; - else - listBox.SelectedIndex -= 1; - listBox.ScrollIntoView(listBox.SelectedItem); - } - } - /// - /// The Keydown navigation for the ListBox - /// - /// The current TextBox. - private static void IncrementSelection(TextBox textBox) - { - if (textBox.Template.FindName(AutoSuggestionListBoxName, textBox) is ListBox listBox) - { - if (listBox.SelectedIndex == listBox.Items.Count - 1) - listBox.SelectedIndex = 0; - else - listBox.SelectedIndex += 1; - listBox.ScrollIntoView(listBox.SelectedItem); - } - } #endregion } diff --git a/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TextBox.xaml b/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TextBox.xaml index b225ccfde0..9728528298 100644 --- a/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TextBox.xaml +++ b/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TextBox.xaml @@ -19,50 +19,18 @@ - - - - - - - - - - - - + + + + + + + + + + + + @@ -75,10 +43,8 @@ - + @@ -182,10 +148,6 @@ - - - - + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch"> @@ -225,10 +187,10 @@ + UseFloating="{TemplateBinding wpf:HintAssist.IsFloating}" + VerticalAlignment="Stretch" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"> - + - - - - + @@ -365,7 +306,7 @@ - + @@ -375,7 +316,7 @@ - + @@ -447,7 +388,7 @@ - + @@ -642,18 +583,18 @@ From d509195cd7e99ba5e96fa9b9dc7bab2b5b9e60bd Mon Sep 17 00:00:00 2001 From: Abderrahmane Ahmam Date: Mon, 19 Jun 2023 09:05:26 +0100 Subject: [PATCH 06/14] creation of control AutoSuggestBox Based on TextBox with custom Template and new parameters, --- MainDemo.Wpf/Domain/FieldsViewModel.cs | 89 +++ MainDemo.Wpf/Fields.xaml | 62 ++ MainDemo.Wpf/Fields.xaml.cs | 17 +- MaterialDesignThemes.Wpf/AutoSuggestBox.cs | 294 +++++++++ .../MaterialDesignThemes.Wpf.csproj | 2 +- MaterialDesignThemes.Wpf/Themes/Generic.xaml | 2 + .../Themes/MaterialDesign2.Defaults.xaml | 1 + .../Themes/MaterialDesign3.Defaults.xaml | 1 + .../MaterialDesignTheme.AutoSuggestBox.xaml | 571 ++++++++++++++++++ .../Themes/MaterialDesignTheme.Defaults.xaml | 1 + 10 files changed, 1036 insertions(+), 4 deletions(-) create mode 100644 MaterialDesignThemes.Wpf/AutoSuggestBox.cs create mode 100644 MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.AutoSuggestBox.xaml diff --git a/MainDemo.Wpf/Domain/FieldsViewModel.cs b/MainDemo.Wpf/Domain/FieldsViewModel.cs index bf2d5dad0b..7bd2329bfc 100644 --- a/MainDemo.Wpf/Domain/FieldsViewModel.cs +++ b/MainDemo.Wpf/Domain/FieldsViewModel.cs @@ -1,3 +1,8 @@ +using System.Collections.ObjectModel; +using System.Windows.Media; +using System.Xml; + + namespace MaterialDesignDemo.Domain { private string? _name; @@ -19,6 +24,12 @@ public string? Name private string? _password2Validated = "pre-filled"; private string? _text1; private string? _text2; + private ObservableCollection? _autoSuggestBox1Suggestions; + private string? _autoSuggestBox1Text; + private List? _originalAutoSuggestBox1Suggestions; + private ObservableCollection>? _autoSuggestBox2Suggestions; + private string? _autoSuggestBox2Text; + private List>? _originalAutoSuggestBox2Suggestions; public string? Name2 { @@ -76,6 +87,49 @@ public string? Password2Validated public FieldsTestObject TestObject => new() { Name = "Mr. Test" }; + public ObservableCollection? AutoSuggestBox1Suggestions + { + get { return _autoSuggestBox1Suggestions; } + set { SetProperty(ref _autoSuggestBox1Suggestions, value); } + } + + public ObservableCollection>? AutoSuggestBox2Suggestions + { + get { return _autoSuggestBox2Suggestions; } + set { SetProperty(ref _autoSuggestBox2Suggestions, value); } + } + + public string? AutoSuggestBox1Text + { + get { return _autoSuggestBox1Text; } + set + { + if (value != _autoSuggestBox1Text) + { + SetProperty(ref _autoSuggestBox1Text, value); + var searchResult = _originalAutoSuggestBox1Suggestions.Where(x => IsMatch(x, value ?? "")); + AutoSuggestBox1Suggestions = new ObservableCollection(searchResult); + } + } + } + + public string? AutoSuggestBox2Text + { + get { return _autoSuggestBox2Text; } + set + { + if (value != _autoSuggestBox2Text) + { + SetProperty(ref _autoSuggestBox2Text, value); + var searchResult = _originalAutoSuggestBox2Suggestions.Where(x => IsMatch(x.Key, value ?? "")); + AutoSuggestBox2Suggestions = new ObservableCollection>(searchResult); + } + } + } + + + + public ICommand SetPassword1FromViewModelCommand { get; } public ICommand SetPassword2FromViewModelCommand { get; } @@ -83,6 +137,41 @@ public FieldsViewModel() { SetPassword1FromViewModelCommand = new AnotherCommandImplementation(_ => Password1 = "Set from ViewModel!"); SetPassword2FromViewModelCommand = new AnotherCommandImplementation(_ => Password2 = "Set from ViewModel!"); + + InitializeData(); + } + private void InitializeData() + { + _originalAutoSuggestBox1Suggestions = new List() + { + "Burger", "Fries", "Shake", "Lettuce" + }; + + _originalAutoSuggestBox2Suggestions = new List>(GetColors()); + + AutoSuggestBox1Suggestions = new ObservableCollection(_originalAutoSuggestBox1Suggestions); + } + + private bool IsMatch(string item, string currentText) + { + return item.ToLower().Contains(currentText.ToLower()); + } + + private IEnumerable> GetColors() + { + return typeof(Colors) + .GetProperties() + .Where(prop => + typeof(Color).IsAssignableFrom(prop.PropertyType)) + .Select(prop => + new KeyValuePair(prop.Name, GenerateColorBrush(prop.GetValue(null)))); + } + + private SolidColorBrush GenerateColorBrush(object? prop) + { + if (prop is Color color) + return new SolidColorBrush(color); + return new SolidColorBrush(Colors.White); } } } diff --git a/MainDemo.Wpf/Fields.xaml b/MainDemo.Wpf/Fields.xaml index 6ee2767681..2780ce9d30 100644 --- a/MainDemo.Wpf/Fields.xaml +++ b/MainDemo.Wpf/Fields.xaml @@ -757,5 +757,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MainDemo.Wpf/Fields.xaml.cs b/MainDemo.Wpf/Fields.xaml.cs index b4d21b8fc4..07c555b577 100644 --- a/MainDemo.Wpf/Fields.xaml.cs +++ b/MainDemo.Wpf/Fields.xaml.cs @@ -1,4 +1,4 @@ -using System.Windows.Navigation; +using System.Windows.Navigation; using MaterialDesignDemo.Domain; namespace MaterialDesignDemo; @@ -7,8 +7,19 @@ public partial class Fields { public Fields() { - InitializeComponent(); - DataContext = new FieldsViewModel(); + public Fields() + { + InitializeComponent(); + DataContext = new FieldsViewModel(); + } + + private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e) + => Link.OpenInBrowser(e.Uri.AbsoluteUri); + + private void AutoSuggestBox_SuggestionChosen(object sender, RoutedPropertyChangedEventArgs e) + { + MessageBox.Show($"You chose {e.NewValue}"); + } } private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e) diff --git a/MaterialDesignThemes.Wpf/AutoSuggestBox.cs b/MaterialDesignThemes.Wpf/AutoSuggestBox.cs new file mode 100644 index 0000000000..62e5b7098d --- /dev/null +++ b/MaterialDesignThemes.Wpf/AutoSuggestBox.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections; +using System.Windows.Media; + +namespace MaterialDesignThemes.Wpf +{ + public class AutoSuggestBox : TextBox + { + #region Consts + + private const string AutoSuggestBoxListPart = "PART_AutoSuggestBoxList"; + + #endregion + + #region Properties + + protected ListBox? _autoSuggestBoxList; + + #endregion + + #region Dependency Properties + + public IEnumerable Suggestions + { + get => (IEnumerable)GetValue(SuggestionsProperty); + set => SetValue(SuggestionsProperty, value); + } + + public static readonly DependencyProperty SuggestionsProperty = + DependencyProperty.Register("Suggestions", typeof(IEnumerable), typeof(AutoSuggestBox), new PropertyMetadata(null)); + + public bool IsAutoSuggestionEnabled + { + get => (bool)GetValue(IsAutoSuggestionEnabledProperty); + set => SetValue(IsAutoSuggestionEnabledProperty, value); + } + + public static readonly DependencyProperty IsAutoSuggestionEnabledProperty = + DependencyProperty.Register("IsAutoSuggestionEnabled", typeof(bool), typeof(AutoSuggestBox), new PropertyMetadata(true, OnIsAutoSuggestionEnabledChanged)); + + + public string AutoSuggestBoxValueMember + { + get => (string)GetValue(AutoSuggestBoxValueMemberProperty); + set => SetValue(AutoSuggestBoxValueMemberProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxValueMemberProperty = + DependencyProperty.Register("AutoSuggestBoxValueMember", typeof(string), typeof(AutoSuggestBox), new PropertyMetadata(default(string))); + + + public string AutoSuggestBoxDisplayMember + { + get => (string)GetValue(AutoSuggestBoxDisplayMemberProperty); + set => SetValue(AutoSuggestBoxDisplayMemberProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxDisplayMemberProperty = + DependencyProperty.Register("AutoSuggestBoxDisplayMember", typeof(string), typeof(AutoSuggestBox), new PropertyMetadata(default(string))); + + + public CornerRadius AutoSuggestBoxCornerRadius + { + get => (CornerRadius)GetValue(AutoSuggestBoxCornerRadiusProperty); + set => SetValue(AutoSuggestBoxCornerRadiusProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxCornerRadiusProperty = + DependencyProperty.Register("AutoSuggestBoxCornerRadius", typeof(CornerRadius), typeof(AutoSuggestBox), new PropertyMetadata(default(CornerRadius))); + + public Brush AutoSuggestBoxBackground + { + get => (Brush)GetValue(AutoSuggestBoxBackgroundProperty); + set => SetValue(AutoSuggestBoxBackgroundProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxBackgroundProperty = + DependencyProperty.Register("AutoSuggestBoxBackground", typeof(Brush), typeof(AutoSuggestBox), new PropertyMetadata(default(Brush))); + + public DataTemplate AutoSuggestBoxItemTemplate + { + get => (DataTemplate)GetValue(AutoSuggestBoxItemTemplateProperty); + set => SetValue(AutoSuggestBoxItemTemplateProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxItemTemplateProperty = + DependencyProperty.Register("AutoSuggestBoxItemTemplate", typeof(DataTemplate), typeof(AutoSuggestBox), new PropertyMetadata(default(DataTemplate))); + + public Style AutoSuggestBoxItemContainerStyle + { + get => (Style)GetValue(AutoSuggestBoxItemContainerStyleProperty); + set => SetValue(AutoSuggestBoxItemContainerStyleProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxItemContainerStyleProperty = + DependencyProperty.Register("AutoSuggestBoxItemContainerStyle", typeof(Style), typeof(AutoSuggestBox), new PropertyMetadata(default(Style))); + + public Elevation AutoSuggestBoxElevation + { + get => (Elevation)GetValue(AutoSuggestBoxElevationProperty); + set => SetValue(AutoSuggestBoxElevationProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxElevationProperty = + DependencyProperty.Register("AutoSuggestBoxElevation", typeof(Elevation), typeof(AutoSuggestBox), new PropertyMetadata(default(Elevation))); + + public Brush AutoSuggestBoxBorderBrush + { + get => (Brush)GetValue(AutoSuggestBoxBorderBrushProperty); + set => SetValue(AutoSuggestBoxBorderBrushProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxBorderBrushProperty = + DependencyProperty.Register("AutoSuggestBoxBorderBrush", typeof(Brush), typeof(AutoSuggestBox), new PropertyMetadata(default(Brush))); + + public Thickness AutoSuggestBoxBorderThickness + { + get => (Thickness)GetValue(AutoSuggestBoxBorderThicknessProperty); + set => SetValue(AutoSuggestBoxBorderThicknessProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxBorderThicknessProperty = + DependencyProperty.Register("AutoSuggestBoxBorderThickness", typeof(Thickness), typeof(AutoSuggestBox), new PropertyMetadata(default(Thickness))); + + public double AutoSuggestBoxMaxHeight + { + get => (double)GetValue(AutoSuggestBoxMaxHeightProperty); + set => SetValue(AutoSuggestBoxMaxHeightProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxMaxHeightProperty = + DependencyProperty.Register("AutoSuggestBoxMaxHeight", typeof(double), typeof(AutoSuggestBox), new PropertyMetadata(200.0)); + + + public bool IsPopupOpen + { + get => (bool)GetValue(IsPopupOpenProperty); + set => SetValue(IsPopupOpenProperty, value); + } + public static readonly DependencyProperty IsPopupOpenProperty = + DependencyProperty.Register("IsPopupOpen", typeof(bool), typeof(AutoSuggestBox), new PropertyMetadata(default(bool))); + + + public static readonly RoutedEvent SuggestionChosenEvent = + EventManager.RegisterRoutedEvent( + "SuggestionChosen", + RoutingStrategy.Bubble, + typeof(RoutedPropertyChangedEventHandler), + typeof(AutoSuggestBox)); + + public event RoutedPropertyChangedEventHandler SuggestionChosen + { + add { AddHandler(SuggestionChosenEvent, value); } + remove { RemoveHandler(SuggestionChosenEvent, value); } + } + + #endregion + + #region Ctors + + static AutoSuggestBox() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(AutoSuggestBox), new FrameworkPropertyMetadata(typeof(AutoSuggestBox))); + } + + #endregion + + #region Override methods + + public override void OnApplyTemplate() + { + if (GetTemplateChild(AutoSuggestBoxListPart) is ListBox listBox) + { + _autoSuggestBoxList = listBox; + + listBox.PreviewMouseDown -= AutoSuggestionListBox_PreviewMouseDown; + + base.OnApplyTemplate(); + + listBox.PreviewMouseDown += AutoSuggestionListBox_PreviewMouseDown; + } + } + + protected override void OnPreviewKeyDown(KeyEventArgs e) + { + base.OnPreviewKeyDown(e); + if (_autoSuggestBoxList is null) return; + switch (e.Key) + { + case Key.Down: + IncrementSelection(); + break; + case Key.Up: + DecrementSelection(); + break; + case Key.Enter: + CommitValueSelection(); + break; + case Key.Escape: + CloseAutoSuggestionPopUp(); + break; + case Key.Tab: + CommitValueSelection(); + break; + default: + return; + } + e.Handled = true; + } + + protected override void OnLostFocus(RoutedEventArgs e) + { + base.OnLostFocus(e); + CloseAutoSuggestionPopUp(); + } + + #endregion + + #region Callback handlers + + private static void OnIsAutoSuggestionEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is AutoSuggestBox instance) + instance.IsPopupOpen = false; + } + + protected override void OnTextChanged(TextChangedEventArgs e) + { + base.OnTextChanged(e); + if (_autoSuggestBoxList is null) + return; + if ((Text.Length == 0 || _autoSuggestBoxList.Items.Count == 0) && IsPopupOpen) + IsPopupOpen = false; + else if (Text.Length > 0 && !IsPopupOpen && IsFocused && _autoSuggestBoxList.Items.Count > 0) + IsPopupOpen = true; + } + + + + #endregion + + #region Methods + + private void AutoSuggestionListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) + { + if (_autoSuggestBoxList is not null && e.OriginalSource is FrameworkElement element) + { + if (!_autoSuggestBoxList.Items.Contains(element.DataContext)) + return; + _autoSuggestBoxList.SelectedItem = element.DataContext; + CommitValueSelection(); + } + } + + private void CloseAutoSuggestionPopUp() + { + IsPopupOpen = false; + } + + private void CommitValueSelection() + { + if (_autoSuggestBoxList is not null && _autoSuggestBoxList.SelectedValue != null) + { + var oldValue = Text; + Text = _autoSuggestBoxList.SelectedValue.ToString(); + if (Text != null) + CaretIndex = Text.Length; + CloseAutoSuggestionPopUp(); + var args = new RoutedPropertyChangedEventArgs( + oldValue, + Text + ) + { + RoutedEvent = SuggestionChosenEvent + }; + RaiseEvent(args); + } + } + + private void DecrementSelection() + { + if (_autoSuggestBoxList is null) + return; + if (_autoSuggestBoxList.SelectedIndex == 0 || _autoSuggestBoxList.SelectedIndex == -1) + _autoSuggestBoxList.SelectedIndex = _autoSuggestBoxList.Items.Count - 1; + else + _autoSuggestBoxList.SelectedIndex -= 1; + _autoSuggestBoxList.ScrollIntoView(_autoSuggestBoxList.SelectedItem); + } + + private void IncrementSelection() + { + if (_autoSuggestBoxList is null) + return; + if (_autoSuggestBoxList.SelectedIndex == _autoSuggestBoxList.Items.Count - 1) + _autoSuggestBoxList.SelectedIndex = 0; + else + _autoSuggestBoxList.SelectedIndex += 1; + _autoSuggestBoxList.ScrollIntoView(_autoSuggestBoxList.SelectedItem); + } + + #endregion + } +} diff --git a/MaterialDesignThemes.Wpf/MaterialDesignThemes.Wpf.csproj b/MaterialDesignThemes.Wpf/MaterialDesignThemes.Wpf.csproj index 5f21628593..42b98478a3 100644 --- a/MaterialDesignThemes.Wpf/MaterialDesignThemes.Wpf.csproj +++ b/MaterialDesignThemes.Wpf/MaterialDesignThemes.Wpf.csproj @@ -1,4 +1,4 @@ - + net462;net6.0-windows;net7.0-windows diff --git a/MaterialDesignThemes.Wpf/Themes/Generic.xaml b/MaterialDesignThemes.Wpf/Themes/Generic.xaml index b8a72197fb..232cbf621a 100644 --- a/MaterialDesignThemes.Wpf/Themes/Generic.xaml +++ b/MaterialDesignThemes.Wpf/Themes/Generic.xaml @@ -29,6 +29,7 @@ + @@ -37,6 +38,7 @@ + + + + + + + + diff --git a/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Defaults.xaml b/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Defaults.xaml index e21deed4b2..916abd7f0d 100644 --- a/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Defaults.xaml +++ b/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Defaults.xaml @@ -42,6 +42,7 @@ + From dbaa4e38ee8dbb286e448ad0d66f959dfc262f7c Mon Sep 17 00:00:00 2001 From: Abderrahmane Ahmam Date: Mon, 19 Jun 2023 09:32:00 +0100 Subject: [PATCH 07/14] Fix Warnings --- MainDemo.Wpf/Domain/FieldsViewModel.cs | 14 ++++++++++---- MaterialDesignThemes.Wpf/AutoSuggestBox.cs | 4 +--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/MainDemo.Wpf/Domain/FieldsViewModel.cs b/MainDemo.Wpf/Domain/FieldsViewModel.cs index 7bd2329bfc..668090a2d4 100644 --- a/MainDemo.Wpf/Domain/FieldsViewModel.cs +++ b/MainDemo.Wpf/Domain/FieldsViewModel.cs @@ -107,8 +107,11 @@ public string? AutoSuggestBox1Text if (value != _autoSuggestBox1Text) { SetProperty(ref _autoSuggestBox1Text, value); - var searchResult = _originalAutoSuggestBox1Suggestions.Where(x => IsMatch(x, value ?? "")); - AutoSuggestBox1Suggestions = new ObservableCollection(searchResult); + if (_originalAutoSuggestBox1Suggestions != null && value != null) + { + var searchResult = _originalAutoSuggestBox1Suggestions.Where(x => IsMatch(x, value)); + AutoSuggestBox1Suggestions = new ObservableCollection(searchResult); + } } } } @@ -121,8 +124,11 @@ public string? AutoSuggestBox2Text if (value != _autoSuggestBox2Text) { SetProperty(ref _autoSuggestBox2Text, value); - var searchResult = _originalAutoSuggestBox2Suggestions.Where(x => IsMatch(x.Key, value ?? "")); - AutoSuggestBox2Suggestions = new ObservableCollection>(searchResult); + if (_originalAutoSuggestBox2Suggestions != null && value != null) + { + var searchResult = _originalAutoSuggestBox2Suggestions.Where(x => IsMatch(x.Key, value)); + AutoSuggestBox2Suggestions = new ObservableCollection>(searchResult); + } } } } diff --git a/MaterialDesignThemes.Wpf/AutoSuggestBox.cs b/MaterialDesignThemes.Wpf/AutoSuggestBox.cs index 62e5b7098d..7820703b8b 100644 --- a/MaterialDesignThemes.Wpf/AutoSuggestBox.cs +++ b/MaterialDesignThemes.Wpf/AutoSuggestBox.cs @@ -224,9 +224,7 @@ protected override void OnTextChanged(TextChangedEventArgs e) else if (Text.Length > 0 && !IsPopupOpen && IsFocused && _autoSuggestBoxList.Items.Count > 0) IsPopupOpen = true; } - - - + #endregion #region Methods From e0d3ed0ffad0382ef7d0aa848eeee54a43306830 Mon Sep 17 00:00:00 2001 From: Kevin Bost Date: Wed, 9 Aug 2023 22:44:40 -0700 Subject: [PATCH 08/14] Rebase build fixing --- MainDemo.Wpf/Domain/FieldsViewModel.cs | 155 ++++--- MainDemo.Wpf/Fields.xaml.cs | 21 +- MaterialDesignThemes.Wpf/AutoSuggestBox.cs | 456 ++++++++++---------- MaterialDesignThemes.Wpf/TextFieldAssist.cs | 38 +- 4 files changed, 329 insertions(+), 341 deletions(-) diff --git a/MainDemo.Wpf/Domain/FieldsViewModel.cs b/MainDemo.Wpf/Domain/FieldsViewModel.cs index 668090a2d4..8ffd88be27 100644 --- a/MainDemo.Wpf/Domain/FieldsViewModel.cs +++ b/MainDemo.Wpf/Domain/FieldsViewModel.cs @@ -1,9 +1,8 @@ using System.Collections.ObjectModel; using System.Windows.Media; -using System.Xml; - -namespace MaterialDesignDemo.Domain +namespace MaterialDesignDemo.Domain; +public class FieldsViewModel : ViewModelBase { private string? _name; private string? _name2; @@ -13,23 +12,18 @@ namespace MaterialDesignDemo.Domain private string? _password2Validated = "pre-filled"; private string? _text1; private string? _text2; + private ObservableCollection? _autoSuggestBox1Suggestions; + private string? _autoSuggestBox1Text; + private List? _originalAutoSuggestBox1Suggestions; + private ObservableCollection>? _autoSuggestBox2Suggestions; + private string? _autoSuggestBox2Text; + private List>? _originalAutoSuggestBox2Suggestions; public string? Name { - private string? _name; - private string? _name2; - private string? _password1 = string.Empty; - private string? _password2 = "pre-filled"; - private string? _password1Validated = "pre-filled"; - private string? _password2Validated = "pre-filled"; - private string? _text1; - private string? _text2; - private ObservableCollection? _autoSuggestBox1Suggestions; - private string? _autoSuggestBox1Text; - private List? _originalAutoSuggestBox1Suggestions; - private ObservableCollection>? _autoSuggestBox2Suggestions; - private string? _autoSuggestBox2Text; - private List>? _originalAutoSuggestBox2Suggestions; + get => _name; + set => SetProperty(ref _name, value); + } public string? Name2 { @@ -85,100 +79,97 @@ public string? Password2Validated public FieldsTestObject TestObject => new() { Name = "Mr. Test" }; - public FieldsTestObject TestObject => new() { Name = "Mr. Test" }; - - public ObservableCollection? AutoSuggestBox1Suggestions - { - get { return _autoSuggestBox1Suggestions; } - set { SetProperty(ref _autoSuggestBox1Suggestions, value); } - } + public ObservableCollection? AutoSuggestBox1Suggestions + { + get { return _autoSuggestBox1Suggestions; } + set { SetProperty(ref _autoSuggestBox1Suggestions, value); } + } - public ObservableCollection>? AutoSuggestBox2Suggestions - { - get { return _autoSuggestBox2Suggestions; } - set { SetProperty(ref _autoSuggestBox2Suggestions, value); } - } + public ObservableCollection>? AutoSuggestBox2Suggestions + { + get { return _autoSuggestBox2Suggestions; } + set { SetProperty(ref _autoSuggestBox2Suggestions, value); } + } - public string? AutoSuggestBox1Text + public string? AutoSuggestBox1Text + { + get { return _autoSuggestBox1Text; } + set { - get { return _autoSuggestBox1Text; } - set + if (value != _autoSuggestBox1Text) { - if (value != _autoSuggestBox1Text) + SetProperty(ref _autoSuggestBox1Text, value); + if (_originalAutoSuggestBox1Suggestions != null && value != null) { - SetProperty(ref _autoSuggestBox1Text, value); - if (_originalAutoSuggestBox1Suggestions != null && value != null) - { - var searchResult = _originalAutoSuggestBox1Suggestions.Where(x => IsMatch(x, value)); - AutoSuggestBox1Suggestions = new ObservableCollection(searchResult); - } + var searchResult = _originalAutoSuggestBox1Suggestions.Where(x => IsMatch(x, value)); + AutoSuggestBox1Suggestions = new ObservableCollection(searchResult); } } } + } - public string? AutoSuggestBox2Text + public string? AutoSuggestBox2Text + { + get { return _autoSuggestBox2Text; } + set { - get { return _autoSuggestBox2Text; } - set + if (value != _autoSuggestBox2Text) { - if (value != _autoSuggestBox2Text) + SetProperty(ref _autoSuggestBox2Text, value); + if (_originalAutoSuggestBox2Suggestions != null && value != null) { - SetProperty(ref _autoSuggestBox2Text, value); - if (_originalAutoSuggestBox2Suggestions != null && value != null) - { - var searchResult = _originalAutoSuggestBox2Suggestions.Where(x => IsMatch(x.Key, value)); - AutoSuggestBox2Suggestions = new ObservableCollection>(searchResult); - } + var searchResult = _originalAutoSuggestBox2Suggestions.Where(x => IsMatch(x.Key, value)); + AutoSuggestBox2Suggestions = new ObservableCollection>(searchResult); } } } + } - public ICommand SetPassword1FromViewModelCommand { get; } - public ICommand SetPassword2FromViewModelCommand { get; } + public ICommand SetPassword1FromViewModelCommand { get; } + public ICommand SetPassword2FromViewModelCommand { get; } - public FieldsViewModel() - { - SetPassword1FromViewModelCommand = new AnotherCommandImplementation(_ => Password1 = "Set from ViewModel!"); - SetPassword2FromViewModelCommand = new AnotherCommandImplementation(_ => Password2 = "Set from ViewModel!"); + public FieldsViewModel() + { + SetPassword1FromViewModelCommand = new AnotherCommandImplementation(_ => Password1 = "Set from ViewModel!"); + SetPassword2FromViewModelCommand = new AnotherCommandImplementation(_ => Password2 = "Set from ViewModel!"); - InitializeData(); - } - private void InitializeData() - { - _originalAutoSuggestBox1Suggestions = new List() + InitializeData(); + } + private void InitializeData() + { + _originalAutoSuggestBox1Suggestions = new List() { "Burger", "Fries", "Shake", "Lettuce" }; - _originalAutoSuggestBox2Suggestions = new List>(GetColors()); + _originalAutoSuggestBox2Suggestions = new List>(GetColors()); - AutoSuggestBox1Suggestions = new ObservableCollection(_originalAutoSuggestBox1Suggestions); - } + AutoSuggestBox1Suggestions = new ObservableCollection(_originalAutoSuggestBox1Suggestions); + } - private bool IsMatch(string item, string currentText) - { - return item.ToLower().Contains(currentText.ToLower()); - } + private bool IsMatch(string item, string currentText) + { + return item.ToLower().Contains(currentText.ToLower()); + } - private IEnumerable> GetColors() - { - return typeof(Colors) - .GetProperties() - .Where(prop => - typeof(Color).IsAssignableFrom(prop.PropertyType)) - .Select(prop => - new KeyValuePair(prop.Name, GenerateColorBrush(prop.GetValue(null)))); - } + private IEnumerable> GetColors() + { + return typeof(Colors) + .GetProperties() + .Where(prop => + typeof(Color).IsAssignableFrom(prop.PropertyType)) + .Select(prop => + new KeyValuePair(prop.Name, GenerateColorBrush(prop.GetValue(null)))); + } - private SolidColorBrush GenerateColorBrush(object? prop) - { - if (prop is Color color) - return new SolidColorBrush(color); - return new SolidColorBrush(Colors.White); - } + private SolidColorBrush GenerateColorBrush(object? prop) + { + if (prop is Color color) + return new SolidColorBrush(color); + return new SolidColorBrush(Colors.White); } } diff --git a/MainDemo.Wpf/Fields.xaml.cs b/MainDemo.Wpf/Fields.xaml.cs index 07c555b577..4433312563 100644 --- a/MainDemo.Wpf/Fields.xaml.cs +++ b/MainDemo.Wpf/Fields.xaml.cs @@ -7,21 +7,16 @@ public partial class Fields { public Fields() { - public Fields() - { - InitializeComponent(); - DataContext = new FieldsViewModel(); - } - - private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e) - => Link.OpenInBrowser(e.Uri.AbsoluteUri); - - private void AutoSuggestBox_SuggestionChosen(object sender, RoutedPropertyChangedEventArgs e) - { - MessageBox.Show($"You chose {e.NewValue}"); - } + InitializeComponent(); + DataContext = new FieldsViewModel(); } private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e) => Link.OpenInBrowser(e.Uri.AbsoluteUri); + + private void AutoSuggestBox_SuggestionChosen(object sender, RoutedPropertyChangedEventArgs e) + { + MessageBox.Show($"You chose {e.NewValue}"); + } + } diff --git a/MaterialDesignThemes.Wpf/AutoSuggestBox.cs b/MaterialDesignThemes.Wpf/AutoSuggestBox.cs index 7820703b8b..cef23274c0 100644 --- a/MaterialDesignThemes.Wpf/AutoSuggestBox.cs +++ b/MaterialDesignThemes.Wpf/AutoSuggestBox.cs @@ -1,292 +1,290 @@ -using System; -using System.Collections; +using System.Collections; using System.Windows.Media; -namespace MaterialDesignThemes.Wpf +namespace MaterialDesignThemes.Wpf; + +public class AutoSuggestBox : TextBox { - public class AutoSuggestBox : TextBox - { - #region Consts + #region Consts - private const string AutoSuggestBoxListPart = "PART_AutoSuggestBoxList"; + private const string AutoSuggestBoxListPart = "PART_AutoSuggestBoxList"; - #endregion + #endregion - #region Properties + #region Properties - protected ListBox? _autoSuggestBoxList; + protected ListBox? _autoSuggestBoxList; - #endregion + #endregion - #region Dependency Properties + #region Dependency Properties - public IEnumerable Suggestions - { - get => (IEnumerable)GetValue(SuggestionsProperty); - set => SetValue(SuggestionsProperty, value); - } + public IEnumerable Suggestions + { + get => (IEnumerable)GetValue(SuggestionsProperty); + set => SetValue(SuggestionsProperty, value); + } - public static readonly DependencyProperty SuggestionsProperty = - DependencyProperty.Register("Suggestions", typeof(IEnumerable), typeof(AutoSuggestBox), new PropertyMetadata(null)); + public static readonly DependencyProperty SuggestionsProperty = + DependencyProperty.Register("Suggestions", typeof(IEnumerable), typeof(AutoSuggestBox), new PropertyMetadata(null)); - public bool IsAutoSuggestionEnabled - { - get => (bool)GetValue(IsAutoSuggestionEnabledProperty); - set => SetValue(IsAutoSuggestionEnabledProperty, value); - } + public bool IsAutoSuggestionEnabled + { + get => (bool)GetValue(IsAutoSuggestionEnabledProperty); + set => SetValue(IsAutoSuggestionEnabledProperty, value); + } - public static readonly DependencyProperty IsAutoSuggestionEnabledProperty = - DependencyProperty.Register("IsAutoSuggestionEnabled", typeof(bool), typeof(AutoSuggestBox), new PropertyMetadata(true, OnIsAutoSuggestionEnabledChanged)); + public static readonly DependencyProperty IsAutoSuggestionEnabledProperty = + DependencyProperty.Register("IsAutoSuggestionEnabled", typeof(bool), typeof(AutoSuggestBox), new PropertyMetadata(true, OnIsAutoSuggestionEnabledChanged)); - public string AutoSuggestBoxValueMember - { - get => (string)GetValue(AutoSuggestBoxValueMemberProperty); - set => SetValue(AutoSuggestBoxValueMemberProperty, value); - } - public static readonly DependencyProperty AutoSuggestBoxValueMemberProperty = - DependencyProperty.Register("AutoSuggestBoxValueMember", typeof(string), typeof(AutoSuggestBox), new PropertyMetadata(default(string))); + public string AutoSuggestBoxValueMember + { + get => (string)GetValue(AutoSuggestBoxValueMemberProperty); + set => SetValue(AutoSuggestBoxValueMemberProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxValueMemberProperty = + DependencyProperty.Register("AutoSuggestBoxValueMember", typeof(string), typeof(AutoSuggestBox), new PropertyMetadata(default(string))); - public string AutoSuggestBoxDisplayMember - { - get => (string)GetValue(AutoSuggestBoxDisplayMemberProperty); - set => SetValue(AutoSuggestBoxDisplayMemberProperty, value); - } - public static readonly DependencyProperty AutoSuggestBoxDisplayMemberProperty = - DependencyProperty.Register("AutoSuggestBoxDisplayMember", typeof(string), typeof(AutoSuggestBox), new PropertyMetadata(default(string))); + public string AutoSuggestBoxDisplayMember + { + get => (string)GetValue(AutoSuggestBoxDisplayMemberProperty); + set => SetValue(AutoSuggestBoxDisplayMemberProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxDisplayMemberProperty = + DependencyProperty.Register("AutoSuggestBoxDisplayMember", typeof(string), typeof(AutoSuggestBox), new PropertyMetadata(default(string))); - public CornerRadius AutoSuggestBoxCornerRadius - { - get => (CornerRadius)GetValue(AutoSuggestBoxCornerRadiusProperty); - set => SetValue(AutoSuggestBoxCornerRadiusProperty, value); - } - public static readonly DependencyProperty AutoSuggestBoxCornerRadiusProperty = - DependencyProperty.Register("AutoSuggestBoxCornerRadius", typeof(CornerRadius), typeof(AutoSuggestBox), new PropertyMetadata(default(CornerRadius))); + public CornerRadius AutoSuggestBoxCornerRadius + { + get => (CornerRadius)GetValue(AutoSuggestBoxCornerRadiusProperty); + set => SetValue(AutoSuggestBoxCornerRadiusProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxCornerRadiusProperty = + DependencyProperty.Register("AutoSuggestBoxCornerRadius", typeof(CornerRadius), typeof(AutoSuggestBox), new PropertyMetadata(default(CornerRadius))); - public Brush AutoSuggestBoxBackground - { - get => (Brush)GetValue(AutoSuggestBoxBackgroundProperty); - set => SetValue(AutoSuggestBoxBackgroundProperty, value); - } - public static readonly DependencyProperty AutoSuggestBoxBackgroundProperty = - DependencyProperty.Register("AutoSuggestBoxBackground", typeof(Brush), typeof(AutoSuggestBox), new PropertyMetadata(default(Brush))); + public Brush AutoSuggestBoxBackground + { + get => (Brush)GetValue(AutoSuggestBoxBackgroundProperty); + set => SetValue(AutoSuggestBoxBackgroundProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxBackgroundProperty = + DependencyProperty.Register("AutoSuggestBoxBackground", typeof(Brush), typeof(AutoSuggestBox), new PropertyMetadata(default(Brush))); - public DataTemplate AutoSuggestBoxItemTemplate - { - get => (DataTemplate)GetValue(AutoSuggestBoxItemTemplateProperty); - set => SetValue(AutoSuggestBoxItemTemplateProperty, value); - } - public static readonly DependencyProperty AutoSuggestBoxItemTemplateProperty = - DependencyProperty.Register("AutoSuggestBoxItemTemplate", typeof(DataTemplate), typeof(AutoSuggestBox), new PropertyMetadata(default(DataTemplate))); + public DataTemplate AutoSuggestBoxItemTemplate + { + get => (DataTemplate)GetValue(AutoSuggestBoxItemTemplateProperty); + set => SetValue(AutoSuggestBoxItemTemplateProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxItemTemplateProperty = + DependencyProperty.Register("AutoSuggestBoxItemTemplate", typeof(DataTemplate), typeof(AutoSuggestBox), new PropertyMetadata(default(DataTemplate))); - public Style AutoSuggestBoxItemContainerStyle - { - get => (Style)GetValue(AutoSuggestBoxItemContainerStyleProperty); - set => SetValue(AutoSuggestBoxItemContainerStyleProperty, value); - } - public static readonly DependencyProperty AutoSuggestBoxItemContainerStyleProperty = - DependencyProperty.Register("AutoSuggestBoxItemContainerStyle", typeof(Style), typeof(AutoSuggestBox), new PropertyMetadata(default(Style))); + public Style AutoSuggestBoxItemContainerStyle + { + get => (Style)GetValue(AutoSuggestBoxItemContainerStyleProperty); + set => SetValue(AutoSuggestBoxItemContainerStyleProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxItemContainerStyleProperty = + DependencyProperty.Register("AutoSuggestBoxItemContainerStyle", typeof(Style), typeof(AutoSuggestBox), new PropertyMetadata(default(Style))); - public Elevation AutoSuggestBoxElevation - { - get => (Elevation)GetValue(AutoSuggestBoxElevationProperty); - set => SetValue(AutoSuggestBoxElevationProperty, value); - } - public static readonly DependencyProperty AutoSuggestBoxElevationProperty = - DependencyProperty.Register("AutoSuggestBoxElevation", typeof(Elevation), typeof(AutoSuggestBox), new PropertyMetadata(default(Elevation))); + public Elevation AutoSuggestBoxElevation + { + get => (Elevation)GetValue(AutoSuggestBoxElevationProperty); + set => SetValue(AutoSuggestBoxElevationProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxElevationProperty = + DependencyProperty.Register("AutoSuggestBoxElevation", typeof(Elevation), typeof(AutoSuggestBox), new PropertyMetadata(default(Elevation))); - public Brush AutoSuggestBoxBorderBrush - { - get => (Brush)GetValue(AutoSuggestBoxBorderBrushProperty); - set => SetValue(AutoSuggestBoxBorderBrushProperty, value); - } - public static readonly DependencyProperty AutoSuggestBoxBorderBrushProperty = - DependencyProperty.Register("AutoSuggestBoxBorderBrush", typeof(Brush), typeof(AutoSuggestBox), new PropertyMetadata(default(Brush))); + public Brush AutoSuggestBoxBorderBrush + { + get => (Brush)GetValue(AutoSuggestBoxBorderBrushProperty); + set => SetValue(AutoSuggestBoxBorderBrushProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxBorderBrushProperty = + DependencyProperty.Register("AutoSuggestBoxBorderBrush", typeof(Brush), typeof(AutoSuggestBox), new PropertyMetadata(default(Brush))); - public Thickness AutoSuggestBoxBorderThickness - { - get => (Thickness)GetValue(AutoSuggestBoxBorderThicknessProperty); - set => SetValue(AutoSuggestBoxBorderThicknessProperty, value); - } - public static readonly DependencyProperty AutoSuggestBoxBorderThicknessProperty = - DependencyProperty.Register("AutoSuggestBoxBorderThickness", typeof(Thickness), typeof(AutoSuggestBox), new PropertyMetadata(default(Thickness))); + public Thickness AutoSuggestBoxBorderThickness + { + get => (Thickness)GetValue(AutoSuggestBoxBorderThicknessProperty); + set => SetValue(AutoSuggestBoxBorderThicknessProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxBorderThicknessProperty = + DependencyProperty.Register("AutoSuggestBoxBorderThickness", typeof(Thickness), typeof(AutoSuggestBox), new PropertyMetadata(default(Thickness))); - public double AutoSuggestBoxMaxHeight - { - get => (double)GetValue(AutoSuggestBoxMaxHeightProperty); - set => SetValue(AutoSuggestBoxMaxHeightProperty, value); - } - public static readonly DependencyProperty AutoSuggestBoxMaxHeightProperty = - DependencyProperty.Register("AutoSuggestBoxMaxHeight", typeof(double), typeof(AutoSuggestBox), new PropertyMetadata(200.0)); + public double AutoSuggestBoxMaxHeight + { + get => (double)GetValue(AutoSuggestBoxMaxHeightProperty); + set => SetValue(AutoSuggestBoxMaxHeightProperty, value); + } + public static readonly DependencyProperty AutoSuggestBoxMaxHeightProperty = + DependencyProperty.Register("AutoSuggestBoxMaxHeight", typeof(double), typeof(AutoSuggestBox), new PropertyMetadata(200.0)); - public bool IsPopupOpen - { - get => (bool)GetValue(IsPopupOpenProperty); - set => SetValue(IsPopupOpenProperty, value); - } - public static readonly DependencyProperty IsPopupOpenProperty = - DependencyProperty.Register("IsPopupOpen", typeof(bool), typeof(AutoSuggestBox), new PropertyMetadata(default(bool))); + public bool IsPopupOpen + { + get => (bool)GetValue(IsPopupOpenProperty); + set => SetValue(IsPopupOpenProperty, value); + } + public static readonly DependencyProperty IsPopupOpenProperty = + DependencyProperty.Register("IsPopupOpen", typeof(bool), typeof(AutoSuggestBox), new PropertyMetadata(default(bool))); - public static readonly RoutedEvent SuggestionChosenEvent = - EventManager.RegisterRoutedEvent( - "SuggestionChosen", - RoutingStrategy.Bubble, - typeof(RoutedPropertyChangedEventHandler), - typeof(AutoSuggestBox)); + public static readonly RoutedEvent SuggestionChosenEvent = + EventManager.RegisterRoutedEvent( + "SuggestionChosen", + RoutingStrategy.Bubble, + typeof(RoutedPropertyChangedEventHandler), + typeof(AutoSuggestBox)); - public event RoutedPropertyChangedEventHandler SuggestionChosen - { - add { AddHandler(SuggestionChosenEvent, value); } - remove { RemoveHandler(SuggestionChosenEvent, value); } - } + public event RoutedPropertyChangedEventHandler SuggestionChosen + { + add { AddHandler(SuggestionChosenEvent, value); } + remove { RemoveHandler(SuggestionChosenEvent, value); } + } - #endregion + #endregion - #region Ctors + #region Ctors - static AutoSuggestBox() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(AutoSuggestBox), new FrameworkPropertyMetadata(typeof(AutoSuggestBox))); - } + static AutoSuggestBox() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(AutoSuggestBox), new FrameworkPropertyMetadata(typeof(AutoSuggestBox))); + } - #endregion + #endregion - #region Override methods + #region Override methods - public override void OnApplyTemplate() + public override void OnApplyTemplate() + { + if (GetTemplateChild(AutoSuggestBoxListPart) is ListBox listBox) { - if (GetTemplateChild(AutoSuggestBoxListPart) is ListBox listBox) - { - _autoSuggestBoxList = listBox; + _autoSuggestBoxList = listBox; - listBox.PreviewMouseDown -= AutoSuggestionListBox_PreviewMouseDown; + listBox.PreviewMouseDown -= AutoSuggestionListBox_PreviewMouseDown; - base.OnApplyTemplate(); + base.OnApplyTemplate(); - listBox.PreviewMouseDown += AutoSuggestionListBox_PreviewMouseDown; - } + listBox.PreviewMouseDown += AutoSuggestionListBox_PreviewMouseDown; } + } - protected override void OnPreviewKeyDown(KeyEventArgs e) + protected override void OnPreviewKeyDown(KeyEventArgs e) + { + base.OnPreviewKeyDown(e); + if (_autoSuggestBoxList is null) return; + switch (e.Key) { - base.OnPreviewKeyDown(e); - if (_autoSuggestBoxList is null) return; - switch (e.Key) - { - case Key.Down: - IncrementSelection(); - break; - case Key.Up: - DecrementSelection(); - break; - case Key.Enter: - CommitValueSelection(); - break; - case Key.Escape: - CloseAutoSuggestionPopUp(); - break; - case Key.Tab: - CommitValueSelection(); - break; - default: - return; - } - e.Handled = true; + case Key.Down: + IncrementSelection(); + break; + case Key.Up: + DecrementSelection(); + break; + case Key.Enter: + CommitValueSelection(); + break; + case Key.Escape: + CloseAutoSuggestionPopUp(); + break; + case Key.Tab: + CommitValueSelection(); + break; + default: + return; } + e.Handled = true; + } - protected override void OnLostFocus(RoutedEventArgs e) - { - base.OnLostFocus(e); - CloseAutoSuggestionPopUp(); - } + protected override void OnLostFocus(RoutedEventArgs e) + { + base.OnLostFocus(e); + CloseAutoSuggestionPopUp(); + } - #endregion + #endregion - #region Callback handlers + #region Callback handlers - private static void OnIsAutoSuggestionEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is AutoSuggestBox instance) - instance.IsPopupOpen = false; - } + private static void OnIsAutoSuggestionEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is AutoSuggestBox instance) + instance.IsPopupOpen = false; + } - protected override void OnTextChanged(TextChangedEventArgs e) - { - base.OnTextChanged(e); - if (_autoSuggestBoxList is null) - return; - if ((Text.Length == 0 || _autoSuggestBoxList.Items.Count == 0) && IsPopupOpen) - IsPopupOpen = false; - else if (Text.Length > 0 && !IsPopupOpen && IsFocused && _autoSuggestBoxList.Items.Count > 0) - IsPopupOpen = true; - } - - #endregion + protected override void OnTextChanged(TextChangedEventArgs e) + { + base.OnTextChanged(e); + if (_autoSuggestBoxList is null) + return; + if ((Text.Length == 0 || _autoSuggestBoxList.Items.Count == 0) && IsPopupOpen) + IsPopupOpen = false; + else if (Text.Length > 0 && !IsPopupOpen && IsFocused && _autoSuggestBoxList.Items.Count > 0) + IsPopupOpen = true; + } + + #endregion - #region Methods + #region Methods - private void AutoSuggestionListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) + private void AutoSuggestionListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) + { + if (_autoSuggestBoxList is not null && e.OriginalSource is FrameworkElement element) { - if (_autoSuggestBoxList is not null && e.OriginalSource is FrameworkElement element) - { - if (!_autoSuggestBoxList.Items.Contains(element.DataContext)) - return; - _autoSuggestBoxList.SelectedItem = element.DataContext; - CommitValueSelection(); - } + if (!_autoSuggestBoxList.Items.Contains(element.DataContext)) + return; + _autoSuggestBoxList.SelectedItem = element.DataContext; + CommitValueSelection(); } + } - private void CloseAutoSuggestionPopUp() - { - IsPopupOpen = false; - } + private void CloseAutoSuggestionPopUp() + { + IsPopupOpen = false; + } - private void CommitValueSelection() + private void CommitValueSelection() + { + if (_autoSuggestBoxList is not null && _autoSuggestBoxList.SelectedValue != null) { - if (_autoSuggestBoxList is not null && _autoSuggestBoxList.SelectedValue != null) + var oldValue = Text; + Text = _autoSuggestBoxList.SelectedValue.ToString(); + if (Text != null) + CaretIndex = Text.Length; + CloseAutoSuggestionPopUp(); + var args = new RoutedPropertyChangedEventArgs( + oldValue, + Text + ) { - var oldValue = Text; - Text = _autoSuggestBoxList.SelectedValue.ToString(); - if (Text != null) - CaretIndex = Text.Length; - CloseAutoSuggestionPopUp(); - var args = new RoutedPropertyChangedEventArgs( - oldValue, - Text - ) - { - RoutedEvent = SuggestionChosenEvent - }; - RaiseEvent(args); - } - } - - private void DecrementSelection() - { - if (_autoSuggestBoxList is null) - return; - if (_autoSuggestBoxList.SelectedIndex == 0 || _autoSuggestBoxList.SelectedIndex == -1) - _autoSuggestBoxList.SelectedIndex = _autoSuggestBoxList.Items.Count - 1; - else - _autoSuggestBoxList.SelectedIndex -= 1; - _autoSuggestBoxList.ScrollIntoView(_autoSuggestBoxList.SelectedItem); + RoutedEvent = SuggestionChosenEvent + }; + RaiseEvent(args); } + } - private void IncrementSelection() - { - if (_autoSuggestBoxList is null) - return; - if (_autoSuggestBoxList.SelectedIndex == _autoSuggestBoxList.Items.Count - 1) - _autoSuggestBoxList.SelectedIndex = 0; - else - _autoSuggestBoxList.SelectedIndex += 1; - _autoSuggestBoxList.ScrollIntoView(_autoSuggestBoxList.SelectedItem); - } + private void DecrementSelection() + { + if (_autoSuggestBoxList is null) + return; + if (_autoSuggestBoxList.SelectedIndex == 0 || _autoSuggestBoxList.SelectedIndex == -1) + _autoSuggestBoxList.SelectedIndex = _autoSuggestBoxList.Items.Count - 1; + else + _autoSuggestBoxList.SelectedIndex -= 1; + _autoSuggestBoxList.ScrollIntoView(_autoSuggestBoxList.SelectedItem); + } - #endregion + private void IncrementSelection() + { + if (_autoSuggestBoxList is null) + return; + if (_autoSuggestBoxList.SelectedIndex == _autoSuggestBoxList.Items.Count - 1) + _autoSuggestBoxList.SelectedIndex = 0; + else + _autoSuggestBoxList.SelectedIndex += 1; + _autoSuggestBoxList.ScrollIntoView(_autoSuggestBoxList.SelectedItem); } + + #endregion } diff --git a/MaterialDesignThemes.Wpf/TextFieldAssist.cs b/MaterialDesignThemes.Wpf/TextFieldAssist.cs index 63bc8d7dd0..17e51a6e19 100644 --- a/MaterialDesignThemes.Wpf/TextFieldAssist.cs +++ b/MaterialDesignThemes.Wpf/TextFieldAssist.cs @@ -11,16 +11,11 @@ public static class TextFieldAssist /// /// The text box view margin property /// - public static class TextFieldAssist - { - /// - /// The text box view margin property - /// - public static readonly DependencyProperty TextBoxViewMarginProperty = DependencyProperty.RegisterAttached( - "TextBoxViewMargin", - typeof(Thickness), - typeof(TextFieldAssist), - new FrameworkPropertyMetadata(new Thickness(double.NegativeInfinity), FrameworkPropertyMetadataOptions.Inherits, TextBoxViewMarginPropertyChangedCallback)); + public static readonly DependencyProperty TextBoxViewMarginProperty = DependencyProperty.RegisterAttached( + "TextBoxViewMargin", + typeof(Thickness), + typeof(TextFieldAssist), + new FrameworkPropertyMetadata(new Thickness(double.NegativeInfinity), FrameworkPropertyMetadataOptions.Inherits, TextBoxViewMarginPropertyChangedCallback)); /// /// Sets the text box view margin. @@ -289,12 +284,12 @@ private static void PasswordBoxOnPasswordChanged(object sender, RoutedEventArgs SetPasswordBoxCharacterCount(passwordBox, passwordBox.SecurePassword.Length); } - internal static readonly DependencyProperty PasswordBoxCharacterCountProperty = DependencyProperty.RegisterAttached( - "PasswordBoxCharacterCount", typeof(int), typeof(TextFieldAssist), new PropertyMetadata(default(int))); - internal static void SetPasswordBoxCharacterCount(DependencyObject element, int value) => element.SetValue(PasswordBoxCharacterCountProperty, value); - internal static int GetPasswordBoxCharacterCount(DependencyObject element) => (int) element.GetValue(PasswordBoxCharacterCountProperty); + internal static readonly DependencyProperty PasswordBoxCharacterCountProperty = DependencyProperty.RegisterAttached( + "PasswordBoxCharacterCount", typeof(int), typeof(TextFieldAssist), new PropertyMetadata(default(int))); + internal static void SetPasswordBoxCharacterCount(DependencyObject element, int value) => element.SetValue(PasswordBoxCharacterCountProperty, value); + internal static int GetPasswordBoxCharacterCount(DependencyObject element) => (int)element.GetValue(PasswordBoxCharacterCountProperty); - #region Methods + #region Methods private static void IncludeSpellingSuggestionsChanged(DependencyObject element, DependencyPropertyChangedEventArgs e) { @@ -452,8 +447,17 @@ private static void TextBoxViewMarginPropertyChangedCallback( return; } - #endregion + if (box.IsLoaded) + { + ApplyTextBoxViewMargin(box, (Thickness)dependencyPropertyChangedEventArgs.NewValue); + } + + box.Loaded += (sender, args) => + { + var textBox = (Control)sender; + ApplyTextBoxViewMargin(textBox, GetTextBoxViewMargin(textBox)); + }; } - #endregion +#endregion } From 7d85d890ba8d6cd963a3e25bcc87813e27ca60b2 Mon Sep 17 00:00:00 2001 From: Kevin Bost Date: Thu, 10 Aug 2023 00:04:15 -0700 Subject: [PATCH 09/14] Adding some TODOs, starting on the UI tests --- .../Converters/ColorToBrushConverter.cs | 8 ++- MainDemo.Wpf/Domain/FieldsViewModel.cs | 70 +++++++------------ MainDemo.Wpf/Fields.xaml | 8 ++- .../AutoSuggestTextBoxWithTemplate.xaml | 18 +++++ .../AutoSuggestTextBoxWithTemplate.xaml.cs | 37 ++++++++++ MaterialDesignThemes.UITests/TestBase.cs | 1 + .../AutoSuggestTextBoxTests.cs | 39 +++++++++++ MaterialDesignThemes.Wpf/AutoSuggestBox.cs | 26 +++---- .../MaterialDesignTheme.AutoSuggestBox.xaml | 2 - .../Themes/MaterialDesignTheme.Defaults.xaml | 1 - 10 files changed, 143 insertions(+), 67 deletions(-) create mode 100644 MaterialDesignThemes.UITests/Samples/AutoSuggestBoxes/AutoSuggestTextBoxWithTemplate.xaml create mode 100644 MaterialDesignThemes.UITests/Samples/AutoSuggestBoxes/AutoSuggestTextBoxWithTemplate.xaml.cs create mode 100644 MaterialDesignThemes.UITests/WPF/AutoSuggestBoxes/AutoSuggestTextBoxTests.cs diff --git a/MainDemo.Wpf/Converters/ColorToBrushConverter.cs b/MainDemo.Wpf/Converters/ColorToBrushConverter.cs index 7052879845..3c6ac31ffd 100644 --- a/MainDemo.Wpf/Converters/ColorToBrushConverter.cs +++ b/MainDemo.Wpf/Converters/ColorToBrushConverter.cs @@ -7,16 +7,18 @@ namespace MaterialDesignDemo.Converters; [ValueConversion(typeof(Color), typeof(Brush))] public class ColorToBrushConverter : IValueConverter { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { if (value is Color color) { - return new SolidColorBrush(color); + SolidColorBrush rv = new(color); + rv.Freeze(); + return rv; } return Binding.DoNothing; } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { if (value is SolidColorBrush brush) { diff --git a/MainDemo.Wpf/Domain/FieldsViewModel.cs b/MainDemo.Wpf/Domain/FieldsViewModel.cs index 8ffd88be27..414010eeec 100644 --- a/MainDemo.Wpf/Domain/FieldsViewModel.cs +++ b/MainDemo.Wpf/Domain/FieldsViewModel.cs @@ -14,10 +14,10 @@ public class FieldsViewModel : ViewModelBase private string? _text2; private ObservableCollection? _autoSuggestBox1Suggestions; private string? _autoSuggestBox1Text; - private List? _originalAutoSuggestBox1Suggestions; - private ObservableCollection>? _autoSuggestBox2Suggestions; + private readonly List? _originalAutoSuggestBox1Suggestions; + private ObservableCollection>? _autoSuggestBox2Suggestions; private string? _autoSuggestBox2Text; - private List>? _originalAutoSuggestBox2Suggestions; + private readonly List>? _originalAutoSuggestBox2Suggestions; public string? Name { @@ -81,53 +81,44 @@ public string? Password2Validated public ObservableCollection? AutoSuggestBox1Suggestions { - get { return _autoSuggestBox1Suggestions; } - set { SetProperty(ref _autoSuggestBox1Suggestions, value); } + get => _autoSuggestBox1Suggestions; + set => SetProperty(ref _autoSuggestBox1Suggestions, value); } - public ObservableCollection>? AutoSuggestBox2Suggestions + public ObservableCollection>? AutoSuggestBox2Suggestions { - get { return _autoSuggestBox2Suggestions; } - set { SetProperty(ref _autoSuggestBox2Suggestions, value); } + get => _autoSuggestBox2Suggestions; + set => SetProperty(ref _autoSuggestBox2Suggestions, value); } public string? AutoSuggestBox1Text { - get { return _autoSuggestBox1Text; } + get => _autoSuggestBox1Text; set { - if (value != _autoSuggestBox1Text) + if (SetProperty(ref _autoSuggestBox1Text, value) && + _originalAutoSuggestBox1Suggestions != null && value != null) { - SetProperty(ref _autoSuggestBox1Text, value); - if (_originalAutoSuggestBox1Suggestions != null && value != null) - { - var searchResult = _originalAutoSuggestBox1Suggestions.Where(x => IsMatch(x, value)); - AutoSuggestBox1Suggestions = new ObservableCollection(searchResult); - } + var searchResult = _originalAutoSuggestBox1Suggestions.Where(x => IsMatch(x, value)); + AutoSuggestBox1Suggestions = new ObservableCollection(searchResult); } } } public string? AutoSuggestBox2Text { - get { return _autoSuggestBox2Text; } + get => _autoSuggestBox2Text; set { - if (value != _autoSuggestBox2Text) + if (SetProperty(ref _autoSuggestBox2Text, value) && + _originalAutoSuggestBox2Suggestions != null && value != null) { - SetProperty(ref _autoSuggestBox2Text, value); - if (_originalAutoSuggestBox2Suggestions != null && value != null) - { - var searchResult = _originalAutoSuggestBox2Suggestions.Where(x => IsMatch(x.Key, value)); - AutoSuggestBox2Suggestions = new ObservableCollection>(searchResult); - } + var searchResult = _originalAutoSuggestBox2Suggestions.Where(x => IsMatch(x.Key, value)); + AutoSuggestBox2Suggestions = new ObservableCollection>(searchResult); } } } - - - public ICommand SetPassword1FromViewModelCommand { get; } public ICommand SetPassword2FromViewModelCommand { get; } @@ -136,40 +127,33 @@ public FieldsViewModel() SetPassword1FromViewModelCommand = new AnotherCommandImplementation(_ => Password1 = "Set from ViewModel!"); SetPassword2FromViewModelCommand = new AnotherCommandImplementation(_ => Password2 = "Set from ViewModel!"); - InitializeData(); - } - private void InitializeData() - { _originalAutoSuggestBox1Suggestions = new List() { "Burger", "Fries", "Shake", "Lettuce" }; - _originalAutoSuggestBox2Suggestions = new List>(GetColors()); + _originalAutoSuggestBox2Suggestions = new List>(GetColors()); AutoSuggestBox1Suggestions = new ObservableCollection(_originalAutoSuggestBox1Suggestions); } - private bool IsMatch(string item, string currentText) + private static bool IsMatch(string item, string currentText) { - return item.ToLower().Contains(currentText.ToLower()); +#if NET6_0_OR_GREATER + return item.Contains(currentText, StringComparison.OrdinalIgnoreCase); +#else + return item.IndexOf(currentText, StringComparison.OrdinalIgnoreCase) >= 0; +#endif } - private IEnumerable> GetColors() + private static IEnumerable> GetColors() { return typeof(Colors) .GetProperties() .Where(prop => typeof(Color).IsAssignableFrom(prop.PropertyType)) .Select(prop => - new KeyValuePair(prop.Name, GenerateColorBrush(prop.GetValue(null)))); - } - - private SolidColorBrush GenerateColorBrush(object? prop) - { - if (prop is Color color) - return new SolidColorBrush(color); - return new SolidColorBrush(Colors.White); + new KeyValuePair(prop.Name, (Color)prop.GetValue(null)!)); } } diff --git a/MainDemo.Wpf/Fields.xaml b/MainDemo.Wpf/Fields.xaml index 2780ce9d30..641f300430 100644 --- a/MainDemo.Wpf/Fields.xaml +++ b/MainDemo.Wpf/Fields.xaml @@ -2,6 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:converters="clr-namespace:MaterialDesignDemo.Converters" xmlns:domain="clr-namespace:MaterialDesignDemo.Domain" xmlns:domain1="clr-namespace:MaterialDesignDemo.Domain" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" @@ -14,6 +15,8 @@ mc:Ignorable="d"> + +