an ASP.NET, C# technical blog, by Gianni Tropiano

How to code a reusable round corner box

by CodeGolem 27. September 2009 13:46

UPDATE: As user Christian Schiffer suggested, I've posted an updated version to get transparent corners.

 

FULL SOURCE CODE IS AVAILABLE AT THE BOTTOM OF THIS ARTICLE.

Most of us already use round-corners to give a more attractive look to our web sites.

What about creating a reusable control, with dynamically generated corner images we can easily skin with themes?

In a previous post I already suggested a way to create a reusable dynamic text-generating control. Here we will use the same technique: a custom HttpHandler to dynamically render the corner images.

We will use CSS absolute positioning to place the rounded corners on top of the square ones, hence giving the illusion of a round corner box.

This is a sample of how the box's elements will be composed: 

But let's start looking at the corner-generating HttpHandler.

Each corner will be rendered as a filled ellipse. All needed parameters need to be passed to the HttpHandler as QueryString parameters.

Here is the HttpHandler's ProcessRequest override implementation, with private properties mapping each QueryString parameter to determine: the corner radius, the border width and color, the back and fore colors, the corner position (top-left, top-right, bottom-left or bottom-right).


public void ProcessRequest(HttpContext context)
{
    // instanciate a new image with needed size
    Bitmap image = new Bitmap(_cornerRadius, _cornerRadius);

    // instanciate a graphics object for drawing on the image
    Graphics graph = Graphics.FromImage(image);
    graph.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

    // fill the whole image with the background color
    graph.Clear(_backColor);

    // instanciate a pen object with the border's color
    Pen pen = new Pen(_borderColor, _borderWidth);

    // determine ellipse's coordinates
    // based on the requested corner position
    int startX = 0, startY = 0;
    switch (_cornerPosition)
    {
        case CornerPositionEnum.TopLeft:
            startX = 0;
            startY = 0;
            break;
        case CornerPositionEnum.TopRight:
            startX = -_cornerRadius;
            startY = 0;
            break;
        case CornerPositionEnum.BottomLeft:
            startX = 0;
            startY = -_cornerRadius;
            break;
        case CornerPositionEnum.BottomRight:
            startX = -_cornerRadius;
            startY = -_cornerRadius;
            break;
    }

    // draw the ellipse
    graph.FillEllipse(new SolidBrush(_foreColor), startX, startY, _cornerRadius * 2 - _borderWidth, _cornerRadius * 2 - _borderWidth);
    graph.DrawEllipse(pen, startX, startY, _cornerRadius * 2 - _borderWidth, _cornerRadius * 2 - _borderWidth);

    // dispose the graphics object
    graph.Dispose();

    // instanciate an encoder with quality parameters
    // to save the image as 100% quality JPEG file
    EncoderParameter encoderParam = new EncoderParameter(Encoder.Quality, 100L);
    EncoderParameters encoderParams = new EncoderParameters();
    encoderParams.Param[0] = encoderParam;

    ImageCodecInfo jpegCodec = null;
    ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();

    for (int i1 = 0; i1 < codecs.Length; i1++)
    {
        if (codecs[i1].FormatDescription == "JPEG")
        {
            jpegCodec = codecs[i1];
            break;
        }
    }

    // output to the response stream
    image.Save(context.Response.OutputStream, jpegCodec, encoderParams);

    // dispose the image object
    image.Dispose();
}

 As usual, the HttpHandler needs to be registered in the web.config file:


<system.web>
    <httpHandlers>
        <add verb="GET" path="CornerImageHandler.ashx" type="CodeGolem.CornerImageHandler"/>
    </httpHandlers>
</system.web>

<!-- IIS 7 -->
<system.webServer>
    <handlers>
        <add name="CornerImageHandler" verb="GET" path="CornerImageHandler.ashx" type="CodeGolem.CornerImageHandler"/>
    </handlers>
</system.webServer>

Now we need a Custom Control to render the whole html for us. I have implemented this as a Control inherited from the Panel class.

The RoundCornerPanel inherits all properties from the Panel class, and declares the following additional ones:


/// <summary>
/// Color of the box's background
/// </summary>
public Color OutColor
{
    get;
    set;
}

/// <summary>
/// Corners' radius, in pixels
/// </summary>
public int CornerRadius
{
    get;
    set;
}

And here is the RoundCornerPanel's Render override implementation:


protected override void Render(HtmlTextWriter writer)
{
    // start rendering the control
    RenderBeginTag(writer);

    // add a relative-positioned div
    writer.WriteLine("<div style='position: relative; width: 100%; height: 100%; left: 0px; top: 0px;'>");

    // create div tags for each corner
    writer.WriteLine("<div style='{0}'></div>", getCornerStyle(CornerPositionEnum.TopLeft));
     writer.WriteLine("<div style='{0}'></div>", getCornerStyle(CornerPositionEnum.TopRight));
     writer.WriteLine("<div style='{0}'></div>", getCornerStyle(CornerPositionEnum.BottomLeft));
     writer.WriteLine("<div style='{0}'></div>", getCornerStyle(CornerPositionEnum.BottomRight));

    // create a content div with a customizable 'Content' CSS class
    writer.WriteLine("<div class='Content' style='position:absolute; top: 0px; left: 0px; z-index: 2; width: 100%; height: 100%'>");

    // render the contents of the panel
    RenderContents(writer);
    
    // close the inner panels
    writer.WriteLine("</div></div>");

    // render the closing tag
    RenderEndTag(writer);
}

Note in bold:

  1. Each corner is rendered as a separate absolute-positioned div
  2. I've added a div with 'Content' CSS class name as a container for child controls. This will allow us to declare a custom CSS class in our website to keep control over the content's layout.

The absolute-positioning CSS style is rendered within the getCornerStyle() method:


private string getCornerStyle(CornerPositionEnum cornerPosition)
{
    Unit top, left, marginTop, marginLeft;
    top = left = marginTop = marginLeft = Unit.Pixel(0);
    int borderWidth = (int)BorderWidth.Value;

    switch (cornerPosition)
    {
        case CornerPositionEnum.TopLeft:
            top = Unit.Pixel(-borderWidth);
            left = Unit.Pixel(-borderWidth);
            break;
        case CornerPositionEnum.TopRight:
            top = Unit.Pixel(-borderWidth);
            left = Unit.Percentage(100);
            marginLeft = Unit.Pixel(borderWidth - CornerRadius);
            break;
        case CornerPositionEnum.BottomLeft:
            top = Unit.Percentage(100);
            marginTop = Unit.Pixel(borderWidth - CornerRadius);
            left = Unit.Pixel(-borderWidth);
            break;
        case CornerPositionEnum.BottomRight:
            top = Unit.Percentage(100);
            marginTop = Unit.Pixel(borderWidth - CornerRadius);
            left = Unit.Percentage(100);
            marginLeft = Unit.Pixel(borderWidth - CornerRadius);
            break;
    }

    return string.Format("position:absolute;z-index:1;width:{0}px;height:{0}px;top:{1};left:{2};margin-top:{3};margin-left:{4};background-image:url(CornerImageHandler.ashx?radius={0}&width={5}&color={6}&fcolor={7}&bcolor={8}&pos={9})",
        CornerRadius,
        top,
        left,
        marginTop,
        marginLeft,
        borderWidth,
        ColorTranslator.ToHtml(BorderColor),
        ColorTranslator.ToHtml(BackColor),
        ColorTranslator.ToHtml(OutColor),
        (int)cornerPosition);
}

Finally, here is a sample page using the RoundCornerPanel control:


<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register TagPrefix="CodeGolem" Namespace="CodeGolem" Assembly="RoundCornerPanel" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Round Corner Panel Sample</title>
    <style type="text/css">
    .RoundBox .Content
    {
        padding: 10px;
        font-family: Verdana;
        font-weight: bold;
    }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <br />
        <CodeGolem:RoundCornerPanel
            runat="server"
            OutColor="White"
            BackColor="Yellow"
            ForeColor="Black"
            BorderColor="Red"
            CornerRadius="20"
            BorderWidth="2"
            Width="200"
            Height="200"
            CssClass="RoundBox">
            Test me!
        </CodeGolem:RoundCornerPanel>
    </form>
</body>
</html>

This will render something like the following sample:

excuse me for poor color choice... Embarassed

Note:

  1. The control needs to be registered in the page, or in the web.config
  2. A custom RoundBox Content CSS class is declared and assigned to the RoundCornerPanel control

Finally, here is full source package:

CodeGolem_RoundCornerPanel.zip (16 kb)

Hope you will find this useful.

Happy round-boxing! Smile

 

Tags: ,

ASP.NET

Comments


Norway Christian Schiffer 
October 9. 2009 11:03
Excelent article, there is one thing missing though, background transparency. The format should be PNG to achieve that.


Italy codegolem 
October 9. 2009 12:31
Christian, you are right! Thank you for pointing this out! Smile

I already have an almost-ready transparent PNG version.
But PNG rendering quality is a little poorer than JPEG.
Furthermore, the round-corner effect is obtained by overlapping the standard square edges, thus the PNG corner image can be transparent only inside the box, not on the outside.

I will soon post an update about the transaprent version. It can be used, for example, to "crop" an image within the rounded-corner box.


United States Rick 
October 9. 2009 18:29
Excellent, I needed something like this...

Thank, you


United States james worth 
October 9. 2009 20:56
Huge wtf on using an HTTPHandler to have ROUNDED CORNERS ON DIVS!!!!


United States David Duey 
October 9. 2009 21:29
Ver cool!

I added a reference to your control in one of my projects and added the rounded corner control to a page; the control rendered, but the corners weren't rounded.  I added an ashx file to my project and I suspect that's where my problem lies.  Would it be possible for you to add an example project using the rounded corner control to your code download.

Thank you very much!


United States Michael 
October 9. 2009 23:31
Going to have to agree with James' comment although with less enthusiasm.  I can see where you were doing this to perhaps get a handle on working with GDI+ and HttpHandlers but this is not practical for real-life business applications.  This is akin to one of those Rube Goldberg machines that end up just lighting a match.  Good effort though! =)


Italy codegolem 
October 9. 2009 23:44
Hi James and Michael!
Any comment is welcome!

But I still don't understand your point...

Maybe you prefer using static images for the corners? This would force you to have images for any combination of back-and-fore colors, while the dynamically-generated ones could be simply skinned by themes... Client-side caching will anyway prevent too workload on the server.

Do you know some other magic to get round corners? Let me know! Bye! Smile


Italy codegolem 
October 10. 2009 00:14
Hi David!

I updated the attached file and included full sample to get the yellow "test me" box. You may need to delete your browser's cache to get the updated file.

Bye!


People's Republic of China wesley.zhao 
October 10. 2009 06:30
            switch (_cornerPosition)
            {
                case CornerPositionEnum.TopLeft:
                    startX = 0 + _borderWidth / 2 ;
                    startY = 0 + _borderWidth / 2 ;
                    break;
                case CornerPositionEnum.TopRight:
                    startX = -_cornerRadius + _borderWidth / 2 ;
                    startY = 0 + _borderWidth / 2 ;
                    break;
                case CornerPositionEnum.BottomLeft:
                    startX = 0 + _borderWidth / 2;
                    startY = -_cornerRadius + _borderWidth / 2;
                    break;
                case CornerPositionEnum.BottomRight:
                    startX = -_cornerRadius + _borderWidth / 2 ;
                    startY = -_cornerRadius + _borderWidth / 2 ;
                    break;
            }

修改了一下。。。。当线比较宽(<50px)时候也不会错乱


India Rozy 
October 10. 2009 09:02
That's good. but,
It's for only Asp.net developers.
Is it works on all browsers?
For Round-Corner does handler,graphics at server-side worth?
It's better to have a JavaScript reusable control and let browser
handle it and it should be useful for all Web Developers.
U can find that JS reusable control at,
rajudasa.blogspot.com/.../...er-box-model-and.html


Italy codegolem 
October 10. 2009 10:35
Hi Rozy,

well, this is an ASP.NET/C# related blog. And, yes: it works on all CSS 2.0 compliant browsers.
As to my opinion, since this is a pure CSS styled div on the client, it may be more cross-browser compatible than a client-side script.
Drawing the graphics on the client may lead to high workload on the client if you have many corners, while server-side drawn ones can be kept in cache, both on the server and the client.


India Thanigainathan 
October 12. 2009 09:49
Hi,

Very nice article.

The same thing can be achieved using JQuery rounded corners. No work at all.simply add the js and set the div for rounded corners and all is done.

How do you compare the both.

Thanks,
Thani


Italy codegolem 
October 12. 2009 10:58
Hi Thanigainathan!
Thank you for your feedback!

I would say: "the same thing can be achieved with simple CSS, with no coding at all".
What my RoundCornerPanel control does, is the same as simple CSS to the client. Only  the corner images are generated on the fly for two simple reasons:
1. free us developers from playing with Paint-like applications to draw corners
2. enable the the rounded-corners control to be easily customized with ASP.NET Skins and Themes.

I think the CSS way is lighter, since the corner images can be kept in cache both on the client and server side. While jQuery forces the browser to always re-draw the corners.
Finally, I can't see a way to Skin client-generated corners with ASP.NET Themes.

Anyway, this is not a "my vs. yours" discussion. There are always many ways to achieve the same thing. I pointed out a different way. We should always consider pros and cons of each, and choose the way that best fits our needs.

Any furher comment is welcome.
Thank you! Bye!


Sweden Goran 
October 13. 2009 13:25
Excellent article we will definitely use it in our app. And to James and Michael, go and be negative somewhere else….


United States Deepu 
October 14. 2009 18:51
Thanks for your great effort..

I am have problem while resizing the box.. There is some form controls i am trying to hiding and showing so the box is getting overlapped

is there any way I can set adjust the height property automatically to extend the box ?



India Manoj Sitapara 
October 15. 2009 14:43
Thanks for effort.

well if i am adding backcolor like this : #FFFFF, it distroy the round corner.

can u help me for this?


India vijayakumar 
March 23. 2010 14:10
it's great thing
it help me to create round corner panel
but i want round corner textbox can you
help me
Thanks in advance
JAI HIND


United States Anderson Silva 
July 20. 2010 18:59
How to code a reusable round corner box, is helpful. does it work with all browsers?


Add comment



  Country flag

biuquote
  • Comment
  • Preview
Loading



Sponsored by

LiveCo®
Silverlight® based
video conferencing platform

Hosting provided by

Lineadigitale