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

How to code a Transparent, reusable round corner box

by CodeGolem 11. October 2009 14:28

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

In my previous post I explained how to code a reusable control to dynamically generate round-corners for our panels (DIVs).

As user Christian Schiffer suggested, here is an updated version to get PNG transparent corners. This may be useful, for example, to "crop" images inside the rounded corners box.

Let's see the modified parts in source code.

First of all, I've added a Transparent property to the RoundCornerPanel:


/// <summary>
/// Sets panel transparency
/// </summary>
public bool Transparent
{
    get;
    set;
}

The new property needs to be passed in QueryString to the HttpHandler:


/// <summary>
/// Get style string for the corner image
/// </summary>
/// <param name="cornerPosition">Corner position</param>
/// <returns></returns>
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}&transparent={10})",
        CornerRadius,
        top,
        left,
        marginTop,
        marginLeft,
        borderWidth,
        ColorTranslator.ToHtml(BorderColor),
        ColorTranslator.ToHtml(BackColor),
        ColorTranslator.ToHtml(OutColor),
        (int)cornerPosition,
        Transparent);
}

Then I made a slight modification to the control's Render override:


protected override void Render(HtmlTextWriter writer)
{
    // keep the original BackColor
    Color originalBackColor = BackColor;

    // set the back color to trasparent to render the begin tag
    if (Transparent)
        BackColor = Color.Transparent;

    // start rendering the control
    RenderBeginTag(writer);

    // restore the original BackColor
    BackColor = originalBackColor;

    // 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: {0}; width: 100%; height: 100%'>",
        Transparent ? 0 : 2);

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

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

As you can see (in bold) I change the BackColor before invoking the base class' RenderBeginTag, and restore it back for further rendering.
I've also altered the content div's style to have a z-index to be on top of the corners if we did not set transparency, beneath them otherwise.

I preferred the BackColor-swap instead of handling Color.Transparent for the inner color so we can still define a custom color to be converted to trasparent. This can help getting the best from antialias.

Finally, the updates made to the corner-drawing HttpHandler.

A new property to get transparency setting from QueryString:


private bool _transparent
{
    get
    {
        if (HttpContext.Current.Request.QueryString["transparent"] != null)
            return bool.Parse(HttpContext.Current.Request.QueryString["transparent"]);
        else
            return false;
    }
}

And the new GDI rendering function:


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

    // 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();

    // make transparent
    if (_transparent)
    {
        image.MakeTransparent(_foreColor);

        // save as PNG
        using (MemoryStream memStream = new MemoryStream())
        {
            //note that context.Response.OutputStream doesn't support the Save, but does support WriteTo       
            image.Save(memStream, ImageFormat.Png);
            memStream.WriteTo(context.Response.OutputStream);
        }
    }
    else
    {
        // save as non-transparent jpeg

        // 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();
}

Updated parts are in bold.

  1. The Image object is constructed using Format32bppArgb PixelFormat, to allow transparent colors to be successfully encoded
  2. The ForeColor of the panel is made transparent
  3. The image is saved as PNG to the output stream. Here we need a MemoryStream, since Response.OutputStream does not support Save from PNG images.

 Finally, a sample page using the new transparent box:


<%@ 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></title>
</head>
<body>
    <form id="form1" runat="server">
    <br />
    <CodeGolem:RoundCornerPanel
        runat="server"
        OutColor="White"
        BackColor="Pink"
        Transparent="True"
        BorderColor="Red"
        CornerRadius="20"
        BorderWidth="2"
        Width="100"
        Height="100">
        <img src="Eye.jpg" alt="My eye" />
    </CodeGolem:RoundCornerPanel>
    </form>
</body>
</html>

This will render something like the following cropped image:


(yes... it is my eye...) Tongue out

Here is full source-code with sample. 

CodeGolem_RoundCornerPanel.zip (26.23 kb)

Happy round-cropping! Wink

Tags: ,

ASP.NET

Comments


India Yachika Verma 
November 25. 2009 10:08
Thanks for Great Information.

Add comment



  Country flag

biuquote
  • Comment
  • Preview
Loading



Sponsored by

LiveCo®
Silverlight® based
video conferencing platform

Hosting provided by

Lineadigitale