Ajax Control Library: Autocomplete Control to Scriptcontrol

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

Ok so in part four I showed you how to create a webcontrol from the AutoComplete control, so now it’s time to take the first four lessons and combine them. Now it’s time to have an autocomplete script control. The first part is really simple, just like before you have to:

  • Inherit from ScriptControl
  • Override the GetScriptDescriptors and GetScriptReferences methods
  • Create the .js file
  • Make the .js file an embedded resource
  • Update the assemblyinfo file in the Properties folder
  • Add the minimal needed javascript

So it will look something like this:

public class AutocompleteScriptControl : ScriptControl, 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;
  }

  protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
  {
    ScriptControlDescriptor desc =
      new ScriptControlDescriptor("Test.Examples.Client.AutocompleteScriptControl", ClientID);
    desc.AddProperty("textboxTargetID", textboxTarget.ClientID);
    desc.AddProperty("autocompleteID", autoComplete.ClientID);

    return new ScriptDescriptor[] { desc };
  }

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

}

And the script file:

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

Test.Examples.Client.AutocompleteScriptControl = function(element)
{
    Test.Examples.Client.AutocompleteScriptControl.initializeBase(this, [element]);

    this._autocompleteID = "";
    this._textboxTargetID = "";

    this._autoComplete = null;
    this._textboxTarget = null;
}

Test.Examples.Client.AutocompleteScriptControl.prototype =
{
    get_autocompleteID: function()
    {
        return this._autocompleteID;
    },

    set_autocompleteID: function(value)
    {
        this._autocompleteID = value;
    },

    get_textboxTargetID: function()
    {
        return this._textboxTargetID;
    },

    set_textboxTargetID: function(value)
    {
        this._textboxTargetID = value;
    },

    initialize: function()
    {
        this._autoComplete = $get(this._autocompleteID);
        this._textboxTarget = $get(this._textboxTargetID);

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

    dispose: function()
    {
        Test.Examples.Client.AutocompleteScriptControl.callBaseMethod(this, 'dispose');
    }
}

/***********************************/

Test.Examples.Client.AutocompleteScriptControl.registerClass
    ('Test.Examples.Client.AutocompleteScriptControl', Sys.UI.Control);

And now this is the bare minimum needed to get this done.

Dynamic Markup Property Collection for a WebControl

I wasn’t exactly sure how to write this title as it’s not easy to explain in a short sentence, however I can say that I am completely embarrassed by the result and may have to consider hiring a title consultant. However, for now it will have to do.

Ok so what is this about? Well let’s say you have the control from the last example and you don’t want a list of controls in the markup, but maybe you want a list of strings set in the markup. (Say for a list of settings) Or even better, let’s say you have controls already on the page but you want our ParentControl (Yes I said “our” but no I’m not proposing… yet) to do something with those controls. Say we want to have the ParentControl to assign some javascript to certain buttons on the page. Ok yeah, kind of dumb but that’s the example I made. I supposed you could think of it like this: You want a validation control to validate multiple controls but you want to give it the names of the controls in the markup. Anyways, let’s stick with the example I already have.

First off you’ll need to create a new class, something that you want to hold the information in. (Although truth be told I could do something way easier and just use a list of strings, but this should prove more interesting) Here’s the one I have:

  namespace Test.Frontend.ControlInControl
  {
    public class ChildControlDefinition
    {
        public String ControlName { get; set; }
    }
  }

Pretty sweet? Eh? Eh? No? Ok. So now we have a class, and that’s like wow. Now with the same attributes from the old example, build the WebControl:

    [ParseChildren(true)]
    [PersistChildren(false)]
    public partial class ParentControl : UserControl
    {
        public List<ChildControlDefinition> NamedControls { get; set; }
    }

Holy smokes, that just got way complicated compared to the last example. I added a whole new property NamedControls that is a list of the ChildControlDefinition class we created. Now you might notice that the AddedControls property from the old example isn’t there anymore. I just removed it to simplify this one.

And then there’s the markup:

    <asp:Button ID="mainButton" runat="server" />
    <uc1:ParentControl ID="ParentControl1" runat="server" >
        <NamedControls>
            <examples:ChildControlDefinition ControlName="mainButton" />
        </NamedControls>
    </uc1:ParentControl>

Now you might notice two things here:

1) There is a mark up section named “NamedControls” just like the collection. Can you guess why?

2) examples:ChildControlDefinition : This is the class type name and the TagPrefix for where the class is. For this to work, you have to register the assembly the class is in EVEN IF the class is in the web application assembly. So it would look like this:

<%@ Register Assembly="Test.Frontend" Namespace="Test.Frontend.ControlInControl" TagPrefix="examples" %>

Now what does this do for anyone? Possibly it prevents you from dying a little inside but it also allows you to find those controls within the usercontrol… Ah buh? Don’t worry, I’ll show you!

    [ParseChildren(true)]
    [PersistChildren(false)]
    public partial class ParentControl : UserControl
    {
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            NamedControls.ForEach(ConnectButtonToClick);
        }

        private void ConnectButtonToClick(ChildControlDefinition control)
        {
            Button foundControl;

            foundControl = Page.FindControl(control.ControlName) as Button;

            if (foundControl != null)
            {
                foundControl.OnClientClick = "alert('clicked'); return false;";
            }
        }

        public List<ChildControlDefinition> NamedControls { get; set; }
    }

See what I did there? SOMETHING COMPLETELY USELESS and yes you too can now have that power. However, there might be something worth taking home from that example. From the list of ChildControlDefinition I was able to get the control name that I wanted to attach the worthless alert script to and attach it. How was that done? Well I just iterated through the ChildControlDefinition list, used the Page.FindControl method to find the control, and then set the OnClientClick method.

Now this example is pretty stupid but I hope you can see the overall value of this. One would be a paging controller that has a list of paging control names that it iterated through and sets various events so they can all work in harmony like one great big continuation of the Hands Across America utopia that just didn’t seem to make it.

Now to spin it Nintendo User Manual Style:

Think you are bad enough for Dynamic Controls? Take grab of the power within and dare to conquer! Take a chance, the world can be your for the taking! Dynamic is waiting…

Return False from a ScriptControl method and why I hate Firefox…

I have to admit that it might be IE that is wrong, but I don’t know. That and I do kind of hate Firefox so I’ll stand by the title anyhow.

This is a little off the current beaten path, but does deal with the current ScriptControl theme so I thought I’d put it in here.

Here’s the problem: You have a script control, a button, and want to have the person confirm before it’s allowed to postback. Easy right? If this were the non script control way you would do this:

Javascript:

    function ReturnFalse()
    {
      var confirmed = confirm('Is there anyone more awesomer that Sean?');
      return confirmed;
    }

Markup:

  <asp:Button ID="whoCares" runat="server" OnClientClick="return ReturnFalse();" >

Class Stuff:

  whoCares.Click += whoCares_Click;
  ...
  private void whoCares_Click(object sender, EventArgs e)
  {
    throw new NotImplementedException();
  }

Basically what I’ve done is made it so that if it doesn’t return false, an exception is thrown. Easy test. Works in both browsers right? Brilliant.

Now for the script control:

  handleLnkDeleteButtonClick: function(e)
  {
      var confirmed = confirm('Is there anyone more awesomer that Sean?');
      return confirmed;
  },

 initialize: function()
 {
    this._lnkDeleteButton = $get(this._lnkDeleteButtonID);
    this._lnkDeleteButton.idpicker = this;

    //HOOK BEGINS HERE
    this._lnkDeleteButtonClick = Function.createDelegate(this, this.handleLnkDeleteButtonClick);
    $addHandler(this._lnkDeleteButton, "click", this._lnkDeleteButtonClick);
    //END HOOK HERE

    NDI.WebControls.Client.PersonalMessageTypePicker.callBaseMethod(this, 'initialize');
},

Now that looks ok right? The method pops up the confirmation button alert dialogue thingy and returns the answer but guess what, you’re wrong. So wrong. In IE this works but in Firefox, sorry no deal. Firefox will ignore anything returned if the “return” keyword is not in the markup. Well from here there’s no real markup so how the hell would I do that? Well yes Virginia there is a Santa Clause and he lives in e.preventDefault();

It’s actually really simple, one small change in the handling method. You replace return X with e.preventDefault();

  handleLnkDeleteButtonClick: function(e)
  {
    var confirmed = confirm('This will delete the currery Message category
      and move all messages to the Oprhan cataegory.  Allow?');
    if (!confirmed)
    {
        e.preventDefault();
    }
  },

And now it works. Yes you can thank me with large donations of money or cheese.

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.

Creating Script Controls And Love (ASP.Net ScriptControl)

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

So this will be the first of a couple posts having to do with Script Controls, the Ajax Control Tool Kit AutoComplete control, and Javascript. Now to get started, what’s a script control?

I’m glad you asked. Basically have you ever created a user control that required Javascript to go along with it? What if the control was in another assembly? Oh woe is you. Well you could require the .js file to be placed in the web project and included on the page. Meh. OR you could use a script control YAY! How does that help? Well it allows you to “attach” a javascript file to a control in a class library, no includes needed. Not only that, but you can pass values from the control to the client and use them with Javascript. (This includes ids of controls in the control.)

So what’s needed? Well…
1. Create a class library, Call it Test and make the default Namespace Test.Examples

2. Create a folder to hold the class file and the JavaScript file. Let’s call it ScriptControlTemplate for now.

2. Create the control class file and the needed JavaScript file. Call them both ScriptControlExample. (For right now it will be easier if they have the same name but they don’t have to be.)

3. Right click the JavaScript Files -< Properties -< Build Action: Embedded Resource

4. Go to the Properties folder in the class Library and open the AssemblyInfo.cs. Add this line:

[assembly: WebResourceAttribute
    ("Test.ScriptControlTemplate.ScriptControlExample.js", "text/javascript")]

Now you might notice something, that almost looks like the actual folder location and it kind of is. In order for this line to work, it has to match the folder location like when Visual Studios creates a Namespace automatically. It uses the directory name as the final part of the namespace. Although to note, the Namespace of the class DOES NOT have to match this. It just has to reflect the directory structure of where the .js file is.

Ok so now we have a blank .cs file and a blank .js file. Woo hoo. Now what? Well first you need to have the class inherit from System.Web.UI.ScriptControl and override two protected methods

    public class ScriptControlExample : ScriptControl , INamingContainer
    {
        protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        {
            ScriptControlDescriptor desc =
                 new ScriptControlDescriptor("Test.Examples.Client.ScriptControlExample", ClientID);
        }

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

        }
    }

Ok so I cheated a little and put in two lines that you will need anyhow. I figured it would save some time, mainly mine. Cause let’s face it, I don’t really care about yours.

The first method GetScriptDescriptors is where we will be putting any values we want to send to the Client. The second just tells the system where the JavaScript file is to load. For now you won’t be doing anything with the second.

Ok now on to the Javascript file. Basically you have some cannon methods to add just to get the thing to work, and I’ll be honest: It’s extremely complicated and you probably won’t be smart enough to figure it out.

//Set the namespace
if (Type)
{
    Type.registerNamespace("Test.Examples.Client");
}

//Won't be doing much with this for the examples I have
Test.Examples.Client.ScriptControlExample = function(element)
{
    Test.Examples.Client.ScriptControlExample.initializeBase(this, [element]);
}

//This is where you basically define the class
Test.Examples.Client.ScriptControlExample.prototype =
{
    //Where you want to place constructor like code
    initialize: function()
    {
        Test.Examples.Client.ScriptControlExample.callBaseMethod(this, 'initialize');
    },

    //Remove events and such here
    dispose: function()
    {
        Test.Examples.Client.ScriptControlExample.callBaseMethod(this, 'dispose');
    }
}
Test.Examples.Client.ScriptControlExample.registerClass('Test.Examples.Client.ScriptControlExample',
   Sys.UI.Control);

Phew that was complicated right?

Ok so now you have what you need to create a script control. Run it and well nothing will really happen except you shouldn’t get errors.

Now just create a new page in a web application (Or project if you’re a loser) and just do this realy quick:

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

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="smMain" runat="server" />
        <test:ScriptControlExample ID="sceTest" runat="server" />
    </div>
    </form>
</body>
</html>

Run it and…..NOTHING! Surprise! Actually there is something, just nothing visual yet. Next post will get to that. Sucker.

One thing you can take away from this: A drinking game. Take a drink everytime you see the word ‘Ok’ in this post. I dare you.