Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ASP.NET Server Control - Updown Control 1

0.00/5 (No votes)
24 Mar 2002 1  
A tutorial on imeplementing an ASP.Net custom server control

Sample Image - updowncontrol1.gif

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 functionality of this control is the same as MFC's Up-Down control and Windows Forms' 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 a question of how to apply that information to create a control that can be useful for your day to day operations on the web 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 the .NET framework documentation about it. Stating it simply, a composite control is a server control that utilizes one or more existing server controls, and a Custom Control is very much like implementing your Windows controls where you will handle the drawing by providing an implementation of the OnPaint event. Similarly for Custom Controls, you will provide your own rendering of the control in the Render event handler. We will discuss these details later in the article.

Why did we create a 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 a composite control made up of one asp:TextBox and two asp:ImageButton controls. But we decided to create a custom control and do our won rendering of HTML tags.

We had couple of reason to go this route. Although the ASP.NET documentation states that the server controls can generate a browser compatible HTML tags, in our real experience we have observed that for browsers other than Internet Explorer, the HTML code generation has been very inconsistent. The other reason is that the control value will be changed 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 as 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 the Control class. The advantage of deriving your control's class from WebControl</u> is that along with the common properties of <code>Control, your control will inherit the implementation of very commonly used properties of web controls e.g. Width, Height, Font etc. So we decided to take advantage of it and derive our control from the WebControl class.

[ToolboxData("<{0}:UpDownControl Runat="server">/>")]
[Designer(typeof(UpDownControl))]
public class UpDownControl : WebControl, IPostBackDataHandler

You should have observed that we have specified the the control will provide implementation of the 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 conveyed to the page when the Form is posted back for processing. The IPostBackDataHandler interface provides two methods, 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.

[Category("Text")]
[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 decided 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,
}

[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 HTML objects. So we defined the property ArrowsSpacing that we can set/get.

[Category("Appearance")]
[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 and implementation of the Render</u> method in your control class. This method provides a chance to the control to render its contents or what ever it wants to emit to display to the browser. <code>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 this user interface, and for displaying the right kind of image, we get the value from the 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 is just one part of implementing 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 an up-down spinner control; that means the control has to react to user's clicks on the up/down arrows. We provide this ability by  implementing the onclick event of each img tag that displays arrows. If you look at the code above, we call the UpArrowClickFunction during the rendering of the img control. This private method returns the name of the client side javascript function that handled click event on the up-arrow image control. Using the same method we add client-side event handlers for the down-arrow image control's 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. This is not totally true, but we don't want this control to do post-backs for every click on the arrow images. This means we will have manage state using some client-side scripts. But first we need some placeholder where we can save the state. We use the old trick of using a 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 a post-back event.

The ASP.NET framework helps in accomplishing this task. There is RegisterHiddenField method on the Page class that automatically registers a hidden field on the form. And when the page is rendered, this hidden field is rendered on the page too. We call this method in OnPreRender</u> event handler. The <code>RegisterHiddenField method has two arguments, one is used to specify the unique ID for the hidden control and 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 rendered the control, handled the click events on the arrows and managed the state. Now the last thing that is left, how 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. In the .NET framework, the callbacks are accomplished through events and delegates. The Page class has a method RegisterRequiresPostBack that needs to be called and handed a reference to our control. This method call registers our control with Page for receiving 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. At the start of the article when the control was defined, we mentioned that control class will implement the IPostBackDataHandler interface. When the Page raises post-back event for the control, it calls the LoadPostData method on the control. This method has two parameters. The send parameter contains the collection of all the values that have posted back, so we can 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 the state has changed. This will raise change event on the control.
    bool IPostBackDataHandler.LoadPostData(string postDataKey, 
       NameValueCollection postCollection)
    {
        string presentValue = this.Text;
        string postedValue = postCollection[HiddenID];
        // If two values don't match, then update the value.
    
        if (!presentValue.Equals(postedValue))
        {
            this.Text = postedValue;
            // Indicate the state of this control has changed.
    
            return true;
        }
        // If there was no change in the data, then indicate that there is
    
        // no change in state by returning false.
    
        return false;
    }        
            
  • If LoadPostData method returns true, the Page will call RaisePostDataChangedEvent on the control giving it a chance to raise its own event 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="Softomatix" 
    Namespace="ASPNet_Controls" 
    Assembly="ASPNet_Controls"
%>

And then include in it the page just like other server controls, specifying the property values that you want to assign to the control.

<form id="UpDownControlClient" method="post" runat="server">
    <Softomatix: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">
    </Softomatix:UpDownControl>
    <asp:Button Runat="server" ID="wndSubmit" Text="Submit">
    </asp:Button>
</form>

What next?

In this article we have shown the implementation of the control. Through out the code you will observe that we have used attributes on properties and on the class definition. After you have created the control, you would like to include it in the tool box so that you can just use it by dragging from the tool box on to page. In the next article we will extend the implementation so that this control can be included in the server control tool box.

Contact Us

If you any questions or suggestions, you can post the comments in this article or directly contact us at softomatix@pardesiservices.com or visit us at Softomatix.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here