ASP.NET Custom Server Controls: Combining ImageHoverButton and RoundCorneredButton

Two of my previous articles introduced you to creating two new controls, “ImageHoverButton” and “RoundCorneredButton.” This article extends those two by combining both of them into a new control with extra flavors!

Contributed by
Rating: 5 stars5 stars5 stars5 stars5 stars / 9
October 31, 2005
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

A downloadable file for this article is availabe 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

Let us first discuss what I covered in the articles describing “ImageHoverButton” and “RoundCorneredButton” controls.  Within the ASP.NET framework, we will not be able to find any “hover” controls.  Instead, we need to emit our own JavaScript for each of the controls, if we really need to have a “hover” control.  I thought to design a control, which would support “hover” for a customized button and thus resulted in the “ImageHoverButton.”

The “ImageHoverButton” is not simply a button; it even accepts background as an image and changes that image at the “hover” stage (automatically with the help of injected JavaScript within the control).  The image within that control can contain embedded text or we can set it up using the properties window at design time (which gets displayed on top of the image).  Now the control would be something like an image of “any” shape (depending on the image we assign to it).

Even though I found the “ImageHoverButton” very useful for my web development, I realized that I need to create separate images for every button, if the size of text would be different, which is problematic.  If all of the buttons were of the same size, then the “ImageHoverButton” would be the easiest implementation (which may not be possible in practical scenarios).  So, I decided to design and create a “dynamically expandable RoundCorneredButton.”

This is also an interesting button of mine, in which I could place any amount of text (as the caption of that button).  The result is that the button would expand automatically based on the content or text; you can even resize at design time.  I took this logic from another control within the same series called  “RoundedCornerBox.”  That control simply displays an automatically expandable box with all graphic corners and sides.  I expanded the same to cover the “RoundCorneredButton,” so that it would support “Postback” when the user clicks on it.

Everything worked as well as I had imagined it would. The only issue is that I didn’t implement hovering styles into the “RoundCorneredButton” control.  Not only that, but, apart from the dynamically expandable background, I would also like to place an icon type of image just before the text (which looks more polished).  And thus I redesigned and recreated by combining all the flavors to give me what I needed.

The new control (what I am about to explain now) now supports all sides and corners with graphic images, a background image at the center, a hovering facility (with a new set of graphic images), the text you could include, an icon which could be placed just before the text, and more. In this scenario, I even exposed several properties, to fix border (rounded) images for our control.  Before talking too much about the control, let us go through the implementation first.  I hope you will enjoy the new control.

Exposing properties for every side (including corners) of the button

As you know, every side of the button needs to be provided with some image (even corners, of course).  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 above 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 (a Visual Studio.NET supported open dialog) for the property, to facilitate the developer in selecting the 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.

Apart from all of the above, I also implemented the following properties:

  • Text
  • ImageIconURL

I hope you can easily understand the “Text” property.  It just displays the information and centers it to the button.  The “ImageIconURL” property is used to specify the icon, which gets displayed just before the text (generally used for OK, SAVE, NEW, CANCEL, and similar buttons). 

Starting with the usual TABLE

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

MyBase.AddAttributesToRender(writer)
       
'registering JavaScripts
        If Not Page.IsStartupScriptRegistered(Me.ClientID &
"_MouseOver")
Then
            Page.RegisterStartupScript(Me.ClientID &
"_MouseOver", getJS4MouseOver(Me.ClientID & "_MouseOver"))
       
End If
        If Not Page.IsStartupScriptRegistered(Me.ClientID &
"_MouseOut")
Then
            Page.RegisterStartupScript(Me.ClientID & "_MouseOut",
getJS4MouseOut(Me.ClientID & "_MouseOut"))
        End If

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).  The next two “if” structures will register two “hover” JavaScripts for the control.  Further proceeding, we have the following:

        'opening table
        writer.AddStyleAttribute("text-Align", "center")
        writer.AddStyleAttribute("vertical-Align", "middle")
        writer.AddStyleAttribute("cursor", "hand")
        writer.AddAttribute(HtmlTextWriterAttribute.Name,
Me.UniqueID)
        writer.AddAttribute(HtmlTextWriterAttribute.Border,
"0px")
        writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding,
"0px")
        writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing,
"0px")
        writer.AddAttribute("onMouseOver", Me.ClientID &
"_MouseOver();")
        writer.AddAttribute("onMouseOut", Me.ClientID &
"_MouseOut();")
        writer.AddAttribute(HtmlTextWriterAttribute.Onclick,
Page.GetPostBackEventReference(Me, String.Empty))
        writer.RenderBeginTag(HtmlTextWriterTag.Table)

From all the above statements, the conclusion would be something like: I am trying to render a “<TABLE>” tag with some attributes. I am also emitting “javascript” events at this stage itself, which means that the “hover” event applies to the whole table!  Another of the most important statements to concentrate on from above is this: 

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 header, second row of cells body (or content) and third row footer for a 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 (continuing 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.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_img_TopLeft")
            writer.RenderBeginTag(HtmlTextWriterTag.Img)
            writer.RenderEndTag()
       
End If
        writer.RenderEndTag() 'td

The above code fragment starts a single row (the first row of 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, "0px")
        writer.AddAttribute(HtmlTextWriterAttribute.Height,
"0px")
        writer.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_td_TopMiddle")
        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 the image for it as a 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.AddAttribute(HtmlTextWriterAttribute.Id,
Me.ClientID & "_img_TopRight")
            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 be very similar to creating the header.  But, we need to play a trick to allow the developer to write his own message during the rendering of the control, and of course the icon, too.  This is the only complex issue to handle in our control.

Let us add the first cell of the row (middle left) using the following code fragment (continuing 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")
        writer.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_td_MiddleLeft")
        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 in this section.  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 what will close the cell? The next section will explain that.          

'middle middle (center)
writer.AddAttribute(HtmlTextWriterAttribute.Width, "100%")
        writer.AddAttribute(HtmlTextWriterAttribute.Height,
"100%")
        writer.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_td_MiddleMiddle")
        If Me.ImageMiddleMiddleURL.Trim.Length > 0 Then

            writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundImage, "url(" &
Me.ImageMiddleMiddleURL.Trim & ")")
       
End If
        writer.RenderBeginTag(HtmlTextWriterTag.Td)

        'adding icon (only if available)
        If Me.ImageIconURL.Trim.Length > 0
Then
            writer.AddAttribute(HtmlTextWriterAttribute.Src,
Me.ImageIconURL.Trim)
            writer.RenderBeginTag(HtmlTextWriterTag.Img)
            writer.RenderEndTag()
'img
        End If

You can observe (within the last “if” structure) that I added the icon, which is expected to display just before the text.  You can look at the last statement.  It only opens “<TD>” and does not closes 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")
        writer.AddAttribute(HtmlTextWriterAttribute.Id, Me.ClientID & "_td_MiddleRight")
        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

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 “Creating the Header” section to the above code fragment.  As the coding is quite similar, you can get it from the downloadable.

Everything 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("&nbsp;" & 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”.  I added a space “&nbsp;” just to maintain some gap after the icon.  For a detailed explanation of “rendering” issues, I suggest you to refer to my previous articles in the same series.

How did I handle the post backs?

Another extension to the same control is the following code fragment.  Take a look at 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 would 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 “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 deals with the concepts of both eventing and delegating.  The above statements made it possible to implement ‘postback’ing together with delegating.  I suggest you to go through OOPS in .NET, if you are not familiar with Events and Delegates.

How is it different from my previous controls?

If you observe carefully, each and every cell (and image) I render is provided with a unique ID (which was not introduced in the article about the “RounderCorneredButton” control).  In that way, I can get hold of each and every cell (and image) through my JavaScript.  Within my JavaScript, I will be able to access each of those cells (or images) during hovering, and change to different images (hovering images) when necessary.  You will be able to understand this from the following JavaScript I included.

RaiseEventPrivate Function getJS4MouseOver(ByVal FunctionName As String)As String
        Dim js As String

        js = "<script language=javascript>"
        js &= "function " & FunctionName & "() "
        js &= "{"
        js &= "  document.all('" & Me.ClientID &
"_img_TopLeft').src = '" & Me.ImageTopLeftURL.Trim.Replace(".",
"_ov.") & "'; "
        js &= "  document.all('" & Me.ClientID &
"_td_TopMiddle').style.backgroundImage='url(" &
Me.ImageTopMiddleURL.Trim.Replace(".", "_ov.") & ")'; "
        js &= "  document.all('" & Me.ClientID &
"_img_TopRight').src = '" & Me.ImageTopRightURL.Trim.Replace(".",
"_ov.") & "'; "
        js &= "  document.all('" & Me.ClientID &
"_td_MiddleLeft').style.backgroundImage='url(" &
Me.ImageMiddleLeftURL.Trim.Replace(".", "_ov.") & ")'; "
        js &= "  document.all('" & Me.ClientID &
"_td_MiddleMiddle').style.backgroundImage='url(" &
Me.ImageMiddleMiddleURL.Trim.Replace(".", "_ov.") & ")'; "
        js &= "  document.all('" & Me.ClientID &
"_td_MiddleRight').style.backgroundImage='url(" &
Me.ImageMiddleRightURL.Trim.Replace(".", "_ov.") & ")'; "
        js &= "  document.all('" & Me.ClientID &
"_img_BottomLeft').src = '" & Me.ImageBottomLeftURL.Trim.Replace
(".", "_ov.") & "'; "
        js &= "  document.all('" & Me.ClientID &
"_td_BottomMiddle').style.backgroundImage='url(" &
Me.ImageBottomMiddleURL.Trim.Replace(".", "_ov.") & ")'; "
        js &= "  document.all('" & Me.ClientID &
"_img_BottomRight').src = '" & Me.ImageBottomRightURL.Trim.Replace(".", "_ov.") & "'; "
        js &= "}"
        js &= "</script>"

        Return js
    End Function

Even though the above code looks a bit clumsy, it just emits JavaScript to change all images at the “OnMouseOver” event.  The most important issue to understand is “what image is it getting for mouse hover”? If you assign “TopLeft.gif” to “ImageTopLeftURL” property, the JavaScript automatically searches for “TopLeft_ov.gif” and displays it at the “hover” event.  I implemented this to maintain consistency and lessen the burden at design time of assigning properties even for the “hover” event.  Of course, you will be able to modify the above code very easily according to your requirements.

Even though I started emitting exclusive HTML using “writer” methods, you can  do the same by extending your control from the already existing “table” web control.  If you directly extend it from the “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, better JavaScript, data-binding, cross browser support, 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