Blog Moved ....

ScottCate.com

community

frenz

my book(s)

my products

TextBoxCounter Atlas Extender

At my User Group meeting last night, I presented on Atlas, and more specifically the control extenders.

Here is a control extender that I wrote for the example, and I think it's a nice little simple control, that is worthy of learning from. First I wrote the sample extender "Disabled Button" that is shown on the Atlas site. From that code, I wrote this control extender. The extender uses a label and a textbox, we watch the TextBox onkeyup event, and then write the length of the text entered in the textbox to the label.

The extender is built with three properties.

  1. TargetControlId (which is a default property, not one I added) that points to the textbox
  2. TargetLabelId is a custom property that I added, and it points to the label
  3. OutputFormat is the final property that asks the user for a string.format like approach to not hardcode the text output.

The finished asp.net code looks like this.

 1: <asp:TextBox ID="TextBox1" runat="server" TextMode="MultiLine" Rows="5" Columns="50"></asp:TextBox><br />
 2: <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
 3:  
 4: <cc1:textboxcounterextender id="TextBoxCounterExtender1" runat="server">
 5:  <cc1:TextBoxCounterProperties
 6:  OutputFormat="{0} Chars"
 7:  TargetControlID="TextBox1" TargetLabelId="Label1" />
 8: </cc1:textboxcounterextender>

The finished screen looks like this. See the 0 Chars? That's because nothing is typed in, as you type, the count updates itself.

Text Box Counter Extender

Once you type, it'll look like this.

Text Box Counter Extender in action

Here are the steps I followed.

First, you have to have the April CTP of Atlas installed, along with the Control Toolkit.

Once this is done, create a project that is of the type "Atlas Control Project" and Name it TextBoxCounter.

NewAtlasProject

Once this is done, the Atlas Control Project template, will create 4 files for you.

TextBoxCounterFiles

These fiels are already stubbed out for you, with great comments on what to do, to get your control extender built. The majority of the work is done in the javascript file, so we'll save that for last.

The designer file, is used for visual studio design support. Remember that an extender is just a server control. It's job is going to be to wire up the control you point it at, and create some javascript that outputs to the page. So my extender targets a textbox. Remember we're watching the textbox (with a client side event) so the textbox is our target control.

Here is my finished TextBoxCounterDesigner.cs file

 1: using System.Web.UI.WebControls;
 2: using System.Web.UI;
 3: using Microsoft.AtlasControlExtender;
 4: using Microsoft.AtlasControlExtender.Design;
 5:  
 6: namespace LearnAtlasExtenders
 7: {
 8:  class TextBoxCounterDesigner : ExtenderControlBaseDesigner<TextBoxCounterProperties, TextBox>
 9:  {
 10:  
 11:  
 12:  }
 13: }

The only change I had to make to this file, is line 8, which originally targets a generic "control", and I changed it to TextBox.

I had to make the same/similar changes to the TextBoxCounterExtender.cs file

 1: using System.Web.UI.WebControls;
 2: using System.Web.UI;
 3: using System.ComponentModel;
 4: using System.ComponentModel.Design;
 5: using Microsoft.AtlasControlExtender;
 6:  
 7: #region Assembly Resource Attribute
 8: [assembly: System.Web.UI.WebResource("LearnAtlasExtenders.TextBoxCounterBehavior.js", "text/javascript")]
 9: #endregion
 10:  
 11:  
 12: namespace LearnAtlasExtenders
 13: {
 14:  [Designer(typeof(TextBoxCounterDesigner))]
 15:  [ClientScriptResource("TextBoxCounter", "TextBoxCounterBehavior", "LearnAtlasExtenders.TextBoxCounterBehavior.js")]
 16:  public class TextBoxCounterExtender : ExtenderControlBase<TextBoxCounterProperties, TextBox>
 17:  {
 18:  }
 19: }

Next is to create our customer properties in TextBoxCounterProperties.cs

 1: using System.Web.UI.WebControls;
 2: using System.Web.UI;
 3: using System.ComponentModel;
 4: using Microsoft.AtlasControlExtender;
 5:  
 6: namespace LearnAtlasExtenders
 7: {
 8:  // TODO Set this to be your default extender property name
 9:  //
 10:  [DefaultProperty("TargetLabelId")]
 11:  public class TextBoxCounterProperties : TargetControlPropertiesBase<TextBox>
 12:  {
 13:  [IDReferenceProperty(typeof(Label))]
 14:  public string TargetLabelId
 15:  {
 16:  get { return GetPropertyStringValue("TargetLabelId"); }
 17:  set { SetPropertyStringValue("TargetLabelId", value); }
 18:  }
 19:  
 20:  public string OutputFormat
 21:  {
 22:  get { return GetPropertyStringValue("OutputFormat"); }
 23:  set { SetPropertyStringValue("OutputFormat", value); }
 24:  }
 25:  }
 26: }

You can see here, we're creating two properties, each wtih a get and set routine. The SetPropertyStringValue and GetPropertyStringValue are methods built into the base class, we don't have to create them, just use them.

Finally, our .js file -- TextBoxCounterBehavior.js

 1: Type.registerNamespace('LearnAtlasExtenders');
 2:  
 3: LearnAtlasExtenders.TextBoxCounterBehavior = function() {
 4:  LearnAtlasExtenders.TextBoxCounterBehavior.initializeBase(this);
 5:  
 6:  // TODO : (Step 1) Add your property variables here 
 7:  //
 8:  var _TargetLabelId;
 9:  var _OutputFormat;
 10:  
 11:  this.initialize = function() {
 12:  LearnAtlasExtenders.TextBoxCounterBehavior.callBaseMethod(this, 'initialize');
 13:  
 14:  this.control.element.attachEvent('onkeyup', Function.createDelegate(this, this._onkeyup));
 15:  this._onkeyup();
 16:  }
 17:  
 18:  this.dispose = function() {
 19:  LearnAtlasExtenders.TextBoxCounterBehavior.callBaseMethod(this, 'dispose');
 20:  }
 21:  
 22:  this.getDescriptor = function() {
 23:  var td = LearnAtlasExtenders.TextBoxCounterBehavior.callBaseMethod(this, 'getDescriptor');
 24:  
 25:  // TODO: (Step 2) Add your property declarations here. 
 26:  //
 27:  td.addProperty('TargetLabelId', String);
 28:  td.addProperty('OutputFormat', String);
 29:  return td;
 30:  }
 31:  
 32:  this.get_TargetLabelId = function () {
 33:  return _TargetLabelId;
 34:  }
 35:  
 36:  this.set_TargetLabelId = function(value) {
 37:  _TargetLabelId = value;
 38:  }
 39:  
 40:  this.get_OutputFormat = function () {
 41:  return _OutputFormat;
 42:  }
 43:  
 44:  this.set_OutputFormat = function(value) {
 45:  _OutputFormat = value;
 46:  }
 47:  
 48:  
 49:  // These are helper functions for communicating state back to the extender on the
 50:  // server side. They take or return a custom string that is available in your initialize method
 51:  // and later.
 52:  //
 53:  this.getClientState = function() {
 54:  var value = LearnAtlasExtenders.TextBoxCounterBehavior.callBaseMethod(this, 'get_ClientState'); 
 55:  if (value == '') value = null;
 56:  return value;
 57:  }
 58:  
 59:  this.setClientState = function(value) {
 60:  return LearnAtlasExtenders.TextBoxCounterBehavior.callBaseMethod(this, 'set_ClientState',[value]); 
 61:  }
 62:  
 63:  //custom behavior
 64:  this._onkeyup = function() {
 65:  var lbl = document.getElementById(_TargetLabelId);
 66:  var regex = /\{0\}/g;
 67:  lbl.innerHTML = _OutputFormat.replace(regex, this.control.element.value.length);
 68:  //lbl.innerHTML = _OutputFormat.split("{0}").join(this.control.element.value.length);
 69:  }
 70:  
 71: }
 72:  
 73: LearnAtlasExtenders.TextBoxCounterBehavior.registerSealedClass('LearnAtlasExtenders.TextBoxCounterBehavior', Microsoft.AtlasControlExtender.BehaviorBase);
 74: Sys.TypeDescriptor.addType('TextBoxCounter', 'TextBoxCounterBehavior', LearnAtlasExtenders.TextBoxCounterBehavior);

On lines 8,9 we define our javascript holder properties.

On lines 14-15 we wire up on onkeyup event to this.control, which is the TargetControl, which is the TextBox we point our extender at.

On lines 27-28 we add our properties to the base method (decalred on line 23

On lines 32-46 we write javascript get/set routines for our 2 properties

And finally on line 64-69 we write our onkeyup function, that changes the label text with a javascript routine. (Special thanks to my user group meeting, who helped my put this together with a regex replace as seen on lines 66-67, I originally had a javascript .split().join() routine that worked, but their solution was more elegant, so mine's commented out).

Save All.

Compile

Set a reference in your web project, to this control extender project

Add the extender to your tool box, and you'll forever have an extender that is easy to use, that will count the length of the text in a textbox.

Hope this helps as a good introduction to building custom control extenders.

Comments

Rodrigo Diniz said:

This should be only an example to learn hot to make an extender. You dont have to go to all that trouble to have this funcionality with only javascript.
# May 4, 2006 10:49 AM

Scott Cate said:

That's correct. Why go through all this trouble when you can just hardcode some javascript on the page to get the same job done? The reason, and the benefit of the extender is that it's a server control, that is smart enoguht to wire up other server controls. So now that I've written this, anyone that wants to have a textbox counter control, can just use mine, and they don't have to write **ANY** javascript to accomplish that.

This is also a simple control, meant to be a simple example on how to build extenders.
# May 4, 2006 11:00 AM

Andrey Skvortsov said:

Server control for pure client functionality?And how about Atlas xml script,as I can understand it dosen't require imperative code too.So,you only really need .js file...but implementation of client side "control" is not perfect IMHO,look at jquery.com or some other client side library for some other examples.You don't have to write custom controls to write elegant code-not always.

Regards.
# May 4, 2006 1:14 PM

Scott Cate said:

The point of extenders is not to make it difficult to write client side code, it's to make it wasy to write up server controls without having to think about the javascript.

I wouldn't write this control for me to be used once. I'd write it to distribute it, so others can enjoy the fruits of my labor, without having to have the knowledge, just getting the benefits.

It's not just a server control for pure client functionlity, it's the disconnection of javacsript and the UI. When I wrote this, I had no idea what the textbox was, and what the label was.

But now that it's written, I can' use it over and over.

Also, this is an educational sample, other extenders are MUCH more eloborate, giving a much larger win, to just consume, and not have to write on your own.
# May 4, 2006 1:26 PM

Andrey Skvortsov said:

<q>
The point of extenders is not to make it difficult to write client side code, it's to make it wasy to write up server controls without having to think about the javascript.
</q>
Don't you think that strong client side(ajax;-) controls can make most of the server side controls obsolete?
I beleive,that all this server side extenders is nice feature per se but it's transient by nature.Most of the server controls need to be rewriten to take full advantage of xmlhttprequest(out of band connections) and accent must be set on client side programming model consistency and easy of use.Anyway, you double you work as developer only to smooth server side experience.By the way,client side xml script programming model looks much more appealing to me;-)

Thanks,great example.
# May 4, 2006 2:05 PM

Scott Cate said:

Andrey,

Extenders are not necessarily used for ajax on the client. Most of the extenders I've seen written are to facilitate the ease of use of using DHTML.

Bottom line, extenders are for re-use, and decoupeling the javascript business logic from the page controls.
# May 4, 2006 2:10 PM

Scott Cate said:

Andrey,

I disagree that controls have to be re-written to take advantage of out of band processes.

Have you looked at the atlas:updatepanel? This is a phenominal control, that hijacked the __dopostback, giving you out of band processing for free.
# May 4, 2006 2:12 PM

jc said:

Right. The UpdatePanel Rocks! Just add it around any group of controls for a quick and dirty ajax enabled app.
# May 4, 2006 2:30 PM

rodrigo diniz said:

My point on my inicial comment is that you don't need atlas to make a control like this.Using atlas for things like that might make someone think that you are using a XMLHTTPRequest , wich you are not using.
You are just adding javascript funcionality to a control wich could be done , in my opinion , a lot easier with an user control.
# May 5, 2006 9:05 AM

Scott Cate said:

Rodrigo,

I agree that the name "Atlas" in the title of control extenders could be misunderstood to be an ajax component somehow.

I disagree that you could create the same functionality that this control provides in a cuser control easier. Here's why. The exnteder control, doesn't know about the textbox or the label. So to do this in a user control, you're have to have textbox and label properties that are set at run time (or if set at design time, get the clientid at runtime) and then output the javascript at that time.

An extender is better for re-use, especially across applications.
# May 5, 2006 10:42 AM

Azhar said:

Hi Scott Where can I find the source code for this TextBoxCounter Atlas Extender
# June 29, 2006 10:09 PM

Steve Gentile said:

Thanks for your sample.

I was stuck using a multi-control extender and this attribute:

[IDReferenceProperty(typeof(Label))]

Got me past that

Thanks again!

# May 2, 2007 11:38 AM

schlub said:

I have been trying to do this using javacript via the OnTextChanged event without success. I already use the AJAX Control Toolkit, so this extender seems interesting. Unfortunately the code you posted is truncated and I am unable to build my own version. Could you please provide a link to the source code?

# September 16, 2007 11:03 PM