Gradient Button Class
CodeKeep C# Feed Novembre 16th, 2007
Description: Gradient button class for .NET Compact Framework.Link: http://www.codekeep.net/snippets/b141ac6f-6d40-4322-88fc-016d95d2bef8.aspx
public sealed class GradientFill
{
// This method wraps the PInvoke to GradientFill.
// Parmeters:
// gr - The Graphics object we are filling
// rc - The rectangle to fill
// startColor - The starting color for the fill
// endColor - The ending color for the fill
// fillDir - The direction to fill
//
// Returns true if the call to GradientFill succeeded; false
// otherwise.
public static bool Fill(
Graphics gr,
Rectangle rc,
Color startColor, Color endColor,
FillDirection fillDir)
{
// Initialize the data to be used in the call to GradientFill.
Win32Helper.TRIVERTEX[] tva = new Win32Helper.TRIVERTEX[2];
tva[0] = new Win32Helper.TRIVERTEX(rc.X, rc.Y, startColor);
tva[1] = new Win32Helper.TRIVERTEX(rc.Right, rc.Bottom, endColor);
Win32Helper.GRADIENT_RECT[] gra = new Win32Helper.GRADIENT_RECT[] {
new Win32Helper.GRADIENT_RECT(0, 1)};
// Get the hDC from the Graphics object.
IntPtr hdc = gr.GetHdc();
// PInvoke to GradientFill.
bool b;
b = Win32Helper.GradientFill(
hdc,
tva,
(uint)tva.Length,
gra,
(uint)gra.Length,
(uint)fillDir);
System.Diagnostics.Debug.Assert(b, string.Format(
"GradientFill failed: {0}",
System.Runtime.InteropServices.Marshal.GetLastWin32Error()));
// Release the hDC from the Graphics object.
gr.ReleaseHdc(hdc);
return b;
}
/// <summary>
/// The direction to the GradientFill will follow
/// </summary>
public enum FillDirection
{
/// <summary>
/// The fill goes horizontally
/// </summary>
LeftToRight = Win32Helper.GRADIENT_FILL_RECT_H,
/// <summary>
/// The fill goes vertically
/// </summary>
TopToBottom = Win32Helper.GRADIENT_FILL_RECT_V
}
}
// Extends the standard button control and performs
// custom drawing with a GradientFill background.
public class GradientFilledButton : Control
{
#region Members
// The coordinates of the cursor the last time
// there was a MouseUp or MouseDown message.
private Point lastCursorCoordinates;
// The value of the text color.
private Color initColor = new Color();
private System.ComponentModel.IContainer components = null;
private bool gotKeyDown = false;
private Bitmap _DoubleBuffer;
private Color _EndColor = Color.Blue;
private Color _StartColor = Color.Red;
private bool _ReverseGradient = false;
private GradientFill.FillDirection _FillDirection;
private int _StartOffset;
private int _EndOffset;
private bool _Enabled = true;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="GradientFilledButton"/> class.
/// </summary>
public GradientFilledButton()
{
components = new System.ComponentModel.Container();
initColor = this.ForeColor;
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the fill direction.
/// </summary>
/// <value>The fill direction.</value>
public GradientFill.FillDirection FillDirection
{
get
{
return _FillDirection;
}
set
{
_FillDirection = value;
Invalidate();
}
}
// The start color for the GradientFill. This is the color
// at the left or top of the control depeneding on the value
// of the FillDirection property.
public Color StartColor
{
get { return _StartColor; }
set
{
_StartColor = value;
Invalidate();
}
}
// The end color for the GradientFill. This is the color
// at the right or bottom of the control depending on the value
// of the FillDirection property
public Color EndColor
{
get { return _EndColor; }
set
{
_EndColor = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets a value indicating whether [reverse gradient].
/// </summary>
/// <value><c>true</c> if [reverse gradient]; otherwise, <c>false</c>.</value>
public bool ReverseGradient
{
get { return _ReverseGradient; }
set { _ReverseGradient = value; }
}
/// <summary>
/// Gets or sets the start offset.
/// This is the offset from the left or top edge
/// of the button to start the gradient fill.
/// </summary>
/// <value>The start offset.</value>
public int StartOffset
{
get { return _StartOffset; }
set
{
_StartOffset = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the end offset.
/// This is the offset from the right or bottom edge
/// of the button to end the gradient fill.
/// </summary>
/// <value>The end offset.</value>
public int EndOffset
{
get { return _EndOffset; }
set
{
_EndOffset = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets a value indicating whether the control can respond to user interaction.
/// </summary>
/// <value></value>
/// <returns>true if the control can respond to user interaction; otherwise, false. The default is true.</returns>
/// <PermissionSet><IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/><IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/></PermissionSet>
new public bool Enabled
{
get {return _Enabled;}
set
{
// Only do if value changes
if (_Enabled != value)
{
_Enabled = value;
if (_Enabled == true)
enableControl();
else
disableControl();
Invalidate();
}
}
}
/// <summary>
/// Gets or sets the double buffer image.
/// Used to double-buffer our drawing to avoid flicker
/// between painting the background, border, focus-rect
/// and the text of the control.
/// </summary>
/// <value>The double buffer image.</value>
private Bitmap DoubleBufferImage
{
get
{
if (_DoubleBuffer == null)
_DoubleBuffer = new Bitmap(
this.ClientSize.Width,
this.ClientSize.Height);
return _DoubleBuffer;
}
set
{
if (_DoubleBuffer != null)
_DoubleBuffer.Dispose();
_DoubleBuffer = value;
}
}
#endregion
#region Events
// Called when the control is resized. When that happens,
// recreate the bitmap used for double-buffering.
protected override void OnResize(EventArgs e)
{
DoubleBufferImage = new Bitmap(
this.ClientSize.Width,
this.ClientSize.Height);
base.OnResize(e);
}
// Called when the control gets focus. Need to repaint
// the control to ensure the focus rectangle is drawn correctly.
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
this.Invalidate();
}
//
// Called when the control loses focus. Need to repaint
// the control to ensure the focus rectangle is removed.
protected override void OnLostFocus(EventArgs e)
{
base.OnLostFocus(e);
this.Invalidate();
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.MouseMove"></see> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"></see> that contains the event data.</param>
protected override void OnMouseMove(MouseEventArgs e)
{
if (this.Capture)
{
Point coord = new Point(e.X, e.Y);
if (this.ClientRectangle.Contains(coord) !=
this.ClientRectangle.Contains(lastCursorCoordinates))
{
DrawButton(this.ClientRectangle.Contains(coord));
}
lastCursorCoordinates = coord;
}
base.OnMouseMove(e);
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.MouseDown"></see> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"></see> that contains the event data.</param>
protected override void OnMouseDown(MouseEventArgs e)
{
if ( (e.Button == MouseButtons.Left) && (_Enabled == true) )
{
// Start capturing the mouse input
this.Capture = true;
// Get the focus because button is clicked.
this.Focus();
// draw the button
DrawButton(true);
}
base.OnMouseDown(e);
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.MouseUp"></see> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"></see> that contains the event data.</param>
protected override void OnMouseUp(MouseEventArgs e)
{
if (_Enabled == true)
{
this.Capture = false;
DrawButton(false);
base.OnMouseUp(e);
}
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.KeyDown"></see> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.Forms.KeyEventArgs"></see> that contains the event data.</param>
protected override void OnKeyDown(KeyEventArgs e)
{
gotKeyDown = true;
switch (e.KeyCode)
{
case System.Windows.Forms.Keys.Space:
case System.Windows.Forms.Keys.Enter:
DrawButton(true);
break;
case System.Windows.Forms.Keys.Up:
case System.Windows.Forms.Keys.Left:
this.Parent.SelectNextControl(this, false, false, true, true);
break;
case System.Windows.Forms.Keys.Down:
case System.Windows.Forms.Keys.Right:
this.Parent.SelectNextControl(this, true, false, true, true);
break;
default:
gotKeyDown = false;
base.OnKeyDown(e);
break;
}
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.KeyUp"></see> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.Forms.KeyEventArgs"></see> that contains the event data.</param>
protected override void OnKeyUp(KeyEventArgs e)
{
switch (e.KeyCode)
{
case System.Windows.Forms.Keys.Space:
case System.Windows.Forms.Keys.Enter:
if (gotKeyDown)
{
DrawButton(false);
OnClick(EventArgs.Empty);
gotKeyDown = false;
}
break;
default:
base.OnKeyUp(e);
break;
}
}
/// <summary>
/// Override this method with no code to avoid flicker.
/// </summary>
/// <param name="e"></param>
protected override void OnPaintBackground(PaintEventArgs e)
{
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.Paint"></see> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"></see> that contains the event data.</param>
protected override void OnPaint(PaintEventArgs e)
{
DrawButton(e.Graphics, this.Capture &&
(this.ClientRectangle.Contains(lastCursorCoordinates)));
}
#endregion
#region Methods
//
// Gets a Graphics object for the provided window handle
// and then calls DrawButton(Graphics, bool).
//
// If pressed is true, the button is drawn
// in the depressed state.
private void DrawButton(bool pressed)
{
Graphics gr = this.CreateGraphics();
DrawButton(gr, pressed);
gr.Dispose();
}
// Draws the button on the specified Grapics
// in the specified state.
//
// Parameters:
// gr - The Graphics object on which to draw the button.
// pressed - If true, the button is drawn in the depressed state.
private void DrawButton(Graphics gr, bool pressed)
{
// Get a Graphics object from the background image.
Graphics gr2 = Graphics.FromImage(DoubleBufferImage);
// Fill solid up until where the gradient fill starts.
if (_StartOffset > 0)
{
if (_FillDirection == GradientFill.FillDirection.LeftToRight)
{
gr2.FillRectangle(
new SolidBrush((pressed ^ _ReverseGradient) ? EndColor : StartColor),
0, 0, _StartOffset, Height);
}
else
{
gr2.FillRectangle(new SolidBrush((pressed ^ _ReverseGradient) ? EndColor : StartColor),
0, 0, Width, _StartOffset);
}
}
// Draw the gradient fill.
Rectangle rc = this.ClientRectangle;
if (_FillDirection == GradientFill.FillDirection.LeftToRight)
{
rc.X = _StartOffset;
rc.Width = rc.Width - _StartOffset - _EndOffset;
}
else
{
rc.Y = _StartOffset;
rc.Height = rc.Height - _StartOffset - _EndOffset;
}
GradientFill.Fill(
gr2,
rc,
(pressed ^ _ReverseGradient) ? _EndColor : _StartColor,
(pressed ^ _ReverseGradient) ? _StartColor : _EndColor,
_FillDirection);
// Fill solid from the end of the gradient fill
// to the edge of the button.
if (_EndOffset > 0)
{
if (_FillDirection == GradientFill.FillDirection.LeftToRight)
{
gr2.FillRectangle(new SolidBrush( (pressed ^ _ReverseGradient) ? StartColor : EndColor),
rc.X + rc.Width, 0, _EndOffset, Height);
}
else
{
gr2.FillRectangle(new SolidBrush((pressed ^ _ReverseGradient) ? StartColor : EndColor),
0, rc.Y + rc.Height, Width, _EndOffset);
}
}
// Draw the text.
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
gr2.DrawString(this.Text, this.Font,
new SolidBrush(this.ForeColor),
this.ClientRectangle, sf);
// Draw the border.
// Need to shrink the width and height by 1 otherwise
// there will be no border on the right or bottom.
rc = this.ClientRectangle;
rc.Width--;
rc.Height--;
Pen pen = new Pen(SystemColors.WindowFrame);
gr2.DrawRectangle(pen, rc);
// Draw from the background image onto the screen.
gr.DrawImage(DoubleBufferImage, 0, 0);
gr2.Dispose();
}
/// <summary>
/// Releases the unmanaged resources used by the <see cref="T:System.Windows.Forms.Control"></see> and its child controls and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
/// <summary>
/// Disables the control.
/// </summary>
protected void disableControl()
{
initColor = this.ForeColor;
this.ForeColor = Color.DarkGray;
}
/// <summary>
/// Enables the control.
/// </summary>
protected void enableControl()
{
this.ForeColor = initColor;
}
#endregion
}
/// <summary>
/// Imports Win32 API Functions
/// </summary>
public sealed class Win32Helper
{
public struct TRIVERTEX
{
public int x;
public int y;
public ushort Red;
public ushort Green;
public ushort Blue;
public ushort Alpha;
public TRIVERTEX(int x, int y, Color color)
: this(x, y, color.R, color.G, color.B, color.A)
{
}
public TRIVERTEX(
int x, int y,
ushort red, ushort green, ushort blue,
ushort alpha)
{
this.x = x;
this.y = y;
this.Red = (ushort)(red << 8);
this.Green = (ushort)(green << 8);
this.Blue = (ushort)(blue << 8);
this.Alpha = (ushort)(alpha << 8);
}
}
public struct GRADIENT_RECT
{
public uint UpperLeft;
public uint LowerRight;
public GRADIENT_RECT(uint ul, uint lr)
{
this.UpperLeft = ul;
this.LowerRight = lr;
}
}
[DllImport("coredll.dll", SetLastError = true, EntryPoint = "GradientFill")]
public extern static bool GradientFill(
IntPtr hdc,
TRIVERTEX[] pVertex,
uint dwNumVertex,
GRADIENT_RECT[] pMesh,
uint dwNumMesh,
uint dwMode);
public const int GRADIENT_FILL_RECT_H = 0x00000000;
public const int GRADIENT_FILL_RECT_V = 0x00000001;
}