[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;