The most common autocomplete in MVVM is combobox, you could refer to the solution here.
You can also refer to the code below to create it as a user control and then use in mvvm.
MainWindow.xaml:
<TextBox VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="SuggestionBox" Width="200" />
MainWindow.xaml.cs:
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace AutoCompleteTextBox
{
public partial class MainWindow : Window
{
private static readonly string[] SuggestionValues = {
"English",
"Chinese",
"Franch",
"Russian",
"Japanese"
};
public MainWindow()
{
InitializeComponent();
SuggestionBox.TextChanged += SuggestionBoxOnTextChanged;
}
private string _currentInput = "";
private string _currentSuggestion = "";
private string _currentText = "";
private int _selectionStart;
private int _selectionLength;
private void SuggestionBoxOnTextChanged(object sender, TextChangedEventArgs e)
{
var input = SuggestionBox.Text;
if (input.Length > _currentInput.Length && input != _currentSuggestion)
{
_currentSuggestion = SuggestionValues.FirstOrDefault(x => x.StartsWith(input));
if (_currentSuggestion != null)
{
_currentText = _currentSuggestion;
_selectionStart = input.Length;
_selectionLength = _currentSuggestion.Length - input.Length;
SuggestionBox.Text = _currentText;
SuggestionBox.Select(_selectionStart, _selectionLength);
}
}
_currentInput = input;
}
}
}
The result:
Update:
MainWindow.xaml:
<Grid>
<ComboBox x:Name="Cmb" IsTextSearchEnabled="False" IsEditable="True"
ItemsSource="{Binding DataSource}" Width="120" Height="50" IsDropDownOpen="True"
StaysOpenOnEdit="True" KeyUp="Cmb_KeyUp" />
</Grid>
MainWindow.xaml.cs:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
namespace ComboboxAutoComplete
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
FilterViewModel vm = new FilterViewModel();
this.DataContext = vm;
}
private void Cmb_KeyUp(object sender, KeyEventArgs e)
{
CollectionView itemsViewOriginal = (CollectionView)CollectionViewSource.GetDefaultView(Cmb.ItemsSource);
itemsViewOriginal.Filter = ((o) =>
{
if (String.IsNullOrEmpty(Cmb.Text)) return true;
else
{
if (((string)o).Contains(Cmb.Text)) return true;
else return false;
}
});
itemsViewOriginal.Refresh();
}
}
public class FilterViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> dataSource;
public ObservableCollection<string> DataSource
{
get
{
return dataSource;
}
set { dataSource = value; OnPropertyChanged("ListOfCountry"); }
}
public FilterViewModel()
{
dataSource = new ObservableCollection<string>();
dataSource.Add("Beijing");
dataSource.Add("New York");
dataSource.Add("Paris");
dataSource.Add("Shanghai");
dataSource.Add("London");
dataSource.Add("Nanjing");
}
public event PropertyChangedEventHandler PropertyChanged;
internal void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
Update 2 :
MainWindow.xaml:
<Grid x:Name="grid">
<ComboBox x:Name="Cmb" IsTextSearchEnabled="False" IsEditable="True"
ItemsSource="{Binding DataSource}" Width="120" Height="50" IsDropDownOpen="True"
StaysOpenOnEdit="True" local:MyComboBox.IsFilterOnAutocompleteEnabled="True" />
</Grid>
CodeBehind:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace ExecuteFunctionOnSpecialTime
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
FilterViewModel vm = new FilterViewModel();
this.DataContext = vm;
}
}
public class FilterViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> dataSource;
public ObservableCollection<string> DataSource
{
get
{
return dataSource;
}
set { dataSource = value; OnPropertyChanged("ListOfCountry"); }
}
public FilterViewModel()
{
dataSource = new ObservableCollection<string>();
dataSource.Add("Beijing");
dataSource.Add("New York");
dataSource.Add("Paris");
dataSource.Add("Shanghai");
dataSource.Add("London");
dataSource.Add("Nanjing");
}
public event PropertyChangedEventHandler PropertyChanged;
internal void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public static class Extensions
{
public static bool TryFindVisualChildElement<TChild>(this DependencyObject parent, out TChild resultElement)
where TChild : DependencyObject
{
resultElement = null;
if (parent is Popup popup)
{
parent = popup.Child;
if (parent == null)
{
return false;
}
}
for (var childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); childIndex++)
{
DependencyObject childElement = VisualTreeHelper.GetChild(parent, childIndex);
if (childElement is TChild child)
{
resultElement = child;
return true;
}
if (childElement.TryFindVisualChildElement(out resultElement))
{
return true;
}
}
return false;
}
}
class MyComboBox : DependencyObject
{
public static readonly DependencyProperty IsFilterOnAutocompleteEnabledProperty =
DependencyProperty.RegisterAttached(
"IsFilterOnAutocompleteEnabled",
typeof(bool),
typeof(MyComboBox),
new PropertyMetadata(default(bool), MyComboBox.OnIsFilterOnAutocompleteEnabledChanged));
public static void SetIsFilterOnAutocompleteEnabled(DependencyObject attachingElement, bool value) =>
attachingElement.SetValue(MyComboBox.IsFilterOnAutocompleteEnabledProperty, value);
public static bool GetIsFilterOnAutocompleteEnabled(DependencyObject attachingElement) =>
(bool)attachingElement.GetValue(MyComboBox.IsFilterOnAutocompleteEnabledProperty);
// Use hash tables for faster lookup
private static Dictionary<TextBox, ComboBox> TextBoxComboBoxMap { get; }
private static Dictionary<TextBox, int> TextBoxSelectionStartMap { get; }
private static Dictionary<ComboBox, TextBox> ComboBoxTextBoxMap { get; }
private static bool IsNavigationKeyPressed { get; set; }
static MyComboBox()
{
MyComboBox.TextBoxComboBoxMap = new Dictionary<TextBox, ComboBox>();
MyComboBox.TextBoxSelectionStartMap = new Dictionary<TextBox, int>();
MyComboBox.ComboBoxTextBoxMap = new Dictionary<ComboBox, TextBox>();
}
private static void OnIsFilterOnAutocompleteEnabledChanged(
DependencyObject attachingElement,
DependencyPropertyChangedEventArgs e)
{
if (!(attachingElement is ComboBox comboBox && comboBox.IsEditable))
{
return;
}
if (!(bool)e.NewValue)
{
MyComboBox.DisableAutocompleteFilter(comboBox);
return;
}
if (!comboBox.IsLoaded)
{
comboBox.Loaded += MyComboBox.EnableAutocompleteFilterOnComboBoxLoaded;
return;
}
MyComboBox.EnableAutocompleteFilter(comboBox);
}
private static async void FilterOnTextInput(object sender, TextChangedEventArgs e)
{
await Application.Current.Dispatcher.InvokeAsync(
() =>
{
if (MyComboBox.IsNavigationKeyPressed)
{
return;
}
var textBox = sender as TextBox;
int textBoxSelectionStart = textBox.SelectionStart;
MyComboBox.TextBoxSelectionStartMap[textBox] = textBoxSelectionStart;
string changedTextOnAutocomplete = textBox.Text.Substring(0, textBoxSelectionStart);
if (MyComboBox.TextBoxComboBoxMap.TryGetValue(
textBox, out ComboBox comboBox))
{
comboBox.Items.Filter = item => item.ToString().StartsWith(
changedTextOnAutocomplete,
StringComparison.OrdinalIgnoreCase);
}
},
DispatcherPriority.Background);
}
private static async void HandleKeyDownWhileFiltering(object sender, KeyEventArgs e)
{
var comboBox = sender as ComboBox;
if (!MyComboBox.ComboBoxTextBoxMap.TryGetValue(comboBox, out TextBox textBox))
{
return;
}
switch (e.Key)
{
case Key.Down
when comboBox.Items.CurrentPosition < comboBox.Items.Count - 1
&& comboBox.Items.MoveCurrentToNext():
case Key.Up
when comboBox.Items.CurrentPosition > 0
&& comboBox.Items.MoveCurrentToPrevious():
{
MyComboBox.IsNavigationKeyPressed = true;
await Application.Current.Dispatcher.InvokeAsync(
() =>
{
MyComboBox.SelectCurrentItem(textBox, comboBox);
MyComboBox.IsNavigationKeyPressed = false;
},
DispatcherPriority.ContextIdle);
break;
}
}
}
private static void SelectCurrentItem(TextBox textBox, ComboBox comboBox)
{
comboBox.SelectedItem = comboBox.Items.CurrentItem;
if (MyComboBox.TextBoxSelectionStartMap.TryGetValue(textBox, out int selectionStart))
{
textBox.SelectionStart = selectionStart;
}
}
private static void EnableAutocompleteFilterOnComboBoxLoaded(object sender, RoutedEventArgs e)
{
var comboBox = sender as ComboBox;
MyComboBox.EnableAutocompleteFilter(comboBox);
}
private static void EnableAutocompleteFilter(ComboBox comboBox)
{
if (comboBox.TryFindVisualChildElement(out TextBox editTextBox))
{
MyComboBox.TextBoxComboBoxMap.Add(editTextBox, comboBox);
MyComboBox.ComboBoxTextBoxMap.Add(comboBox, editTextBox);
editTextBox.TextChanged += MyComboBox.FilterOnTextInput;
comboBox.AddHandler(UIElement.PreviewKeyDownEvent, new KeyEventHandler(HandleKeyDownWhileFiltering), true);
}
}
private static void DisableAutocompleteFilter(ComboBox comboBox)
{
if (comboBox.TryFindVisualChildElement(out TextBox editTextBox))
{
MyComboBox.TextBoxComboBoxMap.Remove(editTextBox);
editTextBox.TextChanged -= MyComboBox.FilterOnTextInput;
}
}
}
}
The result:
If the response is helpful, please click "Accept Answer" and upvote it.
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.