Thursday, June 25, 2009

How to create button with picture(icon) and text for Window CE/Mobile

For desktop program with full compact framework, it is very easy to create button with picture and text. Just drag the button from toolbox. Then choose the image from properties. But compact framework has no such properties for button.

What we need to do is create our own customized button control like following (button.cs):

/*

Requirements: Microsoft Development Environment 2003 Version 7.1.3088
Microsoft .NET Framework Version 1.1.4322
*/

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Windows.Forms;
using System.Drawing.Imaging;

// Assembly attribute that points to the run-time version of the assembly.
// This attribute is needed for this control to function properly during design time.
// When this control is added to the designer, the assembly indicated by
// this attribute is added to the reference collection of the project.
// The run-time assembly must be located in the reference path of the project,
// and the design-time version must appear in the "designer reference path".

//// The NETCFDESIGNTIME proprocessor variable is used to indicate whether the build is for a designer build or run-time device build.
//// This lets you add device specific routines and also desktop requirements (such as design-time attributes) without needlessly increasing your footprint.
//#if NETCFDESIGNTIME
// [assembly: System.CF.Design.
RuntimeAssemblyAttribute("xxxxxxx, Version=2.1.0.0, Culture=neutral, PublicKeyToken=null")]
//#endif

namespace DJHome.SpecialButton
{
///
/// Button class.
///

public class Button : System.Windows.Forms.Control
{
#region Component Designer generated code

///
/// Clean up any resources being used.
///

/// True to dispose of control.
protected override void Dispose( bool disposing )
{
if( disposing )
{
if( components != null )
components.Dispose();
}
base.Dispose( disposing );
}

// Required method for Designer support - do not modify
// the contents of this method with the Code Editor.
private void InitializeComponent()
{
this.Paint += new System.Windows.Forms.PaintEventHandler(this.OnPaint);
}

#endregion

#region Constructors

///
/// Constructor.
///

public Button()
{
m_penBlack = new System.Drawing.Pen(Color.Black);
m_penGray = new System.Drawing.Pen(Color.Gray);
m_penLightGray = new System.Drawing.Pen(Color.Gainsboro);
m_penWhite = new System.Drawing.Pen(Color.White);

// This call is required by the Windows.Forms Designer.
InitializeComponent();
}

#endregion

#region Enumerations

///
/// Used to set the button style.
///

public enum ButtonStyle
{
///
/// Push button style.
///

PushButton,
///
/// Toggle button style.
///

ToggleButton
}

///
/// Used to set the vertical alignment of the text on the button.
///

public enum TLocation
{
///
/// Text at bottom of button.
///

Bottom,
///
/// Text at left of button.
///

Left,
///
/// Text at middle of button.
///

Middle,
///
/// Text at right of button.
///

Right,
///
/// Text at top of button.
///

Top
}

#endregion

#region Variables

private System.ComponentModel.Container components = null;
private Pen m_penBlack;
private Pen m_penGray;
private Pen m_penLightGray;
private Pen m_penWhite;
private Bitmap m_bitBackground = null;
private object m_oControlTag = false;
private Image m_imgDisabled = null;
private Image m_imgPressed = null;
private Image m_imgUnPressed = null;
private bool m_bPressed = false;
private ButtonStyle m_ButtonStyle = ButtonStyle.PushButton;
private TLocation m_textLocation = TLocation.Middle;
private string m_sText2 = "";
private Color m_textColor;

#endregion

#region Properties

// Store a bitmap that is created when the button gets the focus.
// Used to show the dotted lines when the button has the focus.
private Bitmap BitBackground
{
get
{
return m_bitBackground;
}
set
{
m_bitBackground = value;
}
}

///
/// Get or set the tag for the control.
///

public object ControlTag
{
get
{
return m_oControlTag;
}
set
{
m_oControlTag = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("The image displayed when the button is in its disabled state."), Category("Custom Settings"), DefaultValue(null, "none")]
#endif
///
/// Image to be displayed on the button when the button is disabled.
///

public Image ImageDisabled
{
get
{
return m_imgDisabled;
}
set
{
m_imgDisabled = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("The image displayed when the button is in its pressed state."), Category("Custom Settings"), DefaultValue(null, "none")]
#endif
///
/// Image displayed on the button when the button is in the pressed state.
///

public Image ImagePressed
{
get
{
return m_imgPressed;
}
set
{
m_imgPressed = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("The image displayed when the button is in its unpressed state."), Category("Custom Settings"), DefaultValue(null, "none")]
#endif
///
/// Image displayed on the button when the button is in the unpressed state.
///

public Image ImageUnPressed
{
get
{
return m_imgUnPressed;
}
set
{
m_imgUnPressed = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("True sets the button in the pushed state, false otherwise."),Category("Custom Settings"), DefaultValue(typeof(bool), "False")]
#endif
///
/// Get or set the pressed stated of the button.
///

public bool Pressed
{
get
{
return m_bPressed;
}
set
{
m_bPressed = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("Set the button style to be push or toggle button."),Category("Custom Settings"), DefaultValue(typeof(ButtonStyle), "PushButton")]
#endif
///
/// Get or Set the button style.
///

public ButtonStyle Style
{
get
{
return m_ButtonStyle;
}
set
{
m_ButtonStyle = value;
Pressed = false;
this.Invalidate();
}
}

///
/// Set the text when changed.
///

public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("The vertical location of the button text."),Category("Custom Settings"), DefaultValue(typeof(TLocation), "Middle")]
#endif
///
/// Location where the text is to be displayed on the button.
///

public TLocation TextLocation
{
get
{
return m_textLocation;
}
set
{
m_textLocation = value;
this.Invalidate();
}
}

#if NETCFDESIGNTIME
// These design time attributes affect appearance of this property in the property grid.
[Description("The text for the second line."),Category("Custom Settings"), DefaultValue(typeof(string), "")]
#endif
///
/// Set the text when changed.
///

public string Text2
{
get
{
return m_sText2;
}
set
{
m_sText2 = value;
this.Invalidate();
}
}

///
/// Color of the text to be displayed.
///

private Color TextColor
{
get
{
return this.m_textColor;
}
set
{
this.m_textColor = value;
}
}

#endregion

#region Functions

///
/// Create a bitmap image.
///

/// Rectangle.
/// Returns a bitmap image.
private Bitmap CreateBitmap(Rectangle r)
{
Bitmap bit = new Bitmap(this.Width, this.Height);
Graphics g = Graphics.FromImage(bit);

// Fill the bitmap with the background color.
g.FillRectangle(new SolidBrush(this.BackColor), 0, 0, bit.Width, bit.Height);

// Set the pixels for the horizontal dashed lines.
for(int i = 5; i < r.Width - 4; i++)
{
bit.SetPixel(i, 4, Color.FromArgb(64, 64, 64));
i += 1;
}
for(int i = 6; i < r.Width - 4; i++)
{
bit.SetPixel(i, r.Height - 5, Color.FromArgb(64, 64, 64));
i += 1;
}

// Set the pixels for the vertical dashed lines.
for(int i = 5; i < r.Height - 3; i++)
{
bit.SetPixel(4, i, Color.FromArgb(64, 64, 64));
i += 1;
}
for(int i = 4; i < r.Height - 5; i++)
{
bit.SetPixel(r.Width - 5, i, Color.FromArgb(64, 64, 64));
i += 1;
}

return bit;
}

///
/// Draw the text on the graphics object.
///

/// PaintEventArgs.param>
/// Client rectangle.
/// Image width.
private void DrawText(PaintEventArgs e, Rectangle r, int iImageWidth)
{
SizeF size = e.Graphics.MeasureString(this.Text, this.Font);
SizeF size2 = e.Graphics.MeasureString(this.Text2, this.Font);
int iGap = 2;

// Shift the text if the button is pressed.
if (Pressed)
{
if (Text2.Length == 0)
{
// Center the text inside the client area of the button.
// Set the vertical location of the text.
switch(TextLocation)
{
case TLocation.Bottom:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width + 2) / 2, (r.Height - size.Height) - 2);
break;
case TLocation.Left:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width + 2) / 2) - iImageWidth, (r.Height - size.Height) / 2);
break;
case TLocation.Middle:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width + 2) / 2, (r.Height - size.Height) / 2);
break;
case TLocation.Right:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width + 2) / 2) + iImageWidth, (r.Height - size.Height) / 2);
break;
case TLocation.Top:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width + 2) / 2, 5);
break;
}
}
else
{
// Center the text inside the client area of the button.
// Set the vertical location of the text.
switch(TextLocation)
{
case TLocation.Bottom:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width + 2) / 2, (r.Height - (2*size.Height)) - 2 + iGap);
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), (r.Width - size2.Width + 2) / 2, (r.Height - size.Height) - 2 - iGap);
break;
case TLocation.Left:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width + 2) / 2) - iImageWidth, ((r.Height / 2) - size.Height + iGap));
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), ((r.Width - size2.Width + 2) / 2) - iImageWidth, (r.Height / 2) - iGap);
break;
case TLocation.Middle:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width + 2) / 2, ((r.Height / 2) - size.Height + iGap));
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), (r.Width - size2.Width + 2) / 2, (r.Height / 2) - iGap);
break;
case TLocation.Right:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width + 2) / 2) + iImageWidth, ((r.Height / 2) - size.Height + iGap));
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), ((r.Width - size2.Width + 2) / 2) + iImageWidth, (r.Height / 2) - iGap);
break;
case TLocation.Top:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width + 2) / 2, 6 + iGap);
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), (r.Width - size2.Width + 2) / 2, (size2.Height + 6) - iGap);
break;
}
}
}
else
{
if (Text2.Length == 0)
{
// Center the text inside the client area of the button.
// Set the vertical location of the text.
switch(TextLocation)
{
case TLocation.Bottom:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width) / 2, (r.Height - size.Height - 1) - 2);
break;
case TLocation.Left:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width) / 2) - iImageWidth, (r.Height - size.Height - 2) / 2);
break;
case TLocation.Middle:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width) / 2, (r.Height - size.Height - 2) / 2);
break;
case TLocation.Right:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width) / 2) + iImageWidth, (r.Height - size.Height - 2) / 2);
break;
case TLocation.Top:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width) / 2, 4);
break;
}
}
else
{
switch(TextLocation)
{
case TLocation.Bottom:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width) / 2, (r.Height - (2*size.Height) - 1) - 2 + iGap);
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), (r.Width - size2.Width) / 2, (r.Height - size.Height - 1) - 2 - iGap);
break;
case TLocation.Left:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width) / 2) - iImageWidth, ((r.Height / 2) - size.Height) - 1 + iGap);
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), ((r.Width - size2.Width) / 2) - iImageWidth, (r.Height / 2) - 1 - iGap);
break;
case TLocation.Middle:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width) / 2, ((r.Height / 2) - size.Height - 1) + iGap);
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), (r.Width - size2.Width) / 2, (r.Height / 2) - 1 - iGap);
break;
case TLocation.Right:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), ((r.Width - size.Width) / 2) + iImageWidth, ((r.Height / 2) - size.Height - 1 + iGap));
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), ((r.Width - size2.Width) / 2) + iImageWidth, (r.Height / 2) - 1 - iGap);
break;
case TLocation.Top:
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), (r.Width - size.Width) / 2, 5 + iGap);
e.Graphics.DrawString(this.Text2, this.Font, new SolidBrush(TextColor), (r.Width - size2.Width) / 2, (size2.Height + 5 - iGap));
break;
}
}
}
}

///
/// Get the image attributes to set the transparent color.
///

/// Image.
/// ImageAttributes of the image.
private ImageAttributes GetImageAttributes(Image img)
{
Bitmap bit = new Bitmap(img);

// Set the Attributes for the Transparent color
ImageAttributes imageAttr = new ImageAttributes();
Color colorTrans = bit.GetPixel(0, 0);
imageAttr.SetColorKey(colorTrans, colorTrans);

return imageAttr;
}

#endregion

#region Controls

///
/// Repaints the button after the button is clicked.
///

/// EventArgs.
protected override void OnClick(EventArgs e)
{
if (this.Enabled)
{
// Check if toggle is set.
if (Style == ButtonStyle.ToggleButton)
{
// Toggle the pressed state.
if (Pressed)
{
Pressed = false;
}
else
{
Pressed = true;
}
}
else
{
this.Pressed = false;
}

this.Invalidate();
base.OnClick (e);
}
}

///
/// Repaints the button in the enabled/disabled state.
///

/// EventArgs.
protected override void OnEnabledChanged(EventArgs e)
{
this.Invalidate();
base.OnEnabledChanged(e);
}

///
/// Sets the focus to the control.
///

/// EventArgs.
protected override void OnGotFocus(EventArgs e)
{
if (this.Enabled)
{
this.Focus();
this.Invalidate();
base.OnGotFocus(e);
}
}

///
/// Removes focus from the control.
///

/// EventArgs.
protected override void OnLostFocus(EventArgs e)
{
this.Invalidate();
base.OnLostFocus(e);
}

///
/// Sets the button to be pressed.
///

/// MouseEventArgs.param>
protected override void OnMouseDown (MouseEventArgs e)
{
if (this.Enabled)
{
// Check if the left mouse button was pressed.
if (e.Button == MouseButtons.Left)
{
// Check if toggle is set.
if (Style != ButtonStyle.ToggleButton)
{
this.Pressed = true;
}

this.Focus();
this.Invalidate();
}

base.OnMouseDown(e);
}
}

///
/// Repaints the button when the mouse is moved.
///

/// MouseEventArgs.param>
protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
{
if (this.Enabled)
{
// Check if toggle is set.
if (Style != ButtonStyle.ToggleButton)
{
// Check if the left mouse button was pressed.
if (e.Button == MouseButtons.Left)
{
// Paint the button in the unpressed state when the mouse is not over the button.
if ((Pressed) && (!this.ClientRectangle.Contains(e.X, e.Y)))
{
Pressed = false;
this.Invalidate();
}
else if ((!Pressed) && (this.ClientRectangle.Contains(e.X, e.Y)))
{
Pressed = true;
this.Invalidate();
}
}
}

base.OnMouseMove(e);
}
}

///
/// Resets the button.
///

/// MouseEventArgs.param>
protected override void OnMouseUp (MouseEventArgs e)
{
if (this.Enabled)
{
// Check if toggle is set.
if (Style != ButtonStyle.ToggleButton)
{
this.Pressed = false;
}

this.Invalidate();
base.OnMouseUp(e);
}
}

///
/// This Paint function uses routines common to both platforms.
///

private void OnPaint(object sender, System.Windows.Forms.PaintEventArgs e)
{
Rectangle rcClient = this.ClientRectangle;
int intImageWidth = 0;

// Set the text color to be grayed or the forecolor.
if (this.Enabled == false)
{
TextColor = System.Drawing.SystemColors.GrayText;
}
else
{
TextColor = this.ForeColor;

// Draw the dotted lines that shows the button has the focus.
if (this.Focused)
{
// Create the bitmap image.
if (BitBackground == null)
{
BitBackground = CreateBitmap(rcClient);
}

// Draw the bitmap that was created and stored.
e.Graphics.DrawImage((Image)BitBackground, 0, 0);
}
}

// Draw the lines that makeup the button image.
e.Graphics.DrawLine(m_penGray, 0, 0, rcClient.Width - 1, 0);
e.Graphics.DrawLine(m_penGray, 0, 0, 0, rcClient.Height - 1);
e.Graphics.DrawLine(m_penLightGray, 1, 1, rcClient.Width - 2, 1);
e.Graphics.DrawLine(m_penLightGray, 1, 1, 1, rcClient.Height - 2);
e.Graphics.DrawLine(m_penGray, 1, rcClient.Height - 2, rcClient.Width - 2, rcClient.Height - 2);
e.Graphics.DrawLine(m_penGray, rcClient.Width - 2, 1, rcClient.Width - 2, rcClient.Height - 2);
e.Graphics.DrawLine(m_penBlack, 0, rcClient.Height - 1, rcClient.Width - 1, rcClient.Height - 1);
e.Graphics.DrawLine(m_penBlack, rcClient.Width - 1, 0, rcClient.Width - 1, rcClient.Height - 1);

// Check if the button is using an image.
if (ImageUnPressed != null)
{
intImageWidth = ImageUnPressed.Width / 2;
int intImageLeft = (rcClient.Width - ImageUnPressed.Width) / 2;
int intImageTop = (rcClient.Height - ImageUnPressed.Height) / 2;
Rectangle imgRect;

// If the text is being used, set the location of the image to be above, below, or in the middle of the text.
if (this.Text.Length > 0)
{
switch(TextLocation)
{
case TLocation.Bottom:
intImageTop = intImageTop - 5;
break;
case TLocation.Left:
intImageLeft = (rcClient.Width - ImageUnPressed.Width) - 5;
break;
case TLocation.Middle:
// Nothing to do here.
break;
case TLocation.Right:
intImageLeft = 5;
break;
case TLocation.Top:
intImageTop = intImageTop + 5;
break;
}
}

// Shift the image if the button is pressed.
if (Pressed)
{
imgRect = new Rectangle(intImageLeft + 1, intImageTop + 1, ImageUnPressed.Width, ImageUnPressed.Height);

// Draw the unpressed image if it was set.
if (ImagePressed != null)
{
// Get the Attributes for the Transparent color
ImageAttributes imageAttr = GetImageAttributes(ImagePressed);

// Draw the pressed image if set.
e.Graphics.DrawImage(ImagePressed, imgRect, 0, 0, ImagePressed.Width, ImagePressed.Height, GraphicsUnit.Pixel, imageAttr);
}
else
{
// Get the Attributes for the Transparent color
ImageAttributes imageAttr = GetImageAttributes(ImageUnPressed);

// Draw the unpressed image.
e.Graphics.DrawImage(ImageUnPressed, imgRect, 0, 0, ImageUnPressed.Width, ImageUnPressed.Height, GraphicsUnit.Pixel, imageAttr);
}
}
else
{
// Draw the unpressed image if set.
imgRect = new Rectangle(intImageLeft, intImageTop, ImageUnPressed.Width, ImageUnPressed.Height);

// Check which image to set.
if (this.Enabled == false)
{
// Set the ImageDisabled if the button is disabled.
if (ImageDisabled != null)
{
// Get the Attributes for the Transparent color
ImageAttributes imageAttr = GetImageAttributes(ImageDisabled);

e.Graphics.DrawImage(ImageDisabled, imgRect, 0, 0, ImageDisabled.Width, ImageDisabled.Height, GraphicsUnit.Pixel, imageAttr);
}
else
{
// Get the Attributes for the Transparent color
ImageAttributes imageAttr = GetImageAttributes(ImageUnPressed);

e.Graphics.DrawImage(ImageUnPressed, imgRect, 0, 0, ImageUnPressed.Width, ImageUnPressed.Height, GraphicsUnit.Pixel, imageAttr);
}
}
else
{
// Get the Attributes for the Transparent color
ImageAttributes imageAttr = GetImageAttributes(ImageUnPressed);

e.Graphics.DrawImage(ImageUnPressed, imgRect, 0, 0, ImageUnPressed.Width, ImageUnPressed.Height, GraphicsUnit.Pixel, imageAttr);
}
}
}

// Draw the text if needed.
if (this.Text.Length > 0)
{
DrawText(e, rcClient, intImageWidth);
}

// Set the button appearance when pressed.
if (Pressed)
{
// Draw the lines that makeup the button image in the pressed position.
e.Graphics.DrawLine(m_penBlack, 0, 0, rcClient.Width - 1, 0);
e.Graphics.DrawLine(m_penBlack, 0, 0, 0, rcClient.Height - 1);
e.Graphics.DrawLine(m_penBlack, 1, 1, rcClient.Width - 2, 1);
e.Graphics.DrawLine(m_penBlack, 1, 1, 1, rcClient.Height - 2);
e.Graphics.DrawLine(m_penLightGray, 1, rcClient.Height - 2, rcClient.Width - 2, rcClient.Height - 2);
e.Graphics.DrawLine(m_penLightGray, rcClient.Width - 2, 1, rcClient.Width - 2, rcClient.Height - 2);
e.Graphics.DrawLine(m_penWhite, 0, rcClient.Height - 1, rcClient.Width - 1, rcClient.Height - 1);
e.Graphics.DrawLine(m_penWhite, rcClient.Width - 1, 0, rcClient.Width - 1, rcClient.Height - 1);
}
}

///
/// Resize the control in the design view.
///

/// EventArgs.
protected override void OnResize(EventArgs e)
{
this.Refresh();
}

#endregion
}
}


You can always directly include this file to your project then use button in the code to dynamically generate a button. But if you want to use it like the button from toolbar, you have to do special thing if you are using Visual studio 2003.

If you are using Visual Studio 2005 or 2008, it is very easy.

1)Add you button.cs to you project.
2)Include its namespace.
3)From toolbar, you will find out there is one more category with "Pointer" "button" in it, which is the customized button you just created!!!

No comments: