• Home
  • New Entries
  • Popular Entries
  • Submit a Story
  • About

Creating ASP.NET Ajax Scriptable Server Controls - Overview ...

To get even wider reuse from our JavaScript Types, ASP.NET Ajax provides us with some server side goo that makes it easier for us to package client-centric controls as server controls.  In this article I will show you how to create a custom TextBox control which has a Tags property and which can match those tags at runtime in the client.  To do this we will need to create a custom TextBox control (TaggerTextBox) and a custom ASP.NET Ajax control which will represent the behaviour and the client-side interface of the text box in the client.

To start with, you should create a solution with 2 projects - 1 for the web site and 1 for your custom server controls.


To create the Web.Controls project, use the ASP.NET Ajax Server Control template because it comes with some boiler plate code to get you started.
In the Web.Controls project, add a new class named TaggerTextBox, derive it from TextBox and implement a special ASP.NET Ajax interface called IScriptControl.

    namespace ScriptableServerControls.Web.Controls {

        public class TaggerTextBox : TextBox, IScriptControl {

            public IEnumerable<ScriptDescriptor> GetScriptDescriptors() {
                throw new NotImplementedException();
            }

            public IEnumerable<ScriptReference> GetScriptReferences() {
                throw new NotImplementedException();
            }
        }
    }

As you can see, implementing the IScriptControl interface requires us to add 2 methods to our class - GetScriptDescriptors and GetScriptReferences.  Script references are where we get to return a reference to an embedded JavaScript file which contains our client control code.  Script descriptors are a server side API which gives us strongly typed access to emitting the $create statement that will get injected into the client to represent an instance of our JavaScript class.  We will get to both of those in a minute, for now, let is add the 2 properties that give our custom TextBox its special powers:

    public string TagsToMatch {
        get { return (string)ViewState["TagsToMatch"]; }
        set { ViewState["TagsToMatch"] = value; }
    }

    public string ResultElementID {
        get { return (string)ViewState["ResultElementID"]; }
        set { ViewState["ResultElementID"] = value; }
    }

    public string HiddenFieldStateElementID {
        get { return (string)ViewState["HiddenFieldStateElementID"]; }
        set { ViewState["HiddenFieldStateElementID"] = value; }
    }

    public string SelectedValues {
        get {
            HiddenField ctl = this.FindControl(this.HiddenFieldStateElementID) as HiddenField;

            if (ctl == null)
                return "";

            return ctl.Value.TrimEnd(new char[] { , });
        }
        set {
            HiddenField ctl = this.NamingContainer.FindControl(this.HiddenFieldStateElementID) as HiddenField;
            if (ctl != null)
                ctl.Value = "";
        }
    }

As you can see, our TextBox will take a list of TagsToMatch.  It will use this list at runtime to match words that the user types into the control and display matches in another element with the ID of ResultElementID.  This could be a DIV or SPAN element somewhere else on the page.  In this case I have also provided another element reference called HiddenFieldStateElementID which a user would bind to a HiddenField to maintain state of matched tags (in a production example, you would provide a more encapsulated version of HiddenField).  Finally we have a server side method called SelectedValues which we can use to surface the values that were persisted in the HiddenField.

The Client Behaviour

Now it is time to leave our server control for a moment and to think about how our control will work in the clients browser.  To kick things off, add a new JavaScript file to the Web.Controls project named TaggerTextBox.js (the name is not particularly relevant), and include it as an Embedded Resource:

Now we can implement the body of our TaggerTextBox.js file - which will be the behaviour that we wish to bind to our server control TextBox at runtime.  The following snippet of code is a mouthful, but don be too overwhelmed by it.  It is simply the boilerplate code that is required to create a new ASP.NET Ajax client Type with getter and setter accessors for each property that we wish to expose.  In this case, it is 1 property for each of those Server Control properties:

    /// <reference name="MicrosoftAjax.js" />

    Type.registerNamespace("MarkItUp.Web") ;

    MarkItUp.Web.TaggerTextBox = function(element) {
        MarkItUp.Web.TaggerTextBox.initializeBase(this, [element]) ;
        this._resultElement = null ;
        this._stateElement = null ;
        this._tagsToMatch = ;
        this._internalTagsToMatchArray = [] ;
    }

    MarkItUp.Web.TaggerTextBox.prototype = {

        initialize : function() {
            MarkItUp.Web.TaggerTextBox.callBaseMethod(this, initialize) ;
            var func = new Function.createDelegate(this, this.handleKeyPress) ;
            $addHandler(this.get_element(), keyup, func) ;
        },

        dispose : function() {
            $clearHandlers(this.get_element()) ;
            MarkItUp.Web.TaggerTextBox.callBaseMethod(this, wouldispose) ;
        },

        get_TagsToMatch : function() {
            return this._tagsToMatch ;
        },

        set_TagsToMatch : function(value) {
            var str = value.toLowerCase() ;
            this._internalTagsToMatchArray = str.split(,) ;
            this._tagsToMatch = str ;
        },

        get_HiddenFieldStateElementID : function() {
            return this._stateElement ;
        },

        set_HiddenFieldStateElementID : function(value) {
            this._stateElement = value ;
        },

        get_ResultElementID : function() {
            return this._resultElement ;
        },

        set_ResultElementID : function(value) {
            this._resultElement = value ;
        },

        handleKeyPress : function(e) {
            $get(this._resultElement).innerText = this.get_element().value ;
        }

    }

    MarkItUp.Web.TaggerTextBox.registerClass("MarkItUp.Web.TaggerTextBox", Sys.UI.Control) ;

As you can see, I have also taken the liberty of binding an event handler to the keyup event of the underlying HTML element that represents my server control at runtime and cleaning it up in the dispose method.  The handleKeyPress event handler simply echoes the contents of the text box back to the user whenever they press a key but this is where we will write our parsing logic to check tags at runtime.

Let is flip back to the server for a moment...

Referencing our JavaScript file

Before we go ahead and write too much tricky JavaScript code, why don we go ahead and finish off our server control so that we can test it out in our website.  First, we add the Script descriptors - whose job it is to map the server side values out into our $create statement.  ScriptDescriptors come in a few different flavours:


Each of those Addblah methods, allows us to add a property reference which will ultimately end up in the corresponding slot of a $create statement:

In our case, we will return the values that were set from the properties that we exposed like so:

 

    public IEnumerable<ScriptDescriptor> GetScriptDescriptors() {
        ScriptControlDescriptor descriptor = new ScriptControlDescriptor("MarkItUp.Web.TaggerTextBox", this.ClientID);
        descriptor.AddProperty("TagsToMatch", this.TagsToMatch);
        descriptor.AddElementProperty("ResultElementID", this.ResultElementID);
        descriptor.AddElementProperty("HiddenFieldStateElement", this.HiddenFieldStateElementID);

        yield return descriptor;
    }

Now for the Script reference stuff that will ensure that our JavaScript file is emitted at runtime - without that, the above $create code will fail with a message something like... "Namespace MarkItUp not found" (or something along those lines).  Returning the Script reference requires 2 steps:

   1. Add an assembly attribute which marks the embedded resource as a script resource
   2. Return the reference via the interface method

The assembly attribute for our Javascript file can either be at the head of our code file (above the namespace declaration) or, more professionally, in the AssemblyInfo file:

    [assembly: WebResource("ScriptableServerControls.Web.Controls.TaggerTextBox.js", "text/javascript")]

Notice that the WebResource path is the name of the file, prefixed with the namespace of the assembly.

Now we can return the reference via the interface method:

    public IEnumerable<ScriptReference> GetScriptReferences() {
        yield return new ScriptReference(
            Page.ClientScript.GetWebResourceUrl(
                typeof(TaggerTextBox),
                "ScriptableServerControls.Web.Controls.TaggerTextBox.js"
                )
            );
    }

The last thing to do is to call our GetScriptDescriptors and GetScriptReferences methods from within the relevant lifecycle points in the control:

 

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

        ScriptManager manager = ScriptManager.GetCurrent(this.Page);
        manager.RegisterScriptControl(this);
    }

    protected override void Render(HtmlTextWriter writer) {
        base.Render(writer);

        ScriptManager manager = ScriptManager.GetCurrent(this.Page);
        manager.RegisterScriptDescriptors(this);
    }

 

At this point, build your solution to see that everything is working fine.  If it is, add a project reference to your Web.Controls project from the Web project and then after we add the following Register line into a test page, we can reference our control too:

    <%@ Register Namespace="ScriptableServerControls.Web.Controls" Assembly="ScriptableServerControls.Web.Controls" TagPrefix="cc" %>

We will wire up our control with some TagsToMatch, attach to a hidden field element and bind it to an output element:

    <asp:HiddenField ID="hdnField" runat="server" />

    <cc:TaggerTextBox id="tt1" runat="server"
        HiddenFieldStateElementID="hdnField"
        ResultElementID="output"
        TagsToMatch="Silverlight, Ajax, ASP.NET"
         />
    <div id="output"></div>

 

When we run the demo and start typing (don forget to add a ScriptManager first), our text should be echoed in the output element like so:

The remainder of the logic for this control simply requires writing some parsing logic to parse the textbox text and look for matches against the TagsToMatch, let is do a dirt simple implementation of that now.  Go back to the JavaScript file and add the following code the key press handler:

    handleKeyPress : function(e) {

        this._resultElement.innerText = ;
        var str = this.get_element().value.toLowerCase() ;

        for( var i=0; i<this._internalTagsToMatchArray.length; i++ ) {
            var tag = this._internalTagsToMatchArray[i] ;
            if( str.indexOf(tag) != -1 ) {
                this._resultElement.innerHTML +=
                    String.format("Found {0}<br />", tag) ;
            }
        }
    }

Now when we re-run our application, we see the following results:

That is it for now, but over the next few days I will post a few more demo is and examples of different types of scriptable server controls that you might want to create.  If you have any ideas for things that you would like to see, please feel free to drop me a line.

 Original Source:

AddThis Social Bookmark Button

Posted at 08:16:47 am | Permalink | Posted in .Net  

Related Stuff

  • MooV: Using cutting edge Video phones and Software Video Phones - coupling all that with VoIP and empowering the disabled.

  • Moo Telecom: VoIP communications made easy - Ring anyway with the fun and ease of using a normal phone

  • TagR:Mobile Social Network with Real Time Locations Based services, and Ambience Intelligence, VoiP, IM, Skype, Googletalk, Mapping, Flickr, Events, Calendaring, Scheduling, SecondLife Support

  • ClearSMS : ClearSMS is a Web-based application that lets you send bulk SMS messages to your customers, contacts, or just about anyone.

  • Jajah:jah is a VoIP (Voice over IP) provider, founded by Austrians Roman Scharf and Daniel Mattes in 2005[1]. The Jajah headquarters are located in Mountain View, CA, USA, and Luxembourg. Jajah maintains a development centre in Israel.

  • Skype: It’s free to download and free to call other people on Skype. Skype the number one voice over ip software

  • PrivatePhone: a free local phone number with voicemail and messages you can check online or from any phone.

Top Stuff

e-messenger

MSN Web Messenger

eBuddy

ASP.NET Ajax CalendarExtender and Validation

AIM Express

Ajax Tools for ASP.NET Developers



About Ajaxlines

Ajaxlines is a project focused on providing its audience with a database of most of Ajax related articles, resources, tutorials and services from around the world.

Its purpose is to showcase the power of Ajax and to act as a portal to the Ajax development community.


Search


Topics

  • .Net (176)
  • Ajax (112)
  • Ajax Games (10)
  • Articles (95)
  • Bookmarking (35)
  • Calendar (21)
  • Chat (45)
  • ColdFusion (3)
  • CSS (84)
  • Email (23)
  • Facebook (84)
  • Flash (20)
  • Google (54)
  • Html (29)
  • Image (12)
  • International Calls & VOIP (7)
  • Java (58)
  • Javascript (280)
  • jQuery (200)
  • JSON (75)
  • Perl (2)
  • PHP (172)
  • Presentation (19)
  • Python (3)
  • Resources (2)
  • RSS (8)
  • Ruby (32)
  • Storage (4)
  • Toolkits (103)
  • Tutorials (227)
  • UI (11)
  • Utilities (174)
  • Web2.0 (18)
  • XmlHttpRequest (29)
  • YUI (13)

© 2006 www.ajaxlines.com. All Rights Reserved. Powered by IRange