Make an Autocomplete Control a Webcontrol

[Part One] [Part Two] [Part Three] [Part Four] [Part Five]

So in the last post I showed the easy way to use the Toolkit Autocomplete control, but it might have left you with some questions like: Do I have to keep adding a textbox everytime I want an autocomplete control? Is there an easy way to make a composite control? Is this guy an idiot?

The easy answer is: yes.

Now I’m not a huge fan of making web project controls (.ascx) unless there is heavy style formatting. For the most part if its going to be usable somewhere else and it is fairly simple to represent codewise, I’ll put it in a class library as a WebControl or ScriptControl. So how is this done with the autocomplete?

For my examples, I’ll be using the same root namespace as the Script Control examples, namely Test.Examples.AutoCompleteExample. Now that I have a wonderful folder created I’m going to go ahead and create a WebControl Class named AutoCompleteControl.

Now when I create this new control there are some things I will need:
-Has to inherit from at least WebControl (ScriptControl example I will do later)
-Has to have a textbox (The autocomplete needs a target control)
-I would strongly suggest a certain amount of properties to be exposed, basically reflecting the properties on the autocomplete
-I added the implementing of ITextControl mainly to relfect a .Text property but that isn’t really needed. I just found this useful if you end up treating this control like a textbox for say validation purposes.

    public class AutoCompleteControl : WebControl, INamingContainer
    {
        private AutoCompleteExtender autoComplete;
        private TextBox textboxTarget;

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            autoComplete = new AutoCompleteExtender();
            autoComplete.ID = "autoCompleteMain";

            textboxTarget = new TextBox();
            textboxTarget.ID = "textboxTarget";

            Controls.Add(textboxTarget);
            Controls.Add(autoComplete);
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            autoComplete.TargetControlID = textboxTarget.ID;

            autoComplete.CompletionInterval = 5000;
            autoComplete.EnableCaching = EnableCaching;
            autoComplete.CompletionSetCount = CompletionSetCount;
            autoComplete.MinimumPrefixLength = MinimumPrefixLength;
            autoComplete.OnClientItemSelected = OnClientItemSelected;
            autoComplete.ServiceMethod = ServiceMethod;
            autoComplete.ServicePath = ServicePath;
        }

        public Int32 CompletionSetCount { get; set; }

        public Boolean EnableCaching { get; set; }

        public Int32 MinimumPrefixLength { get; set; }

        public String OnClientItemSelected { get; set; }

        public String ServiceMethod { get; set; }

        public String ServicePath { get; set; }

        public String Text
        {
            get
            {
                EnsureChildControls();
                return textboxTarget.Text;
            }
            set
            {
                EnsureChildControls();
                textboxTarget.Text = value;
            }
        }
    }

Well hell that was easy, wasn’t it? All I had to do is create the class, add the textbox and autocomplete as controls, and set a few properties. Voila, we now have a working autocomplete control. Now if you had read the last post on this (Page 3) then you might have noticed this new property:

CompletionInterval

What is that? Well apparently it deals with the amount of time a particular item is highlighted when hovering over it. Found this out the other day. SURPRISE!!

Couple of points from this:

Why?
- Well if nothing else, you now have a compostite control to use. The markup will supply the service location and the method to use, everything else is taken care of with this control.

- On top of that, this can now be snug in a class library (assembly) for reuse thus removing it’s need to be recopied everytime you need it in another web project.

- You can now inherit from this control to add fucntionality if you need.

- You can now convert this to a script control in order to access javascript events and other fun things.

So as you can see, this is a pretty good way to go.

Something else of note is the CreateChildControls/EnsureChildControls situation. CreateChildControls is a overridable method that is used to make sure that the controls themselves are created and handled. The nice thing about this is that you won’t run into the timing issues you might if you try to initialize controls in other methods like OnInit. Also, it allows you the use of the EnsureChildControls method. When this method is called, the CreateChildControls method is either called or not depending if it’s already been run. EnsureChildControls basically asks if CreateChildControls has been run. If it has then it doesn’t call it again, if it hasn’t then the method is called to create the controls. So as you can see, this makes it easy to guarantee that the controls have been created in order to access them. This becomes important with properties that are tied to controls as there is no guarantee that when a property is accessed the controls aren’t null. Kind of nice, huh?

Also you might have noticed that I set all the control properties that were “attached” to the autocomplete in prerender. Usually for safety, I don’t both setting any outward facing properties like that until prerender so that I know I have the latest and greatest values.

Just incase you needed the markup example:

<%@ Register Assembly="Test.Examples"
  Namespace="Test.Examples.AutoCompleteExample" TagPrefix="test" %>

<test:AutoCompleteControl ID="autoCompleteTest" runat="server"
  ServicePath="~/Service/AutocompleteWebControl.svc" ServiceMethod="GetUserNameList"
  CompletionSetCount="10" MinimumPrefixLength="1" />

HOLY SMOKES NO NEED FOR TEXTBOXES!!11

  using System.Web.UI;
  using System.Web.UI.WebControls;
  using AjaxControlToolkit;

Tags: , ,

  • B. Rich Mister

    Hmm. Interesting, except that the AutoCompleteExtender doesn’t expose the ID property in code, and you can’t add it to the Controls collection in a class that derives from System.Web.UI.WebControls.WebControl.

    For those of you like me who fell for this junk, steer clear. It doesn’t work and will only add to your frustration.

  • Sean

    @B. Rich Mister
    Not sure where you’re getting that from. I can assure you this works as I have the code running just fine. I’m wondering if we’re not talking about the same control or possibly I am using a newer version of the Ajax Toolkit?

    AutoCompleteExtender inherits ExtenderControl which inherits Control which exposes a public ID and can be added using the Control.Add() method.

  • Andre

    I concur. I can create an AutoCompleteExtender and progmatically set it’s ID. Sean is correct in the fact that it inherits the ID property from System.Web.UI.Control.

    I am using .NET 3.5 SP1.

    On a side note, if you find a problem with a post, add your findings so that the author can replicate it. Leaving a negative post and telling others to look elsewhere is not helping anyone.

  • http://www.jerashdev.net/blog/ara/ Omar Qadan

    i tried this solution but i got empty page when i use it i got empty page when i view the source it reference all Ajax script correctly but the control render like this

  • http://www.jerashdev.net/blog/ara/ Omar Qadan

    span id=”AutoCompleteTextBox1″ FirstRowSelected=”false” OnClientPopulated=”ClientPopulated” CompletionListHighlightedItemCssClass=”autocomplete_highlightedListItem”

  • http://www.byatool.com Sean

    Ok after some fun searching, I finally found this example in my test project. When run (And this means the barest example that I give in this post) the mark up turns out to look like this:

    <span id=”autoCompleteTest”><input name=”autoCompleteTest$textboxTarget” type=”text” id=”autoCompleteTest_textboxTarget” /></span>

    The span basically being the webcontrol and the input the textbox included. (I assume you know this) The markup you posted doesn’t seem to make full sense as I think maybe something was lost in the copy, paste, and post. The < > characters will be stripped or read incorrectly unless you use &lt ; or &gt ; (minus the space between the t and the ; ) If you want, you can email me what you have to sean@byatool.com . Unfortunately, as I said, the markup has lost something in the translation.

  • http://www.jerashdev.net/blog/ara/ Omar Qadan

    yes like you said i got the markup but with out input stuff,any way i did it as script control and its work fine

    • http://www.byatool.com Sean

      Huh, well glad I could sort of help. Sorry that the initial attempt was less than useful. Good to hear you got something working though.

  • http://boinc.vanderbilt.edu/CSB/view_profile.php?userid=1660 mark

    I want to say – thank you for this!

  • Rafael

    Hi,

    i’m trying to use your code but the webservice method is not fired, and i’m not why.

    “ac” is the AutoCompleteControl class

    UserControls.classAutoComplete ac = new MapaSaoPaulo.UserControls.classAutoComplete();

    I add the ac in a div..
    divMenu.Controls.Add(ac);

    but not works.. =/

    can you help-me please?

    thank you and sorry by english

    • http://www.byatool.com Sean

      Oh boy, I’ll have to look at that one again. Been a while but I’ll check on it for you. Don’t worry about the english, you write better than 99% of the people in the US.