Welcome to Dream.In.Code
Getting C# Help is Easy!

Join 109,157 C# Programmers for FREE! Ask your question and get quick answers from experts. There are 1,038 online right now! We've got more than 500 tutorials and 2,000 snippets. Join and find out why Dream.In.Code is the #1 programming help community on the internet! Registration is fast and FREE... Join Now!



Create an AutoComplete TextBox Control in C#

 
Reply to this topicStart new topic

> Create an AutoComplete TextBox Control in C#

PsychoCoder
Group Icon



post 6 Oct, 2007 - 09:39 PM
Post #1


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:

CODE

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

CODE

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

CODE

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:

CODE

#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:

CODE

#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:

CODE

#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:

CODE

/// <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:

CODE

/// <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.

CODE

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:

CODE

/// <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:

CODE

/// <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:

CODE

/// <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 smile.gif
Attached File  PC_AutoComplete.zip ( 14.71k ) Number of downloads: 1632


This post has been edited by PsychoCoder: 6 Oct, 2007 - 09:41 PM
Go to the top of the page
+Quote Post


Register to Make This Ad Go Away!

skyhawk133
Group Icon



post 9 Oct, 2007 - 12:19 PM
Post #2
Vote for this tutorial at DZone: http://www.dzone.com/links/create_an_autoc...ntrol_in_c.html
Go to the top of the page
+Quote Post


Fast ReplyReply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 

Lo-Fi Version Time is now: 9/5/08 05:31PM

Live C# Help!

C# Tutorials

Reference Sheets

C# Snippets

Bye Bye Ads

Free DIC T-Shirt

T-Shirt Example

Related Sites

Monthly Drawing

Thumb Drive

Partners

Top Contributors

Top 10 Kudos This Month