Skip to content

Commit 9be7c3d

Browse files
[Feature] Add AutoSuggestion option for TextBox (#3207)
* Implement the AutoSuggestion in TextBox by using TextFieldAssist.AutoSuggestionEnabled & ItemsSource * Fixing the code warnings about the null values Add -1 possibility in DecrementSelection Add Enabled check in the ItemsSource callback method in TextFieldAssist * Fix warnings in the FieldsViewModel 0 Warning(s) * Add underscore prefix to the private properties * Reset changes of the AutoSuggestion in the TextFieldAssist * creation of control AutoSuggestBox Based on TextBox with custom Template and new parameters, * Fix Warnings * Rebase build fixing * Adding some TODOs, starting on the UI tests * Remove AutoSuggestBox from properties * Implement the tests: - Validates these elements are found - Validate other items are hidden - Validate that the current text is the same as the selected item * WIP showing sample test for ICollectionView * Use ICollectionView for keyboard navigation and ensure completion of unit testing for CanFilterItems_WithCollectionView_FiltersSuggestions. * rename key --------- Co-authored-by: Kevin Bost <[email protected]>
1 parent 3e0d707 commit 9be7c3d

19 files changed

+1377
-78
lines changed

MainDemo.Wpf/Converters/ColorToBrushConverter.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ namespace MaterialDesignDemo.Converters;
77
[ValueConversion(typeof(Color), typeof(Brush))]
88
public class ColorToBrushConverter : IValueConverter
99
{
10-
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
10+
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
1111
{
1212
if (value is Color color)
1313
{
14-
return new SolidColorBrush(color);
14+
SolidColorBrush rv = new(color);
15+
rv.Freeze();
16+
return rv;
1517
}
1618
return Binding.DoNothing;
1719
}
1820

19-
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
21+
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
2022
{
2123
if (value is SolidColorBrush brush)
2224
{

MainDemo.Wpf/Domain/FieldsViewModel.cs

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
namespace MaterialDesignDemo.Domain;
1+
using System.Collections.ObjectModel;
2+
using System.Windows.Media;
23

4+
namespace MaterialDesignDemo.Domain;
35
public class FieldsViewModel : ViewModelBase
46
{
57
private string? _name;
@@ -10,6 +12,12 @@ public class FieldsViewModel : ViewModelBase
1012
private string? _password2Validated = "pre-filled";
1113
private string? _text1;
1214
private string? _text2;
15+
private ObservableCollection<string>? _autoSuggestBox1Suggestions;
16+
private string? _autoSuggestBox1Text;
17+
private readonly List<string>? _originalAutoSuggestBox1Suggestions;
18+
private ObservableCollection<KeyValuePair<string, Color>>? _autoSuggestBox2Suggestions;
19+
private string? _autoSuggestBox2Text;
20+
private readonly List<KeyValuePair<string, Color>>? _originalAutoSuggestBox2Suggestions;
1321

1422
public string? Name
1523
{
@@ -71,13 +79,81 @@ public string? Password2Validated
7179

7280
public FieldsTestObject TestObject => new() { Name = "Mr. Test" };
7381

82+
public ObservableCollection<string>? AutoSuggestBox1Suggestions
83+
{
84+
get => _autoSuggestBox1Suggestions;
85+
set => SetProperty(ref _autoSuggestBox1Suggestions, value);
86+
}
87+
88+
public ObservableCollection<KeyValuePair<string, Color>>? AutoSuggestBox2Suggestions
89+
{
90+
get => _autoSuggestBox2Suggestions;
91+
set => SetProperty(ref _autoSuggestBox2Suggestions, value);
92+
}
93+
94+
public string? AutoSuggestBox1Text
95+
{
96+
get => _autoSuggestBox1Text;
97+
set
98+
{
99+
if (SetProperty(ref _autoSuggestBox1Text, value) &&
100+
_originalAutoSuggestBox1Suggestions != null && value != null)
101+
{
102+
var searchResult = _originalAutoSuggestBox1Suggestions.Where(x => IsMatch(x, value));
103+
AutoSuggestBox1Suggestions = new ObservableCollection<string>(searchResult);
104+
}
105+
}
106+
}
107+
108+
public string? AutoSuggestBox2Text
109+
{
110+
get => _autoSuggestBox2Text;
111+
set
112+
{
113+
if (SetProperty(ref _autoSuggestBox2Text, value) &&
114+
_originalAutoSuggestBox2Suggestions != null && value != null)
115+
{
116+
var searchResult = _originalAutoSuggestBox2Suggestions.Where(x => IsMatch(x.Key, value));
117+
AutoSuggestBox2Suggestions = new ObservableCollection<KeyValuePair<string, Color>>(searchResult);
118+
}
119+
}
120+
}
121+
74122
public ICommand SetPassword1FromViewModelCommand { get; }
75123
public ICommand SetPassword2FromViewModelCommand { get; }
76124

77125
public FieldsViewModel()
78126
{
79127
SetPassword1FromViewModelCommand = new AnotherCommandImplementation(_ => Password1 = "Set from ViewModel!");
80128
SetPassword2FromViewModelCommand = new AnotherCommandImplementation(_ => Password2 = "Set from ViewModel!");
129+
130+
_originalAutoSuggestBox1Suggestions = new List<string>()
131+
{
132+
"Burger", "Fries", "Shake", "Lettuce"
133+
};
134+
135+
_originalAutoSuggestBox2Suggestions = new List<KeyValuePair<string, Color>>(GetColors());
136+
137+
AutoSuggestBox1Suggestions = new ObservableCollection<string>(_originalAutoSuggestBox1Suggestions);
138+
}
139+
140+
private static bool IsMatch(string item, string currentText)
141+
{
142+
#if NET6_0_OR_GREATER
143+
return item.Contains(currentText, StringComparison.OrdinalIgnoreCase);
144+
#else
145+
return item.IndexOf(currentText, StringComparison.OrdinalIgnoreCase) >= 0;
146+
#endif
147+
}
148+
149+
private static IEnumerable<KeyValuePair<string, Color>> GetColors()
150+
{
151+
return typeof(Colors)
152+
.GetProperties()
153+
.Where(prop =>
154+
typeof(Color).IsAssignableFrom(prop.PropertyType))
155+
.Select(prop =>
156+
new KeyValuePair<string, Color>(prop.Name, (Color)prop.GetValue(null)!));
81157
}
82158
}
83159

MainDemo.Wpf/Fields.xaml

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<UserControl x:Class="MaterialDesignDemo.Fields"
22
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:converters="clr-namespace:MaterialDesignDemo.Converters"
45
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
56
xmlns:domain="clr-namespace:MaterialDesignDemo.Domain"
67
xmlns:domain1="clr-namespace:MaterialDesignDemo.Domain"
@@ -14,6 +15,8 @@
1415
mc:Ignorable="d">
1516

1617
<UserControl.Resources>
18+
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter" />
19+
1720
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource MaterialDesignTextBox}">
1821
<Setter Property="Margin" Value="0,8" />
1922
<Setter Property="VerticalAlignment" Value="Center" />
@@ -757,5 +760,125 @@
757760
</smtx:XamlDisplay>
758761
</StackPanel>
759762
</Grid>
763+
764+
<Grid Margin="0,48,0,0"
765+
HorizontalAlignment="Left"
766+
VerticalAlignment="Top">
767+
<Grid.ColumnDefinitions>
768+
<ColumnDefinition Width="Auto" />
769+
<ColumnDefinition Width="Auto" />
770+
<ColumnDefinition Width="Auto" />
771+
<ColumnDefinition Width="Auto" />
772+
</Grid.ColumnDefinitions>
773+
<Grid.RowDefinitions>
774+
<RowDefinition Height="Auto" />
775+
<RowDefinition Height="Auto" />
776+
<RowDefinition Height="Auto" />
777+
</Grid.RowDefinitions>
778+
<TextBlock Grid.Row="0"
779+
Grid.Column="0"
780+
Grid.ColumnSpan="3"
781+
Margin="0,0,0,10"
782+
VerticalAlignment="Center"
783+
Style="{StaticResource MaterialDesignHeadline5TextBlock}"
784+
Text="AutoSuggestBox" />
785+
<TextBlock Grid.Row="1"
786+
Grid.Column="0"
787+
VerticalAlignment="Center"
788+
Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
789+
Text="Simple source list" />
790+
791+
<smtx:XamlDisplay Grid.Row="2"
792+
Grid.Column="0"
793+
UniqueKey="fields_autosuggestion_1">
794+
<materialDesign:AutoSuggestBox VerticalAlignment="Bottom"
795+
Suggestions="{Binding AutoSuggestBox1Suggestions}"
796+
Text="{Binding AutoSuggestBox1Text, UpdateSourceTrigger=PropertyChanged}" />
797+
</smtx:XamlDisplay>
798+
799+
<TextBlock Grid.Row="1"
800+
Grid.Column="1"
801+
Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
802+
Text="AutoSuggestBox with ItemTemplate" />
803+
<smtx:XamlDisplay Grid.Row="2"
804+
Grid.Column="1"
805+
UniqueKey="fields_autosuggestion_2">
806+
<materialDesign:AutoSuggestBox materialDesign:HintAssist.HelperText="Select color"
807+
materialDesign:HintAssist.Hint="Color"
808+
materialDesign:TextFieldAssist.HasClearButton="True"
809+
DropDownElevation="Dp0"
810+
Suggestions="{Binding AutoSuggestBox2Suggestions}"
811+
Text="{Binding AutoSuggestBox2Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
812+
ValueMember="Key">
813+
<materialDesign:AutoSuggestBox.ItemTemplate>
814+
<DataTemplate>
815+
<DockPanel>
816+
<Border Width="20"
817+
Height="20"
818+
Background="{Binding Value, Converter={StaticResource ColorToBrushConverter}}"
819+
CornerRadius="10" />
820+
<TextBlock Margin="10,0,0,0" Text="{Binding Key}" />
821+
</DockPanel>
822+
</DataTemplate>
823+
</materialDesign:AutoSuggestBox.ItemTemplate>
824+
</materialDesign:AutoSuggestBox>
825+
</smtx:XamlDisplay>
826+
827+
<TextBlock Grid.Row="1"
828+
Grid.Column="2"
829+
Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
830+
Text="Filled AutoSuggestBox" />
831+
<smtx:XamlDisplay Grid.Row="2"
832+
Grid.Column="2"
833+
UniqueKey="fields_autosuggestion_3">
834+
<materialDesign:AutoSuggestBox materialDesign:HintAssist.Hint="Color"
835+
materialDesign:TextFieldAssist.HasClearButton="True"
836+
DropDownElevation="Dp0"
837+
Style="{StaticResource MaterialDesignFilledAutoSuggestBox}"
838+
Suggestions="{Binding AutoSuggestBox2Suggestions}"
839+
Text="{Binding AutoSuggestBox2Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
840+
ValueMember="Key">
841+
<materialDesign:AutoSuggestBox.ItemTemplate>
842+
<DataTemplate>
843+
<DockPanel>
844+
<Border Width="20"
845+
Height="20"
846+
Background="{Binding Value, Converter={StaticResource ColorToBrushConverter}}"
847+
CornerRadius="10" />
848+
<TextBlock Margin="10,0,0,0" Text="{Binding Key}" />
849+
</DockPanel>
850+
</DataTemplate>
851+
</materialDesign:AutoSuggestBox.ItemTemplate>
852+
</materialDesign:AutoSuggestBox>
853+
</smtx:XamlDisplay>
854+
855+
<TextBlock Grid.Row="1"
856+
Grid.Column="2"
857+
Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
858+
Text="Outlined AutoSuggestBox" />
859+
<smtx:XamlDisplay Grid.Row="2"
860+
Grid.Column="2"
861+
UniqueKey="fields_autosuggestion_4">
862+
<materialDesign:AutoSuggestBox materialDesign:HintAssist.Hint="Color"
863+
materialDesign:TextFieldAssist.HasClearButton="True"
864+
DropDownElevation="Dp0"
865+
Style="{StaticResource MaterialDesignOutlinedAutoSuggestBox}"
866+
Suggestions="{Binding AutoSuggestBox2Suggestions}"
867+
Text="{Binding AutoSuggestBox2Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
868+
ValueMember="Key">
869+
<materialDesign:AutoSuggestBox.ItemTemplate>
870+
<DataTemplate>
871+
<DockPanel>
872+
<Border Width="20"
873+
Height="20"
874+
Background="{Binding Value, Converter={StaticResource ColorToBrushConverter}}"
875+
CornerRadius="10" />
876+
<TextBlock Margin="10,0,0,0" Text="{Binding Key}" />
877+
</DockPanel>
878+
</DataTemplate>
879+
</materialDesign:AutoSuggestBox.ItemTemplate>
880+
</materialDesign:AutoSuggestBox>
881+
</smtx:XamlDisplay>
882+
</Grid>
760883
</StackPanel>
761884
</UserControl>

MainDemo.Wpf/Fields.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Windows.Navigation;
1+
using System.Windows.Navigation;
22
using MaterialDesignDemo.Domain;
33

44
namespace MaterialDesignDemo;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<UserControl x:Class="MaterialDesignThemes.UITests.Samples.AutoSuggestBoxes.AutoSuggestTextBoxWithCollectionView"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6+
xmlns:local="clr-namespace:MaterialDesignThemes.UITests.Samples.AutoSuggestBoxes"
7+
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
8+
mc:Ignorable="d"
9+
d:DataContext="{d:DesignInstance Type={x:Type local:AutoSuggestTextBoxWithCollectionViewViewModel}}"
10+
d:DesignHeight="450" d:DesignWidth="800">
11+
<!-- TODO: Selction Similar to SelectedItem="{Binding Suggestions/}" perhaps needing IsSynchronizedWithCurrentItem="True" -->
12+
<StackPanel>
13+
<materialDesign:AutoSuggestBox
14+
materialDesign:HintAssist.HelperText="Name"
15+
materialDesign:TextFieldAssist.HasClearButton="True"
16+
Text="{Binding AutoSuggestText, UpdateSourceTrigger=PropertyChanged}"
17+
Suggestions="{Binding Suggestions}">
18+
</materialDesign:AutoSuggestBox>
19+
<!--
20+
<ComboBox ItemsSource="{Binding Suggestions}"
21+
Margin="0,10"
22+
IsSynchronizedWithCurrentItem="True"/>
23+
<ListBox ItemsSource="{Binding Suggestions}"
24+
IsSynchronizedWithCurrentItem="True"/>
25+
-->
26+
27+
</StackPanel>
28+
</UserControl>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using CommunityToolkit.Mvvm.ComponentModel;
2+
using System.Collections.ObjectModel;
3+
using System.ComponentModel;
4+
using System.Windows.Data;
5+
6+
namespace MaterialDesignThemes.UITests.Samples.AutoSuggestBoxes;
7+
8+
/// <summary>
9+
/// Interaction logic for AutoSuggestTextBoxWithCollectionView.xaml
10+
/// </summary>
11+
public partial class AutoSuggestTextBoxWithCollectionView
12+
{
13+
public AutoSuggestTextBoxWithCollectionView()
14+
{
15+
DataContext = new AutoSuggestTextBoxWithCollectionViewViewModel();
16+
InitializeComponent();
17+
}
18+
}
19+
20+
public partial class AutoSuggestTextBoxWithCollectionViewViewModel : ObservableObject
21+
{
22+
private ObservableCollection<string> BaseSuggestions { get; }
23+
24+
public ICollectionView Suggestions { get; }
25+
26+
[ObservableProperty]
27+
private string? _autoSuggestText;
28+
29+
partial void OnAutoSuggestTextChanged(string? oldValue, string? newValue)
30+
{
31+
if (!string.IsNullOrWhiteSpace(newValue))
32+
{
33+
Suggestions.Filter = x => IsMatch((string)x, newValue);
34+
}
35+
else
36+
{
37+
Suggestions.Filter = null;
38+
}
39+
base.OnPropertyChanged(nameof(Suggestions));
40+
}
41+
42+
public AutoSuggestTextBoxWithCollectionViewViewModel()
43+
{
44+
BaseSuggestions = new()
45+
{
46+
"Apples",
47+
"Bananas",
48+
"Beans",
49+
"Mtn Dew",
50+
"Orange",
51+
};
52+
Suggestions = CollectionViewSource.GetDefaultView(BaseSuggestions);
53+
}
54+
55+
private static bool IsMatch(string item, string currentText)
56+
{
57+
#if NET6_0_OR_GREATER
58+
return item.Contains(currentText, StringComparison.OrdinalIgnoreCase);
59+
#else
60+
return item.IndexOf(currentText, StringComparison.OrdinalIgnoreCase) >= 0;
61+
#endif
62+
}
63+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<UserControl x:Class="MaterialDesignThemes.UITests.Samples.AutoSuggestTextBoxes.AutoSuggestTextBoxWithTemplate"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6+
xmlns:local="clr-namespace:MaterialDesignThemes.UITests.Samples.AutoSuggestTextBoxes"
7+
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
8+
d:DataContext="{d:DesignInstance Type=local:AutoSuggestTextBoxWithTemplateViewModel}"
9+
mc:Ignorable="d"
10+
d:DesignHeight="450" d:DesignWidth="800">
11+
<!-- TODO SELECTED ITEM -->
12+
<materialDesign:AutoSuggestBox
13+
materialDesign:HintAssist.HelperText="Name"
14+
materialDesign:TextFieldAssist.HasClearButton="True"
15+
Text="{Binding AutoSuggestText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
16+
Suggestions="{Binding Suggestions}"
17+
ValueMember="Name"
18+
DisplayMember="Name">
19+
</materialDesign:AutoSuggestBox>
20+
</UserControl>

0 commit comments

Comments
 (0)