ASP.Net Server Controls - Up-Down or Spinner Control
|
|
|
|
|
As the title of the article says, we will attempt to explain the steps for
creating a web control and in particluar an up-down control. The functionlaity
of this control is same as MFC's Up-Down control and Window Form's System.Windows.Forms.NumericUpDown
control. We will try to explain the steps in as much as details as we can. A
lot of information is available in .NET framework documentation. It is just how
do you apply that information to create a control that can be useful for your
day to day operations on the weg pages.
There are two ways that you use to build your server control.
-
Composite Control
-
Custom Control
We will not go into details of defining these two types. There is some good
information available in .NET framework documentation about it. Stating it
simply, Composite Constol is a server control that utilizes one or
more existing server controls. And Custom Control is very much
like implementing your windows controls where you will handle the drawing by
providing implementation in OnPaint event. Similarly for Custom
Controls, you will provide your own rendering of control in Render
event handler. We will discuss these details later in the article.
Why did we create custom control?
If you look at the Up-Down control, it is made up of one textbox and two
buttons to increment or decrement the value. That means this can be
accomplished using creating a composite control made up of one asp:TextBox
and two asp:ImageButton controls. But we decied to create custom
control and do our won redering of HTML tags.
We had couple of reason to go this route. Although ASP.Net documentation states
that the server controls can generate a browser compatible HTML tags.
But in our real expereince we have observred that for browsers other than Internet
Explorer, the HTML code genration has been very
inconsistent. The other reason is that the control value will be chnaged with
every click on spinner controls. That means we will have to manage the state on
client side instead of doing post backs for every click. That is not a very
efficient solution because it generates a lot of network traffic. So we will
have to emit client side javascript for managing state.
Lets get started
Define control
Every server control is derived from Control or WebControl.
So the first step is creating a control class that derives from one of these
classes. WebControl itself is derived from Control class.
The advanatage of deriving your control's class is that along with the common
properties of Control, your control will inherit the
implementation of very commonly properties of web controls e.g. Width,
Height, Font etc. So we decied to take advantage of
it and derive our control WebControl class.
[ToolboxData("<{0}:UpDownControl Runat=server>/>")]
public class UpDownControl : WebControl, IPostBackDataHandler
You must have observed that we have speciifed the the control will provide
impelementation of IPostBackDataHandler interface. The reason for
doing is that our control will be saving the value that user has specified by
incrementing or decrementing through spinners and these value has to be coneyed
to the page when Form is posted back for processing. The IPostBackDataHandler
interface provides two metods LoadPostData and RaisePostDataChangedEvent
that our control will implement to handle the post back events from the
containing page.
What properties to define?
Now lets see what all custom properties that we will need other than the
standard ones like Color, Font, etc. Since our
control is an Up-Down control, that means we require some graphics to display
our up and down arrows. So we have two properties that can be used to set/get
URL s for the up-down arrows.
[Bindable(true),
Category("Appearance"),
Description("URL for up arrow image")]
public string UpArrowImgUrl
{
get{return this.m_strUpArrowImg;}
set{this.m_strUpArrowImg = value;}
}
Then we need to provide some means of controlling, on which side of the text
box, the up-down arrows should be displayed. It will be either left or right.
So we need to define a property that will get/set this value. Instead of just
using int type, we decied to use an enum so that this
value can be set descriptively, meaning instead of setting the value as 0 or 1,
we will set it by "Left" and "Right" values.
Then we need to provide some means of controlling, on which side of the text
box, the up-down arrows should be displayed. It will be either left or right.
So we need to define a property that will get/set this value. Instead of just
using int type, we decied to use an enum so that this
value can be set descriptively, meaning instead of setting the value as 0 or 1,
we will set it by "Left" and "Right" values.
public enum ArrowsPosition
{
Left = 0,
Right = 1,
}
[Bindable(true),
Category("Appearance"),
Description("Specify if up-down arows will be displayed on right side or not.")]
public ArrowsPosition ArrowsPositionSide
{
get{return this.m_enumArrowsPosition;}
set{this.m_enumArrowsPosition = value;}
}
Next we need to specify how far the arrows will be placed from the text box.
This is like providing spacing between two two HTML objects. So we deined
property ArrowsSpacing that cab bet set/get.
[Bindable(true),
Category("Appearance"),
DefaultValue(0),
Description("Specify the spacing between text-box and up-down arrows.")]
public int ArrowsSpacing
{
get{return this.m_iArrowsSpacing;}
set{this.m_iArrowsSpacing = value;}
}
Then the properties that affect the values that are saved in the text box. Like
System.Windows.Forms.NumericUpdown we have specified properties that
control min-max and increment values. For more details, look at the source
control provided with the article.
Rendering of control
To render HTML tags for your custom server control it is very essential that
you provide implementation of this method in your control class. This metod
proides chance to the control to render its contents or what ever it wants to
emit to display on the browser. Render method has only one
parameter HtmlTextWriter which acts as an output stream to write
the HTML contents to the web page.
protected override void Render(HtmlTextWriter output)
{
}
Therefore in this method we render all the HTML tags to display the text box
and up-down arrow images. We emit input and img for
rendering these user interfaces. And for displaying the right kind of image, we
get the value from UpArrowImgUrl and DownArrowImgUrl properties.
This rendering has been implemented in two private methods AddTextSpanSection
and AddImageSection of the control class.
strRender.Append("\n<table cellspacing=\"0px\" cellpadding=\"0px\"");
strRender.Append(" height=\"");
strRender.Append(this.Height + "\">");
// Add up arrow row
strRender.Append("\n<tr>");
strRender.Append("\n<td valign=\"bottom\">");
strRender.Append("<img border=0 title=\"Increment value\" src=\"");
strRender.Append(this.m_strUpArrowImg);
strRender.Append("\"");
strRender.Append(" onclick=\"javascript:" + this.UpArrowClickFunction + "();");
strRender.Append("\">");
strRender.Append("\n</td>");
strRender.Append("\n</tr>");
Handle events
Rendering just one part of impleemnting a server control. It is like providing
a body to a human being. Now this thing needs a brain too. And in the world of
web page controls, the brain power is to react to various actions that happen
to it. We are implementing a up-down spinner control, that meams the control
has to react to user's click on up-down arrows. We provide this ability bt
implementation for onclick event of each img tag that
displays arrows. If you look at the code above, we call UpArrowClickFunction
during rendering of img control. This private method returns name
of the client side javascript function that handled click event on
the up-arrow image control. The same way we add client-side event handlers for
down-arrow image control click event.
strRender.Append("<img border=0 title=\"Decrement value\" src=\"");
strRender.Append(this.m_strDownArrowImg);
strRender.Append("\"");
strRender.Append(" onclick=\"javascript:" + this.DownArrowClickFunction + "();");
strRender.Append("\">");
Managing state of the control
The Up-down control has no way of raising post back events. Its not toally
true, but we don't want this control to do post backs for every click on the
arrow images. This emas we will have manage state using some clsine side
scripts. But first we need some place holder wherr we can save the state. We
use the old trick of using hidden input control on the page. This
input will be used to convey the value stored in the text box to the page when
user submits the form or any other control fires postback event.
ASP.Net framework helps in accomplishing this task. There is RegisterHiddenField
method on Page class that automatically registers a hidden field
on the form. And when the page is rederend, this hidden field is rendered on
the page too. We call this method in OnPreRender event handler. RegisterHiddenField
method has two arguments, one is used to specify the unique ID for the hidden
controland the second argument is used to store the initial value for the
control.
protected override void OnPreRender(EventArgs args)
{
base.OnPreRender(args);
if (Page != null)
{
Page.RegisterHiddenField(HelperID, Text);
}
}
In the control implementation, we have provided a private property, HiddenID,
which uses ClientID property of the control to generate a unique
ID for the hidden variable.
How is state communicated to page?
OK, we have renderd the control, handled the click events on arrows and managed
the state. Now the last thing that is left, how does the change in control's
state will be communicated and if the control wants to raise any event if its
state has changed how will that happen? Here are the steps that need to be
followed to wire this all up.
-
The first step is to tell the containing page that our control needs to be
notified when post back happens. And .NET framework, the callbacks are
accomplished through events and delegates.
Page class has a method
RegisterRequiresPostBack that needs to called and handed reference
to our control. This method calls registers our control with Page for
ecieving events.
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (Page != null)
{
Page.RegisterRequiresPostBack(this);
}
}
-
After registering the control with
Page, now we need to figure out
what are we going to do with the event. And most important, how this all
happens. In the start of the article when control was defined, we mentioned
that control class will implementIPostBackDataHandler interface.
When the Page raises postback event for the control, it calls LoadPostData
method on the control. This method has two parameters. The send parameter
contains the collection of all the values that have posted back. Get the posted
value from this collection. And then you can compare the posted value with the
current value to check if the value has changed or not. If the value has
changed, return true from this method indicating that stae has
changed. This will raise chnage event on the control.
bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection)
{
string presentValue = this.Text;
string postedValue = postCollection[HiddenID];
if (!presentValue.Equals(postedValue))
{
this.Text = postedValue;
return true;
}
return false;
}
-
If
LoadPostDatamethod returns true, the Page
will call RaisePostDataChangedEvent on the control giving it a
chance to raise its own evetn or do what ever processing it needs to do.
How to use this control on your page?
In the page where you want to use this control, the first step is to register
it at the top of the page, using @Register directive.
<%@ Register TagPrefix="ThirdEye" Namespace="ThirdEye.WebControls" Assembly="ThirdEye.WebControls"%>
And then include in the page like other server controls. And specify the
property values that you want to assign to the control.
<form id="UpDownControlClient" method="post" runat="server">
<ThirdEye:UpDownControl
Runat="server"
Height="30"
UpArrowImgUrl="images\UpArrow_10x10.gif"
DownArrowImgUrl="images\DownArrow_10x10.gif"
ArrowsPositionSide="Right"
ArrowsSpacing="2"
Text="2"
MaxValue="10"
MinValue="0"
Increment="1"
id="UpDownControl1">
</ThirdEye:UpDownControl>
</form>
What's New?
The implementation of the control has been enhanced to provide more control on the look of
this server control. Also the rendering methods use the framework specified methods on
HtmlTextWrite class instead of building the string from scratch.
|