Handle Events and Add Controls to Script Controls (ASP.Net ScriptControl)

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

Ok in the last post I went over the creation of a web control. Simple enough as you can see. Now it’s time to screw around with Controls and how to capture their events client side. Something only a brilliant person like me could figure out… I so wish that were true. I could make millions.

Let’s add a control to the eh… control. Say we want to throw an alert everytime a person focuses on a textbox. Yes, that’s really stupid but it’s easy so live with it. Let’s use the class from the other post:

  public class ScriptControlExample : ScriptControl , INamingContainer
  {
     private TextBox textboxStupid;

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

       textboxStupid = new TextBox();
       textboxStupid.ID = "textboxStupid";
       Controls.Add(textboxStupid);
     }

      protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
      {
        ScriptControlDescriptor desc = new ScriptControlDescriptor
          ("Test.Examples.Client.ScriptControlExample", ClientID);
        desc.AddProperty("textboxStupid", textboxStupid.ClientID);

        return new ScriptDescriptor[] { desc };
      }

      protected override IEnumerable<ScriptReference> GetScriptReferences()
      {
         return new ScriptReference[] { new ScriptReference
           ("Test.Examples.ScriptControlTemplate.ScriptControlExample.js", "Test.Examples") };
      }
    }

Notice anything new? I added a textbox, created it in the CreateChildControls method, then added soemthing new to the GetScriptDescriptors method. Remeber the GetScriptDescriptors method is used to send values to the client from the server. Here I am sending the clientID of the textbox… can you guess why? That’s right, because I’m going to use it to find the control client side. Now for the JavaScript end.

  if (Type)
  {
    Type.registerNamespace("Test.Examples.Client");
  }

  NDI.WebControls.Client.GenericAutoComplete = function(element)
  {
      NDI.WebControls.Client.GenericAutoComplete.initializeBase(this, [element]);

      //Textbox ID value
      this._textboxStupidID = "";
      //Textbox Control
      this._textboxStupid = null;
      //Event Handler
      this._textboxStupidOnFocus = null;
  }

  Test.Examples.Client.ScriptControlExample.prototype =
  {
    //Properties for the _textboxStupidID field
    get_textboxStupidID : function()
    {
        return this._textboxStupidID;
    },

    set_textboxStupidID : function(value)
    {
        this._textboxStupidID = value;
    },

    //Method to be called on focus
    handleOnTextBoxFocus : function(e)
    {
        alert('hi');
    },    

     initialize: function()
    {
        //Find and set the textbox field using the passed in ID
        this._textboxStupid = $get(this._textboxStupidID);

        //Create a delegate to handle the onFocus event
        //Basically creating a means to call the handleOnTextBoxFocus method
        this._textboxStupidOnFocus = Function.createDelegate(this, this.handleOnTextBoxFocus);

        //Set the onFocus event to be handled
        $addHandler(this._textboxStupid, "focus", this._textboxStupidOnFocus);

        Test.Examples.Client.ScriptControlExample.callBaseMethod(this, 'initialize');
    },

    dispose: function()
    {
        //Remove the event handling when this is all said and done.  A clean up.
        $removeHandler(this._textboxStupid, "focus", this._textboxStupidOnFocus);
        Test.Examples.Client.ScriptControlExample.callBaseMethod(this, 'dispose');
    }
  }
  //Same as before
  Test.Examples.Client.ScriptControlExample.registerClass(
    'Test.Examples.Client.ScriptControlExample', Sys.UI.Control);

Oooooooooook So some added things, yes? Well I’ll go through them:

  NDI.WebControls.Client.GenericAutoComplete = function(element)
  {
      NDI.WebControls.Client.GenericAutoComplete.initializeBase(this, [element]);

      //Textbox ID value
      this._textboxStupidID = "";
      //Textbox Control
      this._textboxStupid = null;
      //Event Handler
      this._textboxStupidOnFocus = null;
  }

This is basically declaring the fields for the class and setting their defaults. For this example we’ll need the field to hold the ID in, a field to represent the textbox oject, and a field to represent the delegate later on used to hook up the onFocus Event.

Note: In the next bit you will notice that every method/property is followed by a comma. Do not forget that.

Next, let’s look at the actual class definition. First we’ll start at the properties:

  Test.Examples.Client.ScriptControlExample.prototype =
  {
    //Properties for the _textboxStupidID field
    get_textboxStupidID : function()
    {
        return this._textboxStupidID;
    },  //SEE COMMA

    set_textboxStupidID : function(value)
    {
        this._textboxStupidID = value;
    },  //ANOTHER COMMA

    .....

First thing you’ll notice is the :fuction notation. This is used to declare both methods and properties. Nothing huge, just something to note.

Second thing you might notice is that the property names look a little hard coded and you would be right. Remember how the value send out from the class file was named “textboxStupidID”? Well guess what, for .net to be able to attach that value to a client property, you have to name the properties exactly the same with with either get_ or set_ preceding. Not perfect, but since I’m the only perfect thing in existence, I guess it will have to do.

Now onto the method we need:

    ....

    //Method to be called on focus
    handleOnTextBoxFocus : function(e)
    {
        alert('hi');
    },  

    ....

Wow… uh well it’s a method and it takes in an argument. What the hell do you want from me?

Onto the intialization/Constructorish thing:

     initialize: function()
    {
        //Find and set the textbox field using the passed in ID
        this._textboxStupid = $get(this._textboxStupidID);

        //Create a delegate to handle the onFocus event
        //Basically creating a means to call the handleOnTextBoxFocus method
        this._textboxStupidOnFocus = Function.createDelegate(this, this.handleOnTextBoxFocus);

        //Set the onFocus event to be handled
        $addHandler(this._textboxStupid, "focus", this._textboxStupidOnFocus);

        Test.Examples.Client.ScriptControlExample.callBaseMethod(this, 'initialize');
    },

This might be the least easy part of the whole thing. The first commentented area:

    this._textboxStupid = $get(this._textboxStupidID);

Simple just gets the textbox by the passed in ID. I swear this will get more complicated.

    this._textboxStupidOnFocus = Function.createDelegate(this, this.handleOnTextBoxFocus);

This gives a variable that points to the method we want to use when the focus event fires. Still waiting for complicated…

   $addHandler(this._textboxStupid, "focus", this._textboxStupidOnFocus);

This assigns the method we want, through the delegate, to the focus event on the textbox. Hrm. Ok so not really complicated. Just messy looking. One this to note though is that the events on all the controls won’t have the “on” prefix. So if you can’t figure out why you’re getting an error because it can’t find the event, try dropping the “on”.

And there you have it. Now you are free to run it and be really annoyed by how dumb this example was. Next will be using this for the power of evil by adding an autocomplete control with a processing image.