Click here to Skip to main content
Licence CPOL
First Posted 25 Mar 2009
Views 2,240
Downloads 0
Bookmarked 0 times

Extend ImageButton with background, hover, click images and embedded text inside.

By | 25 Mar 2009 | Article
ImageButton with background, hover, click images and embedded text inside using the font style you choose. saving the images in a temporary folder of your choice.

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)
        {
            //call the ImageButton OnPreRender()
            base.OnPreRender(e);

            //generate the prefix for the image file for uniqueness
            //the rule is pageName[_subPageName]_*controlId[_hover].png
            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);
            //generate only when image not found in the temporary folder
            if (!l_isImageExist || !l_isHoverImageExist)
            {
                //generate the image for our button
                GenerateButtonImage(FontSize, base.Height, base.Width, base.Text,
                    base.ForeColor);
            }
            else
            {
                //use the found image
                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.

/// < class=summary class=>
        /// the image is store inside the temporary folder you provide
        /// the file name will be pageName_[subPageName_]controlId[_hover].png
        /// < class=/ class=summary class=>
        /// < class=param  class=name="fontSize" class=>< class=/ class=param class=>
        /// < class=param  class=name="height" class=>< class=/ class=param class=>
        /// < class=param  class=name="width" class=>< class=/ class=param class=>
        /// < class=param  class=name="text" class=>< class=/ class=param class=>
        /// < class=param  class=name="foreColor" class=>< class=/ class=param class=>
        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();     
            //generate an empty image as background
            System.Drawing.Image l_btnImg = new Bitmap(l_btnWidth, l_btnHeight);

            #region load font
            // Load font from a file, not stable, just skip it          
            //string fontName = Request.Form.Get("fontname") + ".ttf";
            //PrivateFontCollection privateFontCollection = new PrivateFontCollection();  
            //privateFontCollection.AddFontFile("~/resources/font/DFT_B7.TTC");
            //Stream fontStream = l_thisLibrary.GetManifestResourceStream(
            //    "MaxiServerControlLibrary.resources.font.DFT_B7.TTC");
            //byte[] fontByte = new byte[fontStream.Length];
            //fontStream.Read(fontByte, 0, (int)fontStream.Length);
            //FontFamily fontFamily = privateFontCollection.Families[0];

            //use build-in font without a problem, CHT supported
            //Font font = new Font("Arial", fontSize, FontStyle.Bold, GraphicsUnit.Pixel); 
            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();

            //Draw the hover/click icon image and text into the background image            
            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 });

            //save it to the temporary folder
            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);

            //javascript that swap between images
            //write a little javascript that set to an image according to a
            //button javascript event, currently use hover effect
            //writer.AddAttribute("onmouseover", "this.src=\'" +
            //this.HoverImageUrl + "\'");
            writer.AddAttribute(onmouseout", this.src=\'" + this.ImageUrl + \'");
            writer.AddAttribute(onmousedown", this.src=\'" + this.HoverImageUrl + \'");
            //writer.AddAttribute("onmouseup", "this.src=\'" + this.ImageUrl + "\'");       
            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.

        /// < class=summary class=>
        /// delete the image if it is not needed to that page
        /// search the directory, if the control cannot find by FindControl with
        /// the image name, delete it
        /// also check if it is equal to the current control id
        /// < class=/ class=summary class=>
        /// < class=param  class=name="e" class=>< class=/ class=param class=>
        protected override void OnUnload(EventArgs e)
        {
            base.OnUnload(e);
            //delete the image
            //search the directory, if the control cannot find by FindControl with
            //the image name, delete it
            //also check if it is equal to the current control id
            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, ");
                    //remove file prefix
                    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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Maxi Ng @ TW

Software Developer
AnaSystem
Taiwan Taiwan

Member

Newbie programmer in Taiwan.
Currently interested in developing game
and Artificial Neural Network.
paying my rent by developing web app. though.

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralMy vote of 1 PinmemberSteven Berkovitz6:26 25 Mar '09  
GeneralRe: My vote of 1 PinmemberMaxi Ng @ TW16:13 25 Mar '09  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120517.1 | Last Updated 25 Mar 2009
Article Copyright 2009 by Maxi Ng @ TW
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid