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.
- The Image object is constructed using Format32bppArgb PixelFormat, to allow transparent colors to be successfully encoded
- The ForeColor of the panel is made transparent
- 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...) 
Here is full source-code with sample.
CodeGolem_RoundCornerPanel.zip (26.23 kb)
Happy round-cropping! 