Skip to main content

Dynamic Rounded Panel

Dynamic Rounded Panel (test sample)

Width, px Height, px
Radius of circle, px Border Width, px
Background Color Border Color
Apply
inner text

Dynamic Rounded Panel

14 Nov 2007 (updated 10 Jun 2008) This is another variant of the rounded panel creation (the first one you can see here), that uses images to present curved corners. To be more precise, the only image is used, the one that is build based on the specified circle radius, the background color, the border color and width. The panel appearance varies according to all these parameters and can be dynamically changed in runtime.

Picture 1. The structure of the top part of the rounded panel
a - the left part where the upper left part of the image is shown.
b - the middle that is filled with the background color and contains a block element (d). The d element imitates the image border.
c - the right part where the upper right part of the image is shown. Of course, the middle can be simpler if to use the only block that is filled with the background color and has the upper border. But different browsers interpret borders ambiguously, some of them enlarge total height of the block element. It caused a small complication of the control.
The bottom part of border is build on the same principle, only mirror-like.

The code overview

The rounded panel is inherited from the ordinary ASP.Net Panel, one property is added:
Radius - the circle radius
Besides, the control implements interface IHttpHandler and contains a code for the image creation. It has to be registered in web.config in order to process requests for the ~/roundedpanel.ashx?... url.
Generation and registration of the styles that are required for rendering the panel occur in the overriden OnPreRender method.
private string styleGroupName = null;
//the name for the styles group that is responsible for the panel representation
private string StyleGroupName
        {
            get
            {
                if (styleGroupName == null)
                {
                    string key = string.Format("{0}{1}{2}{3}", 
                                                        this.BorderColor.ToArgb(), 
                                                        this.BackColor.ToArgb(), 
                                                        this.BorderWidth, 
                                                        this.Radius);
                    byte[] bytes = System.Text.Encoding.UTF8.GetBytes(key);
                    styleGroupName = Convert.ToBase64String(bytes).Replace("=", "");
                }
                return styleGroupName;
            }
}

//query key for the circle radius
private const string radiusQueryKey = "rd";
//query key for the border width
private const string borderWidthQueryKey = "bw";
//query key for the border color
private const string borderColorQueryKey = "bc";
//query key for the background color
private const string backgroundColorQueryKey = "bg";

protected override void OnPreRender(EventArgs e)
{
    if (this.Radius > 0)
    {
        //creates url to the image showing the curved corners
        string imageUrl = string.Format(
           "~/roundedpanel.ashx?{0}={1}&{2}={3}&{4}={5},{6},{7}&{8}={9},{10},{11}",
            radiusQueryKey,
            this.Radius,
            borderWidthQueryKey,
            this.BorderWidth.Value,
            borderColorQueryKey,
            this.BorderColor.R,
            this.BorderColor.G,
            this.BorderColor.B,
            backgroundColorQueryKey,
            this.BackColor.R,
            this.BackColor.G,
            this.BackColor.B);
        //creates styles
        StringBuilder sb = new StringBuilder();
        sb.AppendFormat(
@".{0} .a, .{0} .a b, .{0} .d, .{0} .d b 
{{display:block;font-size:1px;overflow:hidden;}}
.{0} .a, .{0} .d, .{0} .b, .{0} .e 
{{background-image:url({4});background-repeat:no-repeat;height:{1}px;}}
.{0} .b, .{0} .e {{margin-left:{1}px;}}
.{0} .a {{background-position:left top;}}
.{0} .b {{background-position:right top;}}
.{0} .d {{background-position:left bottom;}}
.{0} .e {{background-position:right bottom;}}
.{0} .c, .{0} .f {{background-color:{5};height:{1}px;margin-right:{1}px;}}
.{0} .c b, .{0} .f b {{height:{2}px;background-color:{6};}}
.{0} .f b {{margin-top:{3}px;}}
.{0} .m {{background-color:{5};border-left:solid {2}px {6};border-right:solid {2}px {6};}}",
this.StyleGroupName,
this.Radius,
this.BorderWidth.Value,
this.Radius - this.BorderWidth.Value,
ResolveUrl(imageUrl),
this.BackColor.IsNamedColor ? this.BackColor.Name : "#" + this.BackColor.Name.Substring(2),
this.BorderColor.IsNamedColor ? this.BorderColor.Name : "#" + this.BorderColor.Name.Substring(2));
        //adds styles for the panel width, if it is required
        if (!this.Width.IsEmpty)
            sb.AppendLine(string.Format(".{0}{{width:{1}}}",
                this.StyleGroupName, this.Width));
        
        //adds styles for the panel height, if it is required
        if (!this.Height.IsEmpty && this.Height.Type == UnitType.Pixel)
            sb.AppendLine(string.Format(".{0} .m{{height:{1}px;overflow:visible}}",
                this.StyleGroupName, this.Height.Value - this.Radius * 2));
        //registers styles on the page
        if (!StylesController.IsStyleSheetIncludeRegistered(this.Page, "Rounded Panel"))
            StylesController.RegisterStyleSheetInclude(this.Page, "Rounded Panel", sb.ToString());
    }
    base.OnPreRender(e);
}
StylesController - a class that contains a few methods for registration of the styles created in runtime. It is described in the How to Register Stylesheet Created in Runtime article. Creation of HTML representing the control occurs in the overriden RenderControl method; the upper, bottom and middle parts are rendered separately.
public override void RenderControl(HtmlTextWriter writer)
{
    if (this.Radius > 0)
    {
        //renders the upper part of the control
        writer.Write(string.Format(
@"<div class='{0}'><b class='a'><b class='b'><b class='c'><b></b></b></b></b><div class='m'>", 
this.StyleGroupName));
        //renders inner controls
        base.RenderContents(writer);
        
        //renders the bottom part of the control
        writer.Write("</div><b class='d'><b class='e'><b class='f'><b></b></b></b></b></div>");
    }
    else
        base.RenderControl(writer);
}
The image representing curved corners is generated in the ProcessRequest method of the IHttpHandler implementation. It turns out to be the most complicated part of the work, as for me. The required image has to show a bordered circle filled with the specified background color, the rest of the image has to be transparent. It can be done by means of the transparent gif. My knowledge of GDI+ is rather superficial and examples found on the Internet are too complicated to simply copypaste. The Bob Powell's excellent resource on GDI+ and especially the article about peculiarities of the different images formats help me.
    Here is the way to draw partially transparent gif image for rounded corners:

  • First of all, you need to create a common bitmap in RGB format and draw the required image on its canvas. It is not possible to draw directly on gif-image because it contains indexed colors palette.
  • Create a gif-image of the same size that the image in RGB format.
  • Change color palette of the gif-image, it has to contain 3 colors: a transparent color, a border color and a background color.
  • Read colors pixel-by-pixel from the RGB-image and set colors for the corresponding images in the indexed image.
  • Save gif-image to the Response.
void IHttpHandler.ProcessRequest(HttpContext context)
{
    if (context.Request[radiusQueryKey] != null
        && context.Request[borderWidthQueryKey] != null
        && context.Request[borderColorQueryKey] != null
        && context.Request[backgroundColorQueryKey] != null)
    {
        int radius = int.Parse(context.Request[radiusQueryKey]);
        int borderWidth = int.Parse(context.Request[borderWidthQueryKey]);
        string[] args = context.Request[borderColorQueryKey].Split(new char[] { ',' });
        Color borderColor = Color.FromArgb(int.Parse(args[0]), int.Parse(args[1]), int.Parse(args[2]));
        args = context.Request[backgroundColorQueryKey].Split(new char[] { ',' });
        Color bgColor = Color.FromArgb(int.Parse(args[0]), int.Parse(args[1]), int.Parse(args[2]));
        //sets the transparent color
        Color transparentColor = Color.FromArgb(0, 0, 0, 0);
        //draws the image in the 32 bit RGB format
        Bitmap source = new Bitmap(radius * 2, radius * 2, PixelFormat.Format32bppRgb);
        Graphics g = Graphics.FromImage(source);
        g.FillRectangle(new SolidBrush(transparentColor), 0, 0, source.Width, source.Height);
        g.FillEllipse(new SolidBrush(bgColor), borderWidth / 2, borderWidth / 2, 
                source.Width - borderWidth, source.Height - borderWidth);
        g.DrawEllipse(new Pen(borderColor, borderWidth), 0 + borderWidth / 2, 0 + borderWidth / 2, 
                source.Width - borderWidth, source.Height - borderWidth);
        //creates the image in the indexed format
        Bitmap dest = new Bitmap(source.Width, source.Height, PixelFormat.Format8bppIndexed);
        
        //changes the color palette
        ColorPalette pal = dest.Palette;
        pal.Entries[0] = transparentColor;
        pal.Entries[1] = bgColor;
        pal.Entries[2] = borderColor;
        dest.Palette = pal;
        //sets a rectangle that occupies the whole picture area
        Rectangle rect = new Rectangle(0, 0, source.Width, source.Height);
        //locks the images in the memory
        BitmapData sourceData = source.LockBits(rect, ImageLockMode.ReadOnly, source.PixelFormat);
        BitmapData destData = dest.LockBits(rect, ImageLockMode.WriteOnly, dest.PixelFormat);
        for (int x = 0; x < source.Width; x++)
        {
            for (int y = 0; y < source.Height; y++)
            {
                //reads color of the pixel with coordinates x,y  from the source image
                Color color = Color.FromArgb(Marshal.ReadInt32(sourceData.Scan0, sourceData.Stride * y + x * 4));
                //sets color of the corresponding pixel in the destination image
                if (color == bgColor)
                    Marshal.WriteByte(destData.Scan0, destData.Stride * y + x, 1);
                else if (color == borderColor)
                    Marshal.WriteByte(destData.Scan0, destData.Stride * y + x, 2);
                else
                    Marshal.WriteByte(destData.Scan0, destData.Stride * y + x, 0);
            }
        }
        //unlocks the images
        dest.UnlockBits(destData);
        source.UnlockBits(sourceData);
        source.Dispose();
        //writes the indexed image to the Response
        context.Response.Clear();
        context.Response.ContentType = "image/gif";
        dest.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Gif);
        dest.Dispose();
        context.Response.End();
    }
}
In the end it is required to register in web.config HttpHandlers that are responsible for drawing the image and the stylesheet registration.
<httpHandlers>
 ...
 <add verb="GET" path="roundedpanel.ashx" type="MyAssembly.MyNamespace.RoundedPanel, MyAssembly"/>
 <add verb="GET" path="stylesheet.css" type="MyAssembly.MyNamespace.StylesController, MyAssembly"/>
</httpHandlers>
Source code - 3.5 kB

Comments

Popular posts from this blog

Editing Child GridView in Nested GridView

Editing Child GridView in Nested GridView In this article we will explore how to edit child gridview in the nested gridview.   Let''s write some code. Step 1:  Add scriptmanager in the aspx page. < asp : ScriptManager   ID ="ScriptManager1"   runat ="server"   EnablePageMethods ="true"> </ asp : ScriptManager > Step 2:  Add below stylesheet for modal popup. < style   type ="text/css">        .modalBackground        {              background-color : Gray;              filter : alpha(opacity=80);              opacity : 0.5;       }        .ModalWindow        {     ...

Hierarchical GridView in ASP.NET with AJAX, JQuery implementation - Part 1

Previously I had blogged some post related to showing two grids in hierarchical way. For example - when I need to show an invoice reports on the screen, the user requesting to show the list of invoices at initial time and they can drill down the details of item details of each invoice by clicking on respective row. The implementations can be in multiple ways, but I had blogged the implementation with AJAX and without AJAX using Java script on ASP.NET page. Below are the URLs of the same – Hierarchical GridView in ASP.NET Hierarchical GridView in ASP.NET with AJAX, Javascript implementation In this post I am taking the same requirements done before and enhance with some additional functionality as per readers expectations. The requirement on this implementation will be – The page should show a Grid View with list of Orders with Expand icon on the first column. On click of expand icon on each row, the Order Details of the Order must be fetched from the database ...

Scrollable Gridview With fixheader using JQuery in Asp.net

Scrollable Gridview With fixheader using JQuery in Asp.net Introduction: In this article I will explain how to implement scrollable gridview with fixed header in asp.net using JQuery.  Description:  In Previous posts I explained lot of articles regarding Gridview. Now I will explain how to implement scrollable gridview with fixed header in asp.net. I have one gridview that contains lot of records and I used  paging for gridview  but the requirement is to display all the records without paging. I removed paging at that time gridview occupied lot of space because it contains more records to solve this problem we implemented scrollbar.  After scrollbar implementation if we scroll the gridview we are unable to see Gridview header.   To implement Scrollable gridview with fixed header I tried to implement concept with css and JavaScript but there is no luck because maintaining fixed header working in IE but not in Mozilla and vice versa to ...