Page 1 of 1

Create an AutoComplete TextBox Control in C#

#1 PsychoCoder  Icon User is offline

  • Google.Sucks.Init(true);
  • member icon

Reputation: 1638
  • View blog
  • Posts: 19,853
  • Joined: 26-July 07

Post icon  Posted 06 October 2007 - 09:39 PM

Welcome to my tutorial on Creating You Own AutoComplete TextBox in C#. This tutorial assumes you have a good knowledge of making your own User Controls, and an understanding of overriding base events in the control you're inheriting from. In this tutorial we will be creating a TextBox that has an auto complete ability, retrieving its values from a text file.

In this tutorial we will make use of the following:The latter 2 we will actually be overriding the base functionality to accomplish our specific tasks. Now we dive into the code. When creating this control the first thing we need to do is bring in references to the appropriate Namespaces, we do this with the using statement, so add these Namespaces to the top of your user control class:

using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.ComponentModel;
using System.IO;
using System.Collections;



Those are the Namespaces we will be making use of in creating our control. Second we will want a Namespace for our control, mine, since I am PsychoCoder, is PC, and it looks like this

namespace PC
{
	 //we will add our control in here
}



Since we're inheriting from an existing control library, we need to make sure we add that in the signature of our class

public class AutoComplete : TextBox
{
   /all of our code will go in here
}



Now, in this class I have variables that are referenced throughout the class, so I made them Global variables. The debate rages on over the use of Global variables and I agree with parts of both arguments so Ill say this, it's ok in my mind to use global variables, just use them sparingly and cautiously. Here are the global variables we need for this control:

#region "Variables"
//List that holds the values to be loaded
//by the textbox
private ArrayList _reloadValues = new ArrayList();
//Flag used to tell the keypress 
//handler if it should autocomplete
private bool _updateValues = false;
//The filename of the file that the 
//list is read from and saved to.
private string _file = "";
//Set to true when the _reloadValues is loaded.
private bool _init = false;	
#endregion



NOTE: I always group my code in regions (#region RegionName) as this makes it easier for me to find things and sections as I'm looking through my code.

2 of those global variables are for the only 2 properties of this control, Values and ValuesFile. These properties reference the actual string array of the values for the AutoComplete functionality and the file name the values are stored in:

#region "Properties"

/// <summary>
/// Sets the list of choices directly
/// </summary>
[Description("Sets the list of values")]
public string[] Values
{
	get	{return (string[])_reloadValues.ToArray(typeof(string));}
	set	{_reloadValues = new ArrayList(value);}
}

/// <summary>
/// The filename of the file to get the values from
/// </summary>
[Description("The filename of the file to load from")]
public string ValuesFile
{
	get{return _file;}
	set{ _file = value;}
}

#endregion



NOTE: Notice on the properties I have added a Description, this is always a good idea when creating a user control, this gives a nice description, in code and in the IDE, what that property is to be used for.

Next thing we need for our class are Constructors Constructors are very important in a class, this is what allows you to instantiate them in your form code, such as AutoComplete auto = new AutoComplete();. Without constructors we couldn't instantiate our classes. We have 2 constructors, 1 that sets the file name to an empty string, and one that instantiates it to an actual file:

#region "Constructors"
/// <summary>
/// Constructor that initializes
/// filename to ""
/// </summary>
/// <remarks></remarks>
public AutoComplete()
{
	  _file = string.Empty;
}

/// <summary>
/// Constructor that sets the
/// file to an actual file
/// </summary>
/// <param name="file"></param>
public AutoComplete(string file)
{
	  _file = file;
}
#endregion



NOTE: Always use your XML Comment blocks to descript what taht method does, and always provide good comments in your code.

Now that we have the properties and constructors defined, we can get into the meat of the control, the methods that do the actual work. First we will look at a short method, that is used to call another method. The RefreshTextBox method calls the RefreshValues method, which is used to refresh or reload the values file the control is reading from:

/// <summary>
/// Causes the control to reload the values file.
/// </summary>
/// <remarks></remarks>
[Description("Causes the control to realod the values.")]
public void RefreshTextBox()
{
	RefreshValues();
}



As you can see, here we call the RefreshValues method so now we'll talke about that method. This method first checks to make sure the file name isnt empty, that a file has been passed, if it doesnt then it creates a file based on the control name with ".txt" appended to it. It then opens that file and repopulates our ArrayList that is holding the values for our control:

/// <summary>
/// Refreshes the values already saved in the text file,
/// if no file exists then create one
/// </summary>
/// <remarks></remarks>
private void RefreshValues()
{
	try
	{
		//check to see if a file name was provides, if
		//one wasnt provided then create a new file with
		//a name of the Control.txt
		if (_file == "")
			_file = this.Name + ".txt";
		//re instantiate our array list object
		_reloadValues = new ArrayList();
		//open our file
		FileStream stream = new FileStream(_file, FileMode.OpenOrCreate, FileAccess.ReadWrite);
		//read the file
		StreamReader reader = new StreamReader(stream);
		//loop through each line in the file adding the value
		//to our ArrayList object
		while (reader.Peek() != -1)
			_reloadValues.Add(reader.ReadLine());
		//close out reader and stream objects
		reader.Close();
		stream.Close();
		_init = true;
	}
	catch (Exception ee)
	{
		throw new Exception("Could not open the value file for the control: " + this.Name, ee);
	}
}



So, you say, we're creating an AutoComplete TextBox which reads its values from a text file, so how do the values get into the file. Well I'm glad you asked. We have a method named SaveFeedURL, I used this name as I created this file for the RSS Reader I created (located in another tutorial here on </dream.in.code>, you can change it if you like.

In this method we create our stream and writer objects, then we check to ensure a value was passed to this method, since we don't want to save an empty string to our file. From there we check to see if the value already exists in our file, as we don't want to save duplicate values. If all those pass, then we open the file, write to the file, then close it back up. If the file provided doesn't exist then we need to create one to hold our values.

Once the file is created we use the ArrayList.GetEnumerator to return an enumeration we can use to iterate through the array and add each value to our new file. We then close the file, reallocating those resources.

public void SaveFeedURL(string url)
{
	//create our objects
	string str;
	FileStream stream;
	StreamWriter writer;
	//check to make sure a value was passed
	if (url.Trim() == "") return;	//Don't save empty strings
	bool isFound = IsPresent();
	//check to see if the file already exists
	//if it does we want to add to it, not
	//create a new one
	if (File.Exists(_file))	
	{
		//check to make sure the value passed
		//doesnt already exist in our file
		if (isFound) return;
		try
		{
			//add the value to our ArrayList
			_reloadValues.Add(url);
			//open our file
			stream = new FileStream(_file, FileMode.Append);
			writer = new StreamWriter(stream);
			//write the value
			writer.WriteLine(url);
			//close everthing up
			writer.Close();
			stream.Close();
		}
		catch (Exception ex)
		{
			MessageBox.Show(ex.Message);
			throw new Exception("Could not save the value: " + url + " to the list", ex);
		}
	}
	//the file was not found, so create it.
	else	
	{
		if (!isFound)
			_reloadValues.Add(url);
		try
		{
			//create an enumerator to iterate
			//through our ArrayList with
			IEnumerator enumerate = _reloadValues.GetEnumerator();
			//create our file
			stream = new FileStream(_file, FileMode.Create);
			//generate a StreamWrite to write to
			//our file with
			writer = new StreamWriter(stream);
			//now loop through the ArrayList adding
			//each of the values to our file
			while (enumerate.MoveNext())
			{
				str = (string)enumerate.Current;
				writer.WriteLine(str);
				//close everything up
				writer.Close();
				stream.Close();
			}
		}
		catch (Exception ex)
		{
			MessageBox.Show(ex.Message);
			throw new Exception("Could not save the value: " + url + " to the list", ex);
		}
	}
}



NOTE: Always release resources when you are finished with that particular object.

You will notice that in this method we make a reference to a method named IsPresent, this is the method that takes the value being passed to the save method and makes sure it doesn't already exist in our file. Once again we use the ArrayList.GetEnumerator Method to iterate through our file contents to see if this value exists, if it does we return True, if it doesn't we return False:

/// <summary>
/// Searches the file with the value
/// being passed for save
/// </summary>
/// <returns>True/False</returns>
private bool IsPresent()
{
	//create an enumerator to iterate
	//through our ArrayList
	IEnumerator enumerate = _reloadValues.GetEnumerator();
	string str = this.Text.ToUpper();
	//loop through all the values looking
	//for the value being daved. If it exists
	//return true, e;se return false
	while(enumerate.MoveNext())
	{
		string text = (string)enumerate.Current;
		if(text.ToUpper().IndexOf(str) == 0)
			return true;
	}
	return false;
}




That is the end of our methods that we create, the next 3 are overriding base methods in the TextBox Class. The first one we'll look at is the OnTextChanged Event. This is the most complicated of our methods. We have to first make sure our ArrayList contains items, then we have to check each character typed into the textbox to see if a value in our file resembles whats currently in the textbox, if it does then we highlight the remaining text in the textbox as the user types, letting them know they possible have the value they're looking for:

/// <summary>
/// Overrides the TextChanged event.  We will use this
/// to check and see if what the user is typing resembles anything
/// in our file
/// </summary>
/// <param name="e">An System.EventArgs that contains the event data.</param>
protected override void OnTextChanged(EventArgs e)
{
	if(_reloadValues.Count == 0 && !_init)	//Load the values from the file.
	{
		RefreshValues();
	}
	string tmp = this.Text.ToUpper();
	if((tmp == "") || _updateValues) 
	{
		base.OnTextChanged (e);
	}
	else				
	{
		//create our enumerator to iterate through our ArrayList
		IEnumerator enumerate = _reloadValues.GetEnumerator();
		//set out bool value to false
		bool isFound = false;
		//loop while theres a value is our ArrayList
		//and our boolean value is false
		while((enumerate.MoveNext()) && (!isFound))
		{
			//set our string variable to the value of
			//the current value in the enumerator
			string str = (string)enumerate.Current;
			//check to see if it holds one of our values
			if(str.ToUpper().IndexOf(tmp) == 0)
			{
				//update our values
				_updateValues = true;
				//set the value from our file
				//to the value in the textbox
				this.Text = str;
				//re-highlight the remaining text
				//as the user types
				this.Selectionstart = tmp.Length;
				this.SelectionLength = str.Length - tmp.Length;
				//set our boolean value to true
				isFound = true;
				_updateValues = false;
			}
		}
		base.OnTextChanged (e);
	}
}



The next 2 are relatively simple, we check to see if the user pressed one of the normal keys, a letter or a number (no special characters), then act accordingly. Since they're both but 3 lines apiece, Ill show them both at once:

/// <summary>
/// Overrides KeyDown event.
/// </summary>
/// <param name="e">A System.Windows.Forms.KeyEventArgs that contains the event data.</param>
protected override void onkeydown(KeyEventArgs e)
{
	if(e.KeyValue == 8 || e.KeyValue == 46)
		_updateValues = true;
	base.onkeydown (e);
}

/// <summary>
/// Overrides the KeyUp event.
/// </summary>
/// <param name="e">A System.Windows.Forms.KeyEventArgs that contains the event data.</param>
protected override void onkeyup(KeyEventArgs e)
{
	if(e.KeyValue == 8 || e.KeyValue == 46)
		_updateValues = false;
	base.onkeyup (e);
}



That's it, thats all the code you need for creating your own custom AutoComplete TextBox Control. Compile your code once your finished and you have a user control you can add to any project where you need that functionality. Remember, if you like you can always add more functionality to your control, that is up to you.

I will be providing the control I created while creating my control and this tutorial. Remember though that this file is under the GNU General Public License, meaning you can use or distribute it as you see fit, but the license header must stay intact. I hope you found this tutorial informative and useful, thank you for reading.

Happy Coding :)
Attached File  PC_AutoComplete.zip (14.71K)
Number of downloads: 19382

This post has been edited by PsychoCoder: 06 October 2007 - 09:41 PM


Is This A Good Question/Topic? 1
  • +

Replies To: Create an AutoComplete TextBox Control in C#

#2 skyhawk133  Icon User is offline

  • Head DIC Head
  • member icon

Reputation: 1865
  • View blog
  • Posts: 20,278
  • Joined: 17-March 01

Posted 09 October 2007 - 12:19 PM

Vote for this tutorial at DZone: http://www.dzone.com...ntrol_in_c.html
Was This Post Helpful? 0
  • +
  • -

#3 csharpnerd  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 29-March 08

Posted 16 October 2008 - 12:25 AM

View Postskyhawk133, on 9 Oct, 2007 - 12:19 PM, said:

Vote for this tutorial at DZone: http://www.dzone.com...ntrol_in_c.html

Nice One, but one Query ..
Why should i create a Control when Microsoft already gives me Auto Complete Properties / Source in the TextBox Control ?
Is there any other such benefit in using this Code
Was This Post Helpful? 0
  • +
  • -

#4 StCroixSkipper  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 10
  • View blog
  • Posts: 121
  • Joined: 23-December 08

Posted 16 March 2009 - 02:34 PM

There is no autocomplete textbox in WPF. You have to write you own for now. So this code is interesting. But you are right. If you are using Windows.Forms, then the textbox already has autocomplete.
Was This Post Helpful? 0
  • +
  • -

#5 StCroixSkipper  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 10
  • View blog
  • Posts: 121
  • Joined: 23-December 08

Posted 12 October 2009 - 10:19 AM

View PostStCroixSkipper, on 16 Mar, 2009 - 01:34 PM, said:

There is no autocomplete textbox in WPF. You have to write you own for now. So this code is interesting. But you are right. If you are using Windows.Forms, then the textbox already has autocomplete.


I was wrong. There is an autocomplete combobox in WPF.
Simply create a combobox and set the IsEditable and IsTextSearchEnabled properties to true...

Here is the xaml
<Window x:Class="AutoCompleteCBProject.Window1"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	Title="Window1" Height="300" Width="300">
	<StackPanel>
		<ComboBox x:Name="_cb" Selectionchanged="_cb_Selectionchanged" IsEditable="True" IsTextSearchEnabled="True"
				  >
			<ComboBoxItem>abcdefghijklmnop</ComboBoxItem>
			<ComboBoxItem>bcdefghijklmnop</ComboBoxItem>
			<ComboBoxItem>cdefghijklmnop</ComboBoxItem>
			<ComboBoxItem>defghijklmnop</ComboBoxItem>
			<ComboBoxItem>efghijklmnop</ComboBoxItem>
		</ComboBox>
	</StackPanel>
</Window>


Was This Post Helpful? 0
  • +
  • -

#6 sureej19  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 4
  • Joined: 12-June 10

Posted 12 June 2010 - 08:49 AM

Great article thanks. Will use in WPF Apps

This post has been edited by sureej19: 12 June 2010 - 08:50 AM

Was This Post Helpful? 0
  • +
  • -

#7 nicolcrome  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 5
  • Joined: 02-May 13

Posted 30 June 2014 - 10:11 PM

You can simply create an autocomplete textbox

textBox1.AutoCompleteMode = AutoCompleteMode.Suggest;
textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
AutoCompleteStringCollection DataCollection = new AutoCompleteStringCollection();
addItems(DataCollection);
textBox1.AutoCompleteCustomSource = DataCollection;

Source : Autocomplete Textbox

Nicol
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1