Introduction
I am attempting to make reusable extend version of ASP.NET ImageButton to overcome two problem. 1. font style is limited to browser, 2. hover and clicked image is not supported.
Background
I think it is very common thing that web developer wants but strangly .NET doesn't provide this features. I used to make it by some image generating page or hand-make javascript. But it is so annoying if I have to do it every each time. It is obvious that it can be make into a web control. So I go to learn some basic on how to make web control. Hope that someone need this do not need to start all over again.
Using the Code
Just compile the library with your VS. Add it to toolbox and project reference. drag the control into your design panel. give proper values, done.
< cc1:FontImageButton ID="FontImageButton2" runat="server" Height="73" Width="180"
AlternateText="MaxiImageButton" FontSize="16" ForeColor="Black"
ImagePath="~/resources/images/temp/" / >
< cc1:IconImageButton ID="IconImageButton1" runat="server" Height="50" Width="150"
FontSize="16" ForeColor="Black" ImagePath="~/resources/images/temp/"
AlternateText="MaxiIconBtn"
IconUrl="~/resources/images/icon/folder.png"
HoverIconUrl="~/resources/images/icon/folder_hover.png" / >
All attributes above MOST have value. For FontImageButton, Height, Width are the height and width of the background image you choose, for IconImageButton they are the height and width of the image being generated. AlternateText will be embedded inside the image. ImagePath is the temporary folder for storing the generated images. Other are self-explanatory.
I will explain a little about the library in case you want to extend it yourself. I wil use IconImageButton as an example.
I generate the image in OnPreRender(). So that the ImageUrl value will be ready for the ImageButton to Render().
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
m_prefix = Page.AppRelativeVirtualPath.Substring(
2).Replace('/', '_').Replace(.aspx", _");
m_tempImageFolderPath = Page.Server.MapPath(this.ImagePath);
string l_imageFilename = m_prefix + this.ID + .png";
string l_hoverImageFilename = m_prefix + this.ID + _hover.png";
string l_imageUrl = m_tempImageFolderPath + l_imageFilename;
string l_hoverImageUrl = m_tempImageFolderPath + l_hoverImageFilename;
bool l_isImageExist = File.Exists(l_imageUrl);
bool l_isHoverImageExist = File.Exists(l_hoverImageUrl);
if (!l_isImageExist || !l_isHoverImageExist)
{
GenerateButtonImage(FontSize, base.Height, base.Width, base.Text,
base.ForeColor);
}
else
{
string resolveUrl = Page.ResolveUrl(this.ImagePath + l_imageFilename);
this.HoverImageUrl = Page.ResolveUrl(this.ImagePath + l_hoverImageFilename);
base.ImageUrl = resolveUrl;
}
}
The load font feature is not complete, so I just disable it. Because I don't want to load it into memory and use the unsafe pointer. If you have an approach, please let me know, thank you.
private void GenerateButtonImage(int fontSize, Unit height, Unit width,
string text, Color foreColor)
{
int l_btnWidth = Convert.ToInt32(width.Value);
int l_btnHeight = Convert.ToInt32(height.Value);
Assembly l_thisLibrary = Assembly.GetExecutingAssembly();
System.Drawing.Image l_btnImg = new Bitmap(l_btnWidth, l_btnHeight);
#region load font
Font font = new Font(微軟正黑體", fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
#endregion
SolidBrush shadowBrush = new SolidBrush(foreColor);
//Draw the icon image and text into the background image
Graphics l_btnGrp = Graphics.FromImage(l_btnImg);
System.Drawing.Image l_iconImage = System.Drawing.Image.FromFile(
Page.Server.MapPath(this.IconUrl));
float l_iconX = 0;
float l_iconY = (l_btnHeight - l_iconImage.Height) / 2;
int l_textX = l_iconImage.Width;
int l_textY = 0;
int l_textWidth = l_btnWidth - l_iconImage.Width;
int l_textHeight = l_btnHeight;
l_btnGrp.DrawImage(l_iconImage, l_iconX, l_iconY);
l_btnGrp.DrawString(text, font, shadowBrush, new Rectangle(
l_textX, l_textY, l_textWidth, l_textHeight),
new StringFormat() { Alignment = StringAlignment.Near,
LineAlignment = StringAlignment.Center });
//save it to the temporary folder
m_filename = m_prefix + this.ID + .png";
string l_filepath = m_tempImageFolderPath + m_filename;
FileStream l_fileStream = new FileStream(l_filepath, FileMode.Create);
l_btnImg.Save(l_fileStream, System.Drawing.Imaging.ImageFormat.Png);
l_fileStream.Flush();
l_fileStream.Close();
l_btnImg = new Bitmap(l_btnWidth, l_btnHeight);
l_btnGrp = Graphics.FromImage(l_btnImg);
System.Drawing.Image l_hoverIconImage =
System.Drawing.Image.FromFile(Page.Server.MapPath(this.HoverIconUrl));
l_btnGrp.DrawImage(l_hoverIconImage, 0,
(l_btnHeight - l_hoverIconImage.Height) / 2);
l_btnGrp.DrawString(text, font, shadowBrush, new Rectangle(l_textX, l_textY,
l_textWidth, l_textHeight), new StringFormat() {
Alignment = StringAlignment.Near, LineAlignment =
StringAlignment.Center });
string l_hoverfilename = m_prefix + this.ID + _hover.png";
l_filepath = m_tempImageFolderPath + l_hoverfilename;
l_fileStream = new FileStream(l_filepath, FileMode.Create);
l_btnImg.Save(l_fileStream, System.Drawing.Imaging.ImageFormat.Png);
l_fileStream.Flush();
l_fileStream.Close();
//assign the generated image url to ImageUrl
string resolveUrl = Page.ResolveUrl(this.ImagePath +
m_filename);
this.HoverImageUrl = Page.ResolveUrl(this.ImagePath + l_hoverfilename);
base.ImageUrl = resolveUrl;
}
}
Add the javascript which handle image swap in AddAttributesToRender().
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
m_prefix = Page.AppRelativeVirtualPath.Substring(
2).Replace('/', '_').Replace(.aspx", _");
m_tempImageFolderPath = Page.Server.MapPath(this.ImagePath);
writer.AddAttribute(onmouseout", this.src=\'" + this.ImageUrl + \'");
writer.AddAttribute(onmousedown", this.src=\'" + this.HoverImageUrl + \'");
writer.AddAttribute(HtmlTextWriterAttribute.Src, base.ImageUrl);
}
Delete images in the temporary folder if it is not on the current page. This might cause problem if many people is viewing the webpage. They might delete other people images. I am trying to come up with a thread safe solution. Just comment it if you do not need it.
protected override void OnUnload(EventArgs e)
{
base.OnUnload(e);
string l_tempFolderPath = Page.Server.MapPath(this.ImagePath);
DirectoryInfo l_dir = new DirectoryInfo(l_tempFolderPath);
foreach (FileInfo file in l_dir.GetFiles())
{
if (file.Extension.Equals(.png"))
{
string l_nameNoExtension = file.Name.Replace(
file.Extension, ");
string[] l_split = l_nameNoExtension.Split('_');
if (l_split[l_split.Length - 1].Equals(hover"))
{
l_nameNoExtension = l_split[l_split.Length - 2];
}
else
{
l_nameNoExtension = l_split[l_split.Length - 1];
}
//take care suffic too! e.g. _hover
if (Page.FindControl(l_nameNoExtension) == null &&
!this.ID.Equals(l_nameNoExtension))
{
file.Delete();
}
}
}
}
Points of Interest
I start out overriding Render() of ImageButton. The button looks okay but if you click on it. The postback event won't fire. I do not know how to overcome this yet since I just start to learn custom web control. So I try not to touch anything excepting adding properties and attributes. If you know more detail about overriding Render(), please teach me.
All images are designed by my designer friend, not me. Those are not cover by the CPOL.
History
try to publish my first article the secord time.