ASP.NET Custom Server Controls: Dynamically Expandable Round Cornered Button

This article introduces you to creating your own dynamically expandable round cornered button control in ASP.NET.

Contributed by
Rating: 4 stars4 stars4 stars4 stars4 stars / 24
October 10, 2005
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

A downloadable file for this article is available here.

The sample downloadable solution (zip) is entirely developed using Visual Studio.NET 2003 Enterprise Architect on Windows Server 2003 Standard Edition.  But, I am confident that it would work with other versions of Windows (which support .NET 1.1) as well.

Introduction

Everyone knows that ASP.NET already contains a flexible Table control to design round cornered boxes with ease.  But, what if we wanted to convert that “table” to a button, which supports “postback” and other stuff?  That forces us to examine the development of a new “dynamically expandable round cornered button.”

In my sense, “dynamically expandable” means the button has to expand (either vertically or horizontally) based on the content we provide to the button.  It should be supported even at design-time.  “Round cornered” means the button should accept different graphic images at all sides and corners to make it look like it is “round cornered.”  And finally, it should support “posback”, “viewstate” and other stuff.

In my previous articles, I already introduced how to design and implement our own Custom Server Control from the scratch.  If you are quite new to the concept of Custom Controls in ASP.NET, I suggest you to go through my previous articles before going through this article.  This will also be similar to my “round cornered box control” article (within the same series) in several aspects, but with some modifications.

Even though I could use System.Web.UI.Control as the base class for my button control, I selected System.Web.UI.WebControls.WebControl as the base class, just because of the convenience and ease.  Apart from all of these, we will also look into “<EditorAttribute>” available in ASP.NET.

System.Web.UI.Control class has only few rendering methods which could be overridden.  This gives us less flexibility in developing the custom control, when we compare with the rendering methods available in System.Web.UI.WebControls.WebControl class. Of course, some of the most important properties (like width, height, font, and so on) of the WebControl class get inherited to our control.

In this scenario, I even exposed nine properties to fix border (rounded) images for our control.  Before talking too much about the control, let us go through the implementation first.  We will create a Visual Studio.NET 2003 solution throughout this article with more than one project.

Exposing properties for every side (including corners) of button

As you know, every side of the box needs to be provided with some image (even the corners).  The following are the properties which handle all of those images (paths of those images).

    Property ImageTopLeftURL() As String
    Property ImageTopMiddleURL()
As String
    Property ImageTopRightURL()
As String
    Property ImageMiddleLeftURL()
As String
    Property ImageMiddleMiddleURL()
As String
    Property ImageMiddleRightURL()
As String
    Property ImageBottomLeftURL()
As String
    Property ImageBottomMiddleURL()
As String
    Property ImageBottomRightURL() As String

I hope all of the above are simple to read and understand. I declared each of those properties with an attribute at the top as follows:

<EditorAttribute(GetType(System.Web.UI.Design.UrlEditor), GetType
(System.Drawing.Design.UITypeEditor))>

The above is the most exciting statement used with all the previous properties.  The “EditorAttribute” is mainly helpful for the Visual Studio.NET designer to facilitate an editor for the value being provided to that property (similar to the “Font” property). 

In this case, we attach a dialog box (Visual Studio.NET supported open dialog) for the property, to facilitate the developer in selecting a respective image from folders within the web application hierarchy. If we don’t specify the above attribute, the developer needs to remember and type the entire path of the image within the property window of Visual Studio.NET designer, as opposed to simply selecting the image file.

Starting with Table

The default “Render” method (when not overridden by us) usually calls the “RenderBeginTag” before calling any other methods.  Let us examine the “RenderBeginTag” method in my control step by step:

MyBase.AddAttributesToRender (writer)
        writer.AddAttribute(HtmlTextWriterAttribute.Border,
"0px")
        writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding,
"0px")
        writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing,
"0px")
        writer.AddStyleAttribute("text-Align", "center")
        writer.AddStyleAttribute("vertical-Align", "middle")
        writer.AddAttribute(HtmlTextWriterAttribute.Onclick,
Page.GetPostBackEventReference(Me, String.Empty))
        writer.AddStyleAttribute("cursor", "hand")
        writer.RenderBeginTag(HtmlTextWriterTag.Table)

Within the above method, I am trying to call the “MyBase.AddAttributesToRender(Writer)” statement to add any design-time attributes to my control.  This is a must when we use the control with the Visual Studio.NET designer.  You can also override with any other attributes you want (as shown in my previous articles of the same series).  From all the above statements, the conclusion would be something like this: I am trying to render a “<TABLE>” tag with some attributes. Note that the tags have not yet been closed!

One of the most important statements to focus on from the above is:

writer.AddAttribute(HtmlTextWriterAttribute.Onclick,
Page.GetPostBackEventReference(Me, String.Empty))

That is the main trick I played to convert the “table” to “button.”  Once the above statement is executed, it now supports a post back when clicked!

Starting simply with “<TABLE>” is not enough. We need to frame a 3x3 set of cells within the table.  I call the first row of cells the header, second row of cells the body (or content) and third row the footer for better understanding.

In our next section, we shall see how to implement the header.

Creating the header

The header part mainly contains three cells (top left, top middle, top right).  We need to create a new row with three cells and fill these cells with their respective images.  The following is the next code fragment (continuation from the above code fragment within the same “RenderBeginTag” method).

writer.RenderBeginTag(HtmlTextWriterTag.Tr)
       
'top left
        writer.AddAttribute(HtmlTextWriterAttribute.Width, "0px")
        writer.AddAttribute(HtmlTextWriterAttribute.Height,
"0px")
        writer.RenderBeginTag(HtmlTextWriterTag.Td)
        If Me.ImageTopLeftURL.Trim.Length > 0
Then
            writer.AddAttribute(HtmlTextWriterAttribute.Src,
Me.ImageTopLeftURL.Trim)
            writer.AddAttribute(HtmlTextWriterAttribute.Border,
"0px")
            writer.RenderBeginTag(HtmlTextWriterTag.Img)
            writer.RenderEndTag()
       
End If
        writer.RenderEndTag() 'td

The above code fragment starts a single row (first row in the table) and creates the first cell (top left cell).  Once the image specification (path of the image) for the cell is provided, I embed it into the cell using the “<img>” tag.  By default, I fixed the width to “0px” allowing it to fit automatically based on the image size.

        'top middle
        writer.AddAttribute(HtmlTextWriterAttribute.Width,
"100%")
        writer.AddAttribute(HtmlTextWriterAttribute.Height,
"0px")
        If Me.ImageTopMiddleURL.Trim.Length > 0
Then
            writer.AddStyleAttribute
(HtmlTextWriterStyle.BackgroundImage, "url(" &
Me.ImageTopMiddleURL.Trim & ")")
            writer.AddStyleAttribute("background-repeat",
"repeat-x")
       
End If
        writer.RenderBeginTag(HtmlTextWriterTag.Td)
        writer.RenderEndTag() 'td

As the middle cell may expand according to the content, I set it up so that the image (for the top middle cell) would be set as the background image.  I also added a style attribute which makes it repeat when expanded.  And finally, the following code fragment is for the last cell (top right).

        'top right
        writer.AddAttribute(HtmlTextWriterAttribute.Width, "0px")
        writer.AddAttribute(HtmlTextWriterAttribute.Height,
"0px")
        writer.RenderBeginTag(HtmlTextWriterTag.Td)
        If Me.ImageTopRightURL.Trim.Length > 0
Then
            writer.AddAttribute(HtmlTextWriterAttribute.Src,
Me.ImageTopRightURL.Trim)
            writer.AddAttribute(HtmlTextWriterAttribute.Border,
"0px")
            writer.RenderBeginTag(HtmlTextWriterTag.Img)
            writer.RenderEndTag()
       
End If
        writer.RenderEndTag()
'td
        writer.RenderEndTag() 'tr

The above is very similar to the first cell (top left) and I don’t think that I need to explain it much.  But the important issue is that I closed the header row with the last statement. 

Creating the body (content)

Creating the body would also be very similar to the header.  But, we need to play a trick to allow the developer to write his own message during the rendering of the control.  This is the only complex issue to handle in our control.

Let us first add the first cell of the row (Middle left) using the following code fragment (a continuation from the above code fragment within the same “RenderBeginTag” method).

writer.RenderBeginTag(HtmlTextWriterTag.Tr)
       
'middle left
        writer.AddAttribute(HtmlTextWriterAttribute.Width, "0px")
        writer.AddAttribute(HtmlTextWriterAttribute.Height,
"0px")
        If Me.ImageMiddleLeftURL.Trim.Length > 0
Then
            writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundImage, "url(" &
Me.ImageMiddleLeftURL.Trim & ")")
            writer.AddStyleAttribute("background-repeat",
"repeat-y")
       
End If
        writer.RenderBeginTag(HtmlTextWriterTag.Td)
        writer.RenderEndTag() 'td

The above completes the creation of the first cell.  Now we create the second cell without closing it.  I just open the middle cell with “<td>” tag and don’t close it at all.  This allows the developer to write his own content according to his needs.  Then who will close the cell? The next section will explain that.          

'middle middle (center)
        writer.AddAttribute(HtmlTextWriterAttribute.Width,
"100%")
        writer.AddAttribute(HtmlTextWriterAttribute.Height,
"100%")
        If Me.ImageMiddleMiddleURL.Trim.Length > 0
Then
            writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundImage, "url(" &
Me.ImageMiddleMiddleURL.Trim & ")")
       
End If
        writer.RenderBeginTag(HtmlTextWriterTag.Td)

You can look at the last statement.  It only opens “<TD>” and does not close it yet.

Closing the body (content) and creating the footer

In the previous section, we already opened the center cell.  But we didn’t close yet. In this section we are going to close it.

writer.RenderEndTag() 'td
       
'middle right
        writer.AddAttribute(HtmlTextWriterAttribute.Width, "0px")
        writer.AddAttribute(HtmlTextWriterAttribute.Height,
"0px")
        If Me.ImageMiddleRightURL.Trim.Length > 0
Then
            writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundImage, "url(" &
Me.ImageMiddleRightURL.Trim & ")")
            writer.AddStyleAttribute("background-repeat",
"repeat-y")
       
End If
        writer.RenderBeginTag(HtmlTextWriterTag.Td)
        writer.RenderEndTag()
'td
        writer.RenderEndTag()
'tr
        writer.RenderEndTag() 'table

The above is quite straightforward for closing the previously opened “<TD>” tag.  But the above code is not part of the “RenderBeginTag” method.  Instead, it is from the “RenderEndTag” method.  We further proceed to create the footer row by adding the same type of code (with very few modifications) available in the “Creating the Header” section to the above code fragment.  As the coding is quite similar, you can get it from the downloadable.

Everything up until now is quite okay.  How did I implement the trick to get the “developer specified content” into my control?  This is from the following code fragment.

Protected Overrides Sub RenderContents(ByVal writer As
System.Web.UI.HtmlTextWriter)
        writer.Write(Text)
    End Sub

Even though it is only three lines, it should be considered the heart of the control.  In other words, when the control is getting rendered, it first executes the “RenderBeginTag” method followed by the “RenderContents” method and finally “RenderEndTag”.  For a detailed explanation of “rendering” issues, I suggest you to refer my previous articles of the same series.

How did I handle the postbacks?

Another extension to the same control is the following code fragment.  Try obrserving it.

Protected Overrides Sub Render(ByVal writer As
System.Web.UI.HtmlTextWriter)
        If Not Page
Is Nothing Then
            Page.VerifyRenderingInServerForm(Me)
       
End If
        MyBase.Render(writer)
    End Sub

The above implementation of the “Render” method ensures that the placement of your control should be within the “<FORM>” tag itself.  It will automatically raise an error if not placed within the “<FORM>” tag.  You should also observe the second statement, “MyBase.Render(writer)”.  It asks to proceed with rendering as usual without considering any other issue.

The next question would be, how did I implement the “OnClick” event within my control?  The following statements at various locations made it possible:

Public Event Click As EventHandler

Public Sub RaisePostBackEvent(ByVal eventArgument As String)
Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent
        OnClick(EventArgs.Empty)
    End Sub

Protected Overridable Sub OnClick(ByVal e As EventArgs)
        RaiseEvent Click(Me, e)
    End Sub

It is the concept of both eventing and delegating. The above statements made it possible to implement ‘postback’ing together with delegating.  I suggest you go through OOPS in .NET, if you are not familiar with events and delegates.

Even though I started emitting exclusive HTML using “writer” methods, you can also do the same by extending your control from an already existing “table” web control.  If you directly extend it from a “table” web control, you can work with an almost .NET based ASP.NET framework model to add table rows and cells, which looks convenient and even readable.  But then we need to understand a bit about the “rendering” issues of child controls within the parent control.

I leave it to the developers to further enhance the same control.  The areas of improving the same control would include eventing, JavaScript for mouse hovers, data-binding, and so forth.  Good luck.

Any comments, suggestions, bugs, errors, feedback etc. are highly appreciated at jag_chat@yahoo.com.

 

blog comments powered by Disqus
ASP.NET ARTICLES

- Implementing ASP.NET 4.0 Page.MetaDescriptio...
- ASP.Net Development Tips
- Intro to Sessions in ASP.Net
- Google Maps API Introduction in ASP.NET usin...
- Creating an ASP.NET 3.5 Gridview Image Galle...
- Encrypt QueryString in ASP.NET 3.5 using VB....
- ASP.NET 3.5 Drop Down List Controls
- Connect to Access Database with ASP.Net
- Secure Audio Streaming with ASP.Net and Flash
- Dynamic Sitemap and Navigation in ASP.Net
- Implement Gzip and Deflate Compression in AS...
- Run ASP.Net in Ubuntu with Apache
- ASP.Net Mono Website Contact Forms
- ASP.Net URL Rewriting Methods
- Murach`s ASP.NET 4 Web Programming with C# 2...

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 11 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials