The WPF ComboBox lacks the auto-complete feature and in this article, we will develop our own WPF user control that resembles the combobox, and supports auto-complete.
Note: There are multiple ways of adding the auto-complete feature to the ComboBox control, like extending the existing ComboBox and then implementing the auto-complete feature. However for this article, we are creating our own user control that resembles the ComboBox, but with its own look and feel. Feel free to change the code to suit your requirement.
Step 1: Create a WPF application and add a user control to it. Name this as ‘AutoCompleteTextBox.xaml’.
Step 2: Write the following Xaml code in it:
<UserControl x:Class="WPF_Combobox_AutoComplete.AutoCompleteTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="140" d:DesignWidth="248" x:Name="rootControl">
<Grid Height="120" Width="247">
<TextBox Height="23" HorizontalAlignment="Left" Margin="88,49,0,0"
Name="txtAutoComplete" VerticalAlignment="Top" Width="120"
Text="{Binding ElementName=rootControl,Path=TextToChanged}"/>
<ListBox Height="100" HorizontalAlignment="Left"
Margin="88,71,0,0" Name="lstData"
VerticalAlignment="Top" Width="120"
ItemsSource="{Binding ElementName=rootControl,Path=DataSource}"
UseLayoutRounding="True" IsTextSearchEnabled="False" />
</Grid>
</UserControl>
The ListBox ‘lstData’ in the above code will be used to display the ‘possible filtered names’ based on what the user has already entered in the TextBox txtAutoComplete.
Step 3: In this user control, I have added the following Dependency Properties.
C#
public string TextToChanged
{
get { return (string)GetValue(TextToChangedProperty); }
set { SetValue(TextToChangedProperty, value); }
}
// Using a DependencyProperty as the backing store for TextToChanged. // This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextToChangedProperty =
DependencyProperty.Register("TextToChanged", typeof(string), typeof(AutoCompleteTextBox));
public IEnumerable DataSource
{
get { return (IEnumerable)GetValue(DataSourceProperty); }
set { SetValue(DataSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for DataSource. // This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataSourceProperty =
DependencyProperty.Register("DataSource", typeof(IEnumerable), typeof(AutoCompleteTextBox));
VB.NET (Converted Code)
Public Property TextToChanged() As String
Get
Return CStr(GetValue(TextToChangedProperty))
End Get
Set(ByVal value As String)
SetValue(TextToChangedProperty, value)
End Set
End Property
' Using a DependencyProperty as the backing store for TextToChanged. // This enables animation, styling, binding, etc...
Public Shared ReadOnly TextToChangedProperty As DependencyProperty = DependencyProperty.Register("TextToChanged", GetType(String), GetType(AutoCompleteTextBox))
Public Property DataSource() As IEnumerable
Get
Return CType(GetValue(DataSourceProperty), IEnumerable)
End Get
Set(ByVal value As IEnumerable)
SetValue(DataSourceProperty, value)
End Set
End Property
' Using a DependencyProperty as the backing store for DataSource. // This enables animation, styling, binding, etc...
Public Shared ReadOnly DataSourceProperty As DependencyProperty = DependencyProperty.Register("DataSource", GetType(IEnumerable), GetType(AutoCompleteTextBox))
The property ‘TextToChanged’ is used to store the text entered in the TextBox ‘txtAutoComplete’. The property ‘DataSource’ is used to bind with the ListBox ‘lstData’. This property holds all the values to be filtered.
Step 4: The following Routed Event is declared in the user control which will be raised when the TextChanged event of the TextBox ‘txtAutoComplete’ is handled.
C#
public event RoutedEventHandler TextBoxContentChanged
{
add { AddHandler(TextBoxContentChangedEvent, value); }
remove { RemoveHandler(TextBoxContentChangedEvent, value); }
}
public static readonly RoutedEvent TextBoxContentChangedEvent =
EventManager.RegisterRoutedEvent("TextBoxContentChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AutoCompleteTextBox));
VB.NET (Converted Code)
Public Custom Event TextBoxContentChanged As RoutedEventHandler
AddHandler(ByVal value As RoutedEventHandler)
MyBase.AddHandler(TextBoxContentChangedEvent, value)
End AddHandler
RemoveHandler(ByVal value As RoutedEventHandler)
MyBase.RemoveHandler(TextBoxContentChangedEvent, value)
End RemoveHandler
RaiseEvent(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
End RaiseEvent
End Event
Public Shared ReadOnly TextBoxContentChangedEvent As RoutedEvent = EventManager.RegisterRoutedEvent("TextBoxContentChanged", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(AutoCompleteTextBox))
Step 5: The above routed event is raised in the TextChanged event of the ‘txtAutoComplete’ shown below. Also the text entered in ‘txtAutoComplete’ TextBox is stored in the dependency property ‘TextToChanged’ as shown below:
C#
public AutoCompleteTextBox()
{
InitializeComponent();
txtAutoComplete.TextChanged += new TextChangedEventHandler(txtAutoComplete_TextChanged);
}
void txtAutoComplete_TextChanged(object sender, TextChangedEventArgs e)
{
e.Handled = true;
TextToChanged = txtAutoComplete.Text;
RoutedEventArgs args = new RoutedEventArgs(TextBoxContentChangedEvent);
RaiseEvent(args);
}
VB.NET (Converted Code)
Public Sub New()
InitializeComponent()
AddHandler txtAutoComplete.TextChanged, AddressOf txtAutoComplete_TextChanged
End Sub
Private Sub txtAutoComplete_TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
e.Handled = True
TextToChanged = txtAutoComplete.Text
Dim args As New RoutedEventArgs(TextBoxContentChangedEvent)
MyBase.RaiseEvent(args)
End Sub
Step 6: Open MainPage.xaml and register the user control created in the previous steps, as shown below:
<Window x:Class="WPF_Combobox_AutoComplete.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WPF_Combobox_AutoComplete"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<src:AutoCompleteTextBox x:Name="txtAutoCompleteTextBox" TextBoxContentChanged="txtAutoCompleteTextBox_TextBoxContentChanged"></src:AutoCompleteTextBox>
</Grid>
</Window>
Step 7: In the MainPage.Xaml.cs or .vb, I have written the following code in ‘Windows_Loaded’ and the method ‘ListData()’ where the List<string> object is loaded with some default strings.
C#
List<string> lstNames;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
lstNames = new List<string>();
}
private void ListData()
{
lstNames.Add("");
lstNames.Add("Ajay");
lstNames.Add("Rahul");
lstNames.Add("Rajesh");
lstNames.Add("Ravi");
lstNames.Add("Suprotim");
lstNames.Add("Ajit");
lstNames.Add("Suraj");
lstNames.Add("Vikram");
lstNames.Add("Vikas");
lstNames.Add("Pravin");
lstNames.Add("Suprabhat");
txtAutoCompleteTextBox.DataSource = lstNames; ;
}
VB.NET (Converted Code)
Private lstNames As List(Of String)
Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
lstNames = New List(Of String)()
End Sub
Private Sub ListData()
lstNames.Add("")
lstNames.Add("Ajay")
lstNames.Add("Rahul")
lstNames.Add("Rajesh")
lstNames.Add("Ravi")
lstNames.Add("Suprotim")
lstNames.Add("Ajit")
lstNames.Add("Suraj")
lstNames.Add("Vikram")
lstNames.Add("Vikas")
lstNames.Add("Pravin")
lstNames.Add("Suprabhat")
txtAutoCompleteTextBox.DataSource = lstNames
End Sub
Step 8: In the MainPage.Xaml.cs or.vb, the event ‘TextBoxContentChanged’ event declared in the user control, is handled with the following code:
C#
private void txtAutoCompleteTextBox_TextBoxContentChanged(object sender, RoutedEventArgs e)
{
ListData();
string s = txtAutoCompleteTextBox.TextToChanged;
var filter = (from name in lstNames
where (name.Contains(s))
select name).ToList<string>();
foreach (var item in filter)
{
if (item.Equals(s))
{
txtAutoCompleteTextBox.TextToChanged = s;
}
}
txtAutoCompleteTextBox.DataSource = filter;
if (s == string.Empty)
{
filter.Clear();
txtAutoCompleteTextBox.DataSource = filter;
}
}
VB.NET
Private Sub txtAutoCompleteTextBox_TextBoxContentChanged(ByVal sender As Object, ByVal e As RoutedEventArgs)
ListData()
Dim s As String = txtAutoCompleteTextBox.TextToChanged
Dim filter = (
From name In lstNames
Where (name.Contains(s))
Select name).ToList(Of String)()
For Each item In filter
If item.Equals(s) Then
txtAutoCompleteTextBox.TextToChanged = s
End If
Next item
txtAutoCompleteTextBox.DataSource = filter
If s = String.Empty Then
filter.Clear()
txtAutoCompleteTextBox.DataSource = filter
End If
End Sub
The above code calls the ‘ListData()’ method and based upon the text entered in the textbox, the LINQ query retrieves the matched string from the List<string> ‘lstData’.
Step 9: Run the application and the following result will be displayed:
Note: This user control is just a demonstration of how auto-complete can be implemented in your custom control. The control has to be perfected in terms of UI and functionality. So feel free to do so.
The entire source code of this article can be downloaded over here
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions), and Front-end technologies like Angular and React. Follow him on twitter @
maheshdotnet or connect with him on
LinkedIn