Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Converting zichun's auto-complete textbox control to a .NET ASCX user control

2.67/5 (6 votes)
28 Mar 2008CPOL5 min read 1   192  
This article covers how I converted the JavaScript auto-complete textbox into a .NET user control which can have the items set easily and programmatically.

Introduction

I had a requirement to be able to quickly record call history for a customer when they telephoned; which was to be implemented in a footer of a .NET master page; thus appear on every page of the website. Since there were over 500 customers in the system, a dropdown list would have been too large, and it would have been too complex to introduce some sort of search-engine in the footer of a master page.

I came across a CodeProject article written by zichun here. It also includes a demo which did exactly what I wanted; but was implemented in JavaScript and required the entries for the autocomplete dropdown to be pre-specified in script, which was not quite ideal for what I needed: a dynamic list of strings from a database.

This article looks at how I implemented this control as a .NET user control which can be drag-dropped onto any ASPX web form where it is needed and can have its items list dynamically and easily set programmatically.

Credits

Thank you to zichun of CodeProject for the original article referenced above. All the JavaScript code is his; and is excellent.

Implementation

The first thing to do was to include the two JavaScript files in my project. For more information about these files, you can see the original article this was based on, here. I created an App_Javascript folder in the root of my project (just to follow the .NET naming convention) and included actb.js and common.js; both of which were written by zychun and I take no credit for.

Next, I created a /Controls folder and added into it a new .NET user control called AutoCompleteTextBox.ascx. The first thing to do was create the markup. All that was needed was simply a TextBox control (where the user will enter what they want to auto-complete) and a Literal which was used to output the script tags containing the items which would appear as auto-complete options. Thus, the content of AutoCompleteTextBox.ascx was:

ASP.NET
<asp:TextBox id="txtTheTextbox" runat="server" />
<asp:Literal id="litTheJS" runat="server" />

The next thing to do was to write some code in Page_Init of this user control which wrote out references to common.js and actb.js in the head tag of the page if they did not exist already. This means that these references will only appear in pages which use this control, and they will never be accidentally missed out. To do this, I simply checked for the existence of a Literal in the page header; if it did not exist, then I created it and added it as follows:

Note that this relies on the files being in the App_Javascript folder in your application root, change as appropriate:

VB
Protected Sub Page_Init(ByVal sender As Object, _
                        ByVal e As System.EventArgs) Handles Me.Init
    ' Register the JS which is needed (if it has not been added before):
    If IsNothing(Me.Page.Header.FindControl("litAutocompleteTextBoxSrc")) Then
        Dim lit As New Literal
        lit.ID = "litAutocompleteTextBoxSrc"
        lit.Text = vbCrLf + String.Format("<script language="'javascript'"" &_
            " type='text/javascript' src='{0}'></script>", _
            Server.MapPath("~/App_Javascript/actb.js"))

        lit.Text += vbCrLf + String.Format("<script language="'javascript'"" &_
            " type='text/javascript' src='{0}'></script>", _
            Server.MapPath("~/App_Javascript/common.js"))

        lit.Text += vbCrLf

        Me.Page.Header.Controls.Add(lit)
    End If
End Sub

With the .js files now included in the header tags of the rendered ASPX page, the next job was simply to provide two properties to get/set the text and the width of the TextBox control:

VB
Public Property Text() As String
    Get
        Return Me.txtTheTextbox.Text
    End Get
    Set(ByVal value As String)
        Me.txtTheTextbox.Text = value
    End Set
End Property

Public Property Width() As Unit
    Get
        Return Me.txtTheTextbox.Width
    End Get
    Set(ByVal value As Unit)
        Me.txtTheTextbox.Width = value
    End Set
End Property

Now, I added a write-only property named Items which is an array of strings. I implemented this as WriteOnly because I wanted it to be possible to change or set the items at any time. However, I saw no real reason to ever need to read out the string array of items, so implemented this as write-only so that I would never try to read them and get confused as to why I got a null/empty string array!

The trick is that this property converts the string array object into a long string which the JavaScript uses as its items array. So, a string array containing the words apple, orange, and banana gets converted into the long string 'apple','orange','banana' including all the ' marks.

This long string is then stored into a ViewState variable which is used later when rendering out the JavaScript:

VB
Public WriteOnly Property Items() As String()
    Set(ByVal value As String())
        Dim ItemsString As String = String.Empty

        For Each s As String In value
            ItemsString += "'" & s & "',"
        Next

        ' Remove the training comma
        ItemsString = ItemsString.Substring(0, ItemsString.Length - 1)

        ViewState("TheItems") = ItemsString
    End Set
End Property

And finally, my last trick is to set the contents of the literal we defined in the markup to be the javascript which is required to make the auto-complete dropdown list work. I did this in the Page_Load event of the control; and simply used the contents of the ViewState variable set using the .Items property created above. This also calls the actb function (defined in one of the JavaScript files) to "attach" the dropdown list to the TextBox using the TextBox's ClientID:

VB
Protected Sub Page_Load(ByVal sender As Object, _
                        ByVal e As System.EventArgs) Handles Me.Load
    Me.litTheJS.Text = String.Format("<script type='text/javascript'> " &_
            "var customarray = new Array({1}); " &_
            "actb(document.getElementById('{0}'),customarray); </script>",
            Me.txtTheTextbox.ClientID, ViewState("TheItems"))
End Sub

And that's it! The control is now ready for use.

Using the Control

To use the control, simply add a reference to it in a page's markup (such as .aspx or .master) as you would any custom user control. (This can also be done by dragging the control from the Solution Explorer onto the page in Design view, and Visual Studio will automatically write the markup for you):

ASP.NET
<%@ Register Src="~/Controls/AutoCompleteTextBox.ascx" 
    TagName="AutoCompleteTextBox" TagPrefix="bgs" %>

And then when you want the TextBox to appear:

ASP.NET
<bgs:AutoCompleteTextBox ID="txtAutoComplete" runat="server" Width="300px"  />

Finally, all that is left is to set the .Items property of the control anywhere you like in code to set the auto-complete dropdown content of the box, and the rest of it just works, thanks to zichun's JavaScript:

VB
Me.txtAutoComplete.Items = New String() {"Apple", "Orange", "Banana"}

Known Issues

I noticed that originally this worked perfectly when working on the web server machine, but failed when looking at the application on the server from a remote machine. However, Server.MapPath("~") returns a link to c:\inetpub\wwwroot\... - which obviously when connecting to the application remotely caused a JavaScript "object required" error because the remote machine had no such file - but worked perfectly on the server. The answer was to fix the path a bit in Page_Init of the control.

Feedback

I would welcome any feedback on this control; although I cannot promise to be able to help with any queries. This works perfectly for my needs; and whilst I will try to help where I can, I am very busy at the moment!

Thanks

I've said it a lot, but this control could not have been written without the JavaScript and article by zychun, here.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)