No need to Inherit from Button if you want a button with a completely custom appearance.
The following class defines a Custom Button which can be set as a forms AcceptButton or CancelButton, but which is inherited from Control rather than Button. It also includes a custom TypeEditor to allow selection of corners to be rounded.
Note: that this example needs a reference to System.Design.dll since it uses a custom TypeEditor
by: Mick Dohertys'
The following class defines a Custom Button which can be set as a forms AcceptButton or CancelButton, but which is inherited from Control rather than Button. It also includes a custom TypeEditor to allow selection of corners to be rounded.
Note: that this example needs a reference to System.Design.dll since it uses a custom TypeEditor
VB net Sample:
Imports System.ComponentModel
Imports System.Security.Permissions
Imports System.Drawing.Design
Imports System.Windows.Forms.Design
Namespace Dotnetrix.Samples.VB
<Flags()> _
Public Enum Corners
None = 0
TopLeft = 1
TopRight = 2
BottomLeft = 4
BottomRight = 8
All = TopLeft Or TopRight Or BottomLeft Or BottomRight
End Enum
Public Enum CustomButtonState
[Normal] = 1
[Hot]
[Pressed]
[Disabled]
[Focused]
End Enum
Public Class CustomButton
Inherits Control
Implements IButtonControl
Public Sub New()
MyBase.New()
Me.SetStyle(ControlStyles.Selectable Or ControlStyles.StandardClick Or ControlStyles.ResizeRedraw Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.DoubleBuffer Or ControlStyles.UserPaint Or ControlStyles.SupportsTransparentBackColor, True)
End Sub
#Region " Private Instance Variables "
Private m_DialogResult As DialogResult
Private m_IsDefault As Boolean
Private m_CornerRadius As Int32 = 8
Private m_RoundCorners As Corners
Private m_ButtonState As CustomButtonState = CustomButtonState.Normal
Private m_ImageAlign As ContentAlignment = ContentAlignment.MiddleCenter
Private m_TextAlign As ContentAlignment = ContentAlignment.MiddleCenter
Private m_ImageList As ImageList
Private m_ImageIndex As Int32 = -1
Private keyPressed As Boolean
Private contentRect As Rectangle
#End Region
#Region " IButtonControl Implementation "
<Category("Behavior"), DefaultValue(GetType(DialogResult), "None"), _
Description("The dialog result produced in a modal form by clicking the button.")> _
Public Property DialogResult() As System.Windows.Forms.DialogResult Implements System.Windows.Forms.IButtonControl.DialogResult
Get
Return m_DialogResult
End Get
Set(ByVal Value As System.Windows.Forms.DialogResult)
If [Enum].IsDefined(GetType(DialogResult), Value) Then
m_DialogResult = Value
End If
End Set
End Property
Public Sub NotifyDefault(ByVal value As Boolean) Implements System.Windows.Forms.IButtonControl.NotifyDefault
If m_IsDefault <> value Then
m_IsDefault = value
End If
Me.Invalidate()
End Sub
Public Sub PerformClick() Implements System.Windows.Forms.IButtonControl.PerformClick
If Me.CanSelect Then
MyBase.OnClick(EventArgs.Empty)
End If
End Sub
#End Region
#Region " Properties "
'ButtonState
<Browsable(False)> _
Public ReadOnly Property ButtonState() As CustomButtonState
Get
Return m_ButtonState
End Get
End Property
'CornerRadius
<Category("Appearance"), _
DefaultValue(8), _
Description("Defines the radius of the controls RoundedCorners.")> _
Public Property CornerRadius() As Int32
Get
Return m_CornerRadius
End Get
Set(ByVal Value As Int32)
If m_CornerRadius = Value Then Return
m_CornerRadius = Value
Me.Invalidate()
End Set
End Property
'DefaultSize
Protected Overrides ReadOnly Property DefaultSize() As System.Drawing.Size
Get
Return New Size(75, 23)
End Get
End Property
'IsDefault
<Browsable(False)> _
Public ReadOnly Property IsDefault() As Boolean
Get
Return m_IsDefault
End Get
End Property
'ImageList
<Category("Appearance"), DefaultValue(GetType(ImageList), Nothing), _
Description("The image list to get the image to display in the face of the control.")> _
Public Property ImageList() As ImageList
Get
Return m_ImageList
End Get
Set(ByVal Value As ImageList)
m_ImageList = Value
Me.Invalidate()
End Set
End Property
'ImageIndex
<Category("Appearance"), DefaultValue(-1), _
Description("The index of the image in the image list to display in the face of the control."), _
TypeConverter(GetType(ImageIndexConverter)), _
Editor("System.Windows.Forms.Design.ImageIndexEditor, System.Design", GetType(System.Drawing.Design.UITypeEditor))> _
Public Property ImageIndex() As Int32
Get
Return m_ImageIndex
End Get
Set(ByVal Value As Int32)
m_ImageIndex = Value
Me.Invalidate()
End Set
End Property
'ImageAlign
<Category("Appearance"), DefaultValue(GetType(ContentAlignment), "MiddleCenter"), _
Description("The alignment of the image that will be displayed in the face of the control.")> _
Public Property ImageAlign() As ContentAlignment
Get
Return m_ImageAlign
End Get
Set(ByVal Value As ContentAlignment)
If Not [Enum].IsDefined(GetType(ContentAlignment), Value) Then
Throw New InvalidEnumArgumentException("value", CInt(Value), GetType(ContentAlignment))
End If
If m_ImageAlign = Value Then Return
m_ImageAlign = Value
Invalidate()
End Set
End Property
'RoundCorners
<Category("Appearance"), _
DefaultValue(GetType(Corners), "None"), _
Description("Gets/sets the corners of the control to round."), _
Editor(GetType(RoundCornersEditor), GetType(UITypeEditor)), _
DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> _
Public Property RoundCorners() As Corners
Get
Return m_RoundCorners
End Get
Set(ByVal Value As Corners)
If m_RoundCorners = Value Then Return
m_RoundCorners = Value
Me.Invalidate()
End Set
End Property
'TextAlign
<Category("Appearance"), DefaultValue(GetType(ContentAlignment), "MiddleCenter"), _
Description("The alignment of the text that will be displayed in the face of the control.")> _
Public Property TextAlign() As ContentAlignment
Get
Return m_TextAlign
End Get
Set(ByVal Value As ContentAlignment)
If Not [Enum].IsDefined(GetType(ContentAlignment), Value) Then
Throw New InvalidEnumArgumentException("value", CInt(Value), GetType(ContentAlignment))
End If
If m_TextAlign = Value Then Return
m_TextAlign = Value
Invalidate()
End Set
End Property
#End Region
#Region " Overriden Methods "
Protected Overrides Sub OnKeyDown(ByVal kevent As System.Windows.Forms.KeyEventArgs)
MyBase.OnKeyDown(kevent)
If kevent.KeyValue = Keys.Space Then
keyPressed = True
m_ButtonState = CustomButtonState.Pressed
End If
OnStateChange(EventArgs.Empty)
End Sub
Protected Overrides Sub OnKeyUp(ByVal kevent As System.Windows.Forms.KeyEventArgs)
MyBase.OnKeyUp(kevent)
If kevent.KeyValue = Keys.Space Then
If Me.ButtonState = CustomButtonState.Pressed Then
Me.PerformClick()
End If
keyPressed = False
m_ButtonState = CustomButtonState.Focused
End If
OnStateChange(EventArgs.Empty)
End Sub
Protected Overrides Sub OnMouseEnter(ByVal eventargs As System.EventArgs)
MyBase.OnMouseEnter(eventargs)
If Not keyPressed Then
m_ButtonState = CustomButtonState.Hot
End If
OnStateChange(eventargs.Empty)
End Sub
Protected Overrides Sub OnMouseLeave(ByVal eventargs As System.EventArgs)
MyBase.OnMouseLeave(eventargs)
If Not keyPressed Then
If Me.IsDefault Then
m_ButtonState = CustomButtonState.Focused
Else
m_ButtonState = CustomButtonState.Normal
End If
End If
OnStateChange(eventargs.Empty)
End Sub
Protected Overrides Sub OnMouseDown(ByVal mevent As System.Windows.Forms.MouseEventArgs)
MyBase.OnMouseDown(mevent)
If mevent.Button = MouseButtons.Left Then
Me.Focus()
m_ButtonState = CustomButtonState.Pressed
End If
OnStateChange(EventArgs.Empty)
End Sub
Protected Overrides Sub OnMouseUp(ByVal mevent As System.Windows.Forms.MouseEventArgs)
MyBase.OnMouseUp(mevent)
m_ButtonState = CustomButtonState.Focused
OnStateChange(EventArgs.Empty)
End Sub
Protected Overrides Sub OnMouseMove(ByVal mevent As System.Windows.Forms.MouseEventArgs)
MyBase.OnMouseMove(mevent)
If New Rectangle(Point.Empty, Me.Size).Contains(mevent.X, mevent.Y) AndAlso mevent.Button = MouseButtons.Left Then
m_ButtonState = CustomButtonState.Pressed
Else
If keyPressed Then Return
m_ButtonState = CustomButtonState.Hot
End If
OnStateChange(EventArgs.Empty)
End Sub
Protected Overrides Sub OnGotFocus(ByVal e As System.EventArgs)
MyBase.OnGotFocus(e)
m_ButtonState = CustomButtonState.Focused
Me.NotifyDefault(True)
End Sub
Protected Overrides Sub OnLostFocus(ByVal e As System.EventArgs)
MyBase.OnLostFocus(e)
If Me.FindForm.Focused Then
Me.NotifyDefault(False)
End If
m_ButtonState = CustomButtonState.Normal
End Sub
Protected Overrides Sub OnEnabledChanged(ByVal e As System.EventArgs)
MyBase.OnEnabledChanged(e)
If Me.Enabled Then
m_ButtonState = CustomButtonState.Normal
Else
m_ButtonState = CustomButtonState.Disabled
End If
OnStateChange(EventArgs.Empty)
End Sub
Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
'Click gets fired before MouseUp which is handy
If Me.ButtonState = CustomButtonState.Pressed Then
Me.Focus()
Me.PerformClick()
End If
End Sub
Protected Overrides Sub OnDoubleClick(ByVal e As System.EventArgs)
If Me.ButtonState = CustomButtonState.Pressed Then
Me.Focus()
Me.PerformClick()
End If
End Sub
Protected Overrides Function ProcessMnemonic(ByVal charCode As Char) As Boolean
If IsMnemonic(charCode, Me.Text) Then
MyBase.OnClick(EventArgs.Empty)
Return True
End If
Return MyBase.ProcessMnemonic(charCode)
End Function
Protected Overrides Sub OnTextChanged(ByVal e As System.EventArgs)
MyBase.OnTextChanged(e)
Me.Invalidate()
End Sub
Protected Overrides Sub OnPaintBackground(ByVal pevent As System.Windows.Forms.PaintEventArgs)
'Simulate Transparency
Dim g As System.Drawing.Drawing2D.GraphicsContainer = pevent.Graphics.BeginContainer()
Dim translateRect As Rectangle = Me.Bounds
pevent.Graphics.TranslateTransform(-Me.Left, -Me.Top)
Dim pe As PaintEventArgs = New PaintEventArgs(pevent.Graphics, translateRect)
Me.InvokePaintBackground(Me.Parent, pe)
Me.InvokePaint(Me.Parent, pe)
pevent.Graphics.ResetTransform()
pevent.Graphics.EndContainer(g)
pevent.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
Dim shadeColor, fillColor As Color
Dim darkColor As Color = DarkenColor(Me.BackColor, 10)
Dim darkDarkColor As Color = DarkenColor(Me.BackColor, 25)
Dim lightColor As Color = LightenColor(Me.BackColor, 25)
Dim lightLightColor As Color = LightenColor(Me.BackColor, 60)
If Me.ButtonState = CustomButtonState.Hot Then
fillColor = lightColor
shadeColor = darkDarkColor
ElseIf Me.ButtonState = CustomButtonState.Pressed Then
fillColor = Me.BackColor
shadeColor = Me.BackColor
Else
fillColor = Me.BackColor
shadeColor = darkDarkColor
End If
Dim r As Rectangle = Me.ClientRectangle
Dim path As Drawing2D.GraphicsPath = RoundRectangle(r, Me.CornerRadius, Me.RoundCorners)
Dim paintBrush As New Drawing2D.LinearGradientBrush(r, fillColor, shadeColor, Drawing2D.LinearGradientMode.Vertical)
'We want a sharp change in the colors so define a Blend for the brush
Dim b As New Drawing2D.Blend
b.Positions = New Single() {0, 0.45, 0.55, 1}
b.Factors = New Single() {0, 0, 1, 1}
paintBrush.Blend = b
'Draw the Button Background
pevent.Graphics.FillPath(paintBrush, path)
paintBrush.Dispose()
'...and border
Dim drawingPen As New Pen(darkDarkColor)
pevent.Graphics.DrawPath(drawingPen, path)
drawingPen.Dispose()
'Get the Rectangle to be used for Content
Dim inBounds As Boolean
'We could use some Math to get this from the radius but I'm
'not great at Math so for the example this hack will suffice.
While Not (inBounds) AndAlso r.Width >= 1 AndAlso r.Height >= 1
inBounds = path.IsVisible(r.Left, r.Top) AndAlso _
path.IsVisible(r.Right, r.Top) AndAlso _
path.IsVisible(r.Left, r.Bottom) AndAlso _
path.IsVisible(r.Right, r.Bottom)
r.Inflate(-1, -1)
End While
contentRect = r
End Sub
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
DrawImage(e.Graphics)
DrawText(e.Graphics)
DrawFocus(e.Graphics)
MyBase.OnPaint(e)
End Sub
Protected Overrides Sub OnParentBackColorChanged(ByVal e As System.EventArgs)
MyBase.OnParentBackColorChanged(e)
Me.Invalidate()
End Sub
Protected Overrides Sub OnParentBackgroundImageChanged(ByVal e As System.EventArgs)
MyBase.OnParentBackgroundImageChanged(e)
Me.Invalidate()
End Sub
#End Region
#Region " Internal Draw Methods "
Private Sub DrawImage(ByVal g As Graphics)
If Me.ImageList Is Nothing OrElse Me.ImageIndex = -1 Then Return
If Me.ImageIndex < 0 OrElse Me.ImageIndex >= Me.ImageList.Images.Count Then Return
Dim _Image As Image = Me.ImageList.Images(Me.ImageIndex)
Dim pt As Point
Select Case Me.ImageAlign
Case ContentAlignment.TopLeft
pt.X = contentRect.Left
pt.Y = contentRect.Top
Case ContentAlignment.TopCenter
pt.X = (Width - _Image.Width) \ 2
pt.Y = contentRect.Top
Case ContentAlignment.TopRight
pt.X = contentRect.Right - _Image.Width
pt.Y = contentRect.Top
Case ContentAlignment.MiddleLeft
pt.X = contentRect.Left
pt.Y = (Height - _Image.Height) \ 2
Case ContentAlignment.MiddleCenter
pt.X = (Width - _Image.Width) \ 2
pt.Y = (Height - _Image.Height) \ 2
Case ContentAlignment.MiddleRight
pt.X = contentRect.Right - _Image.Width
pt.Y = (Height - _Image.Height) \ 2
Case ContentAlignment.BottomLeft
pt.X = contentRect.Left
pt.Y = contentRect.Bottom - _Image.Height
Case ContentAlignment.BottomCenter
pt.X = (Width - _Image.Width) \ 2
pt.Y = contentRect.Bottom - _Image.Height
Case ContentAlignment.BottomRight
pt.X = contentRect.Right - _Image.Width
pt.Y = contentRect.Bottom - _Image.Height
End Select
If Me.ButtonState = CustomButtonState.Pressed Then
pt.Offset(1, 1)
End If
If Me.Enabled Then
Me.ImageList.Draw(g, pt, Me.ImageIndex)
Else
ControlPaint.DrawImageDisabled(g, _Image, pt.X, pt.Y, Me.BackColor)
End If
End Sub
Private Sub DrawText(ByVal g As Graphics)
Dim TextBrush As New SolidBrush(Me.ForeColor)
Dim R As RectangleF = RectangleF.op_Implicit(contentRect)
If Not Me.Enabled Then TextBrush.Color = SystemColors.GrayText
Dim sf As New StringFormat(StringFormatFlags.NoWrap Or StringFormatFlags.NoClip)
If ShowKeyboardCues Then
sf.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show
Else
sf.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Hide
End If
Select Case Me.TextAlign
Case ContentAlignment.TopLeft
sf.Alignment = StringAlignment.Near
sf.LineAlignment = StringAlignment.Near
Case ContentAlignment.TopCenter
sf.Alignment = StringAlignment.Center
sf.LineAlignment = StringAlignment.Near
Case ContentAlignment.TopRight
sf.Alignment = StringAlignment.Far
sf.LineAlignment = StringAlignment.Near
Case ContentAlignment.MiddleLeft
sf.Alignment = StringAlignment.Near
sf.LineAlignment = StringAlignment.Center
Case ContentAlignment.MiddleCenter
sf.Alignment = StringAlignment.Center
sf.LineAlignment = StringAlignment.Center
Case ContentAlignment.MiddleRight
sf.Alignment = StringAlignment.Far
sf.LineAlignment = StringAlignment.Center
Case ContentAlignment.BottomLeft
sf.Alignment = StringAlignment.Near
sf.LineAlignment = StringAlignment.Far
Case ContentAlignment.BottomCenter
sf.Alignment = StringAlignment.Center
sf.LineAlignment = StringAlignment.Far
Case ContentAlignment.BottomRight
sf.Alignment = StringAlignment.Far
sf.LineAlignment = StringAlignment.Far
End Select
If Me.ButtonState = CustomButtonState.Pressed Then
R.Offset(1, 1)
End If
If Me.Enabled Then
g.DrawString(Me.Text, Me.Font, TextBrush, R, sf)
Else
ControlPaint.DrawStringDisabled(g, Me.Text, Me.Font, Me.BackColor, R, sf)
End If
End Sub
Private Sub DrawFocus(ByVal g As Graphics)
Dim r As Rectangle = contentRect
r.Inflate(1, 1)
If Me.Focused AndAlso Me.ShowFocusCues AndAlso Me.TabStop Then
ControlPaint.DrawFocusRectangle(g, r, Me.ForeColor, Me.BackColor)
End If
End Sub
#End Region
#Region " Helper Methods "
Private Function RoundRectangle(ByVal r As Rectangle, ByVal radius As Int32, ByVal corners As Corners) As System.Drawing.Drawing2D.GraphicsPath
'Make sure the Path fits inside the rectangle
r.Width -= 1
r.Height -= 1
'Scale the radius if it's too large to fit.
If (radius > (r.Width)) Then radius = r.Width
If (radius > (r.Height)) Then radius = r.Height
Dim path As New System.Drawing.Drawing2D.GraphicsPath
If radius <= 0 Then
path.AddRectangle(r)
Else
If (corners And corners.TopLeft) = corners.TopLeft Then
path.AddArc(r.Left, r.Top, radius, radius, 180, 90)
Else
path.AddLine(r.Left, r.Top, r.Left, r.Top)
End If
If (corners And corners.TopRight) = corners.TopRight Then
path.AddArc(r.Right - radius, r.Top, radius, radius, 270, 90)
Else
path.AddLine(r.Right, r.Top, r.Right, r.Top)
End If
If (corners And corners.BottomRight) = corners.BottomRight Then
path.AddArc(r.Right - radius, r.Bottom - radius, radius, radius, 0, 90)
Else
path.AddLine(r.Right, r.Bottom, r.Right, r.Bottom)
End If
If (corners And corners.BottomLeft) = corners.BottomLeft Then
path.AddArc(r.Left, r.Bottom - radius, radius, radius, 90, 90)
Else
path.AddLine(r.Left, r.Bottom, r.Left, r.Bottom)
End If
path.CloseFigure()
End If
Return path
End Function
'The ControlPaint Class has methods to Lighten and Darken Colors, but they return a Solid Color.
'The Following 2 methods return a modified color with original Alpha.
Private Function DarkenColor(ByVal colorIn As Color, ByVal percent As Int32) As Color
'This method returns Black if you Darken by 100%
If percent < 0 OrElse percent > 100 Then
Throw New ArgumentOutOfRangeException("percent")
End If
Dim a, r, g, b As Int32
a = colorIn.A
r = colorIn.R - CInt((colorIn.R / 100) * percent)
g = colorIn.G - CInt((colorIn.G / 100) * percent)
b = colorIn.B - CInt((colorIn.B / 100) * percent)
Return Color.FromArgb(a, r, g, b)
End Function
Private Function LightenColor(ByVal colorIn As Color, ByVal percent As Int32) As Color
'This method returns White if you lighten by 100%
If percent < 0 OrElse percent > 100 Then
Throw New ArgumentOutOfRangeException("percent")
End If
Dim a, r, g, b As Int32
a = colorIn.A
r = colorIn.R + CInt(((255 - colorIn.R) / 100) * percent)
g = colorIn.G + CInt(((255 - colorIn.G) / 100) * percent)
b = colorIn.B + CInt(((255 - colorIn.B) / 100) * percent)
Return Color.FromArgb(a, r, g, b)
End Function
#End Region
Private Sub OnStateChange(ByVal e As EventArgs)
Static currentState As CustomButtonState
'Repaint the button only if the state has actually changed
If Me.ButtonState = currentState Then Return
currentState = Me.ButtonState
Me.Invalidate()
End Sub
End Class
#Region " Custom TypeEditor for RoundCorners property "
<PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted:=True), PermissionSetAttribute(SecurityAction.InheritanceDemand, Unrestricted:=True)> _
Public Class RoundCornersEditor
Inherits UITypeEditor
Public Overloads Overrides Function GetEditStyle(ByVal context As System.ComponentModel.ITypeDescriptorContext) As System.Drawing.Design.UITypeEditorEditStyle
Return UITypeEditorEditStyle.DropDown
End Function
Public Overloads Overrides Function EditValue(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal provider As System.IServiceProvider, ByVal value As Object) As Object
If Not TypeOf value Is Corners OrElse _
provider Is Nothing Then
Return value
End If
Dim edSvc As IWindowsFormsEditorService = CType(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService)
If Not (edSvc Is Nothing) Then
Dim lb As New CheckedListBox
lb.BorderStyle = System.Windows.Forms.BorderStyle.None
lb.CheckOnClick = True
lb.Items.Add("TopLeft", (DirectCast(context.Instance, CustomButton).RoundCorners And Corners.TopLeft) = Corners.TopLeft)
lb.Items.Add("TopRight", (DirectCast(context.Instance, CustomButton).RoundCorners And Corners.TopRight) = Corners.TopRight)
lb.Items.Add("BottomLeft", (DirectCast(context.Instance, CustomButton).RoundCorners And Corners.BottomLeft) = Corners.BottomLeft)
lb.Items.Add("BottomRight", (DirectCast(context.Instance, CustomButton).RoundCorners And Corners.BottomRight) = Corners.BottomRight)
edSvc.DropDownControl(lb)
Dim cornerFlags As Corners
For Each o As Object In lb.CheckedItems
cornerFlags = cornerFlags Or DirectCast([Enum].Parse(GetType(Corners), o.ToString), Corners)
Next
lb.Dispose()
edSvc.CloseDropDown()
Return cornerFlags
End If
Return value
End Function
End Class
#End Region
End Namespace
CSharp Sample:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Security.Permissions;
namespace Dotnetrix.Samples.CSharp
{
[System.Flags]
public enum Corners
{
None = 0,
TopLeft = 1,
TopRight = 2,
BottomLeft = 4,
BottomRight = 8,
All = TopLeft | TopRight | BottomLeft | BottomRight
}
public enum CustomButtonState
{
Normal = 1,
Hot,
Pressed,
Disabled,
Focused
}
public class CustomButton : Control, IButtonControl
{
public CustomButton():base()
{
this.SetStyle(ControlStyles.Selectable | ControlStyles.StandardClick | ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.SupportsTransparentBackColor, true);
}
#region Private Instance Variables
private DialogResult m_DialogResult;
private bool m_IsDefault;
private int m_CornerRadius = 8;
private Corners m_RoundCorners;
private CustomButtonState m_ButtonState = CustomButtonState.Normal;
private ContentAlignment m_ImageAlign = ContentAlignment.MiddleCenter;
private ContentAlignment m_TextAlign = ContentAlignment.MiddleCenter;
private ImageList m_ImageList;
private int m_ImageIndex = -1;
private bool keyPressed;
private Rectangle contentRect;
#endregion
#region IButtonControl Implementation
[Category("Behavior"), DefaultValue(typeof(DialogResult), "None")]
[Description("The dialog result produced in a modal form by clicking the button.")]
public DialogResult DialogResult
{
get{return m_DialogResult;}
set
{
if (Enum.IsDefined(typeof(DialogResult), value))
m_DialogResult = value;
}
}
public void NotifyDefault(bool value)
{
if (m_IsDefault != value)
m_IsDefault = value;
this.Invalidate();
}
public void PerformClick()
{
if (this.CanSelect)
base.OnClick(EventArgs.Empty);
}
#endregion
#region Properties
//ButtonState
[Browsable(false)]
public CustomButtonState ButtonState
{
get{return m_ButtonState;}
}
//CornerRadius
[Category("Appearance")]
[DefaultValue(8)]
[Description("Defines the radius of the controls RoundedCorners.")]
public int CornerRadius
{
get{return m_CornerRadius;}
set
{
if (m_CornerRadius == value)
return;
m_CornerRadius = value;
this.Invalidate();
}
}
//DefaultSize
protected override System.Drawing.Size DefaultSize
{
get{return new Size(75, 23);}
}
//IsDefault
[Browsable(false)]
public bool IsDefault
{
get{return m_IsDefault;}
}
//ImageList
[Category("Appearance"), DefaultValue(typeof(ImageList), null)]
[Description("The image list to get the image to display in the face of the control.")]
public ImageList ImageList
{
get{return m_ImageList;}
set
{
m_ImageList = value;
this.Invalidate();
}
}
//ImageIndex
[Category("Appearance"), DefaultValue(-1)]
[Description("The index of the image in the image list to display in the face of the control.")]
[TypeConverter(typeof(ImageIndexConverter))]
[Editor("System.Windows.Forms.Design.ImageIndexEditor, System.Design", typeof(System.Drawing.Design.UITypeEditor))]
public int ImageIndex
{
get{return m_ImageIndex;}
set
{
m_ImageIndex = value;
this.Invalidate();
}
}
//ImageAlign
[Category("Appearance"), DefaultValue(typeof(ContentAlignment), "MiddleCenter")]
[Description("The alignment of the image that will be displayed in the face of the control.")]
public ContentAlignment ImageAlign
{
get{return m_ImageAlign;}
set
{
if (!Enum.IsDefined(typeof(ContentAlignment), value))
throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment));
if (m_ImageAlign == value )
return;
m_ImageAlign = value;
this.Invalidate();
}
}
//RoundCorners
[Category("Appearance")]
[DefaultValue(typeof(Corners), "None")]
[Description("Gets/sets the corners of the control to round.")]
[Editor(typeof(RoundCornersEditor), typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Corners RoundCorners
{
get{return m_RoundCorners;}
set
{
if (m_RoundCorners == value)
return;
m_RoundCorners = value;
this.Invalidate();
}
}
//TextAlign
[Category("Appearance"), DefaultValue(typeof(ContentAlignment), "MiddleCenter")]
[Description("The alignment of the text that will be displayed in the face of the control.")]
public ContentAlignment TextAlign
{
get{return m_TextAlign;}
set
{
if (!Enum.IsDefined(typeof(ContentAlignment), value))
throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment));
if (m_TextAlign == value)
return;
m_TextAlign = value;
this.Invalidate();
}
}
#endregion
#region Overriden Methods
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown (e);
if (e.KeyCode == Keys.Space)
{
keyPressed = true;
m_ButtonState = CustomButtonState.Pressed;
}
OnStateChange(EventArgs.Empty);
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp (e);
if (e.KeyCode == Keys.Space)
{
if (this.ButtonState == CustomButtonState.Pressed)
this.PerformClick();
keyPressed = false;
m_ButtonState = CustomButtonState.Focused;
}
OnStateChange(EventArgs.Empty);
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter (e);
if (!keyPressed)
m_ButtonState = CustomButtonState.Hot;
OnStateChange(EventArgs.Empty);
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave (e);
if (!keyPressed)
if (this.IsDefault)
m_ButtonState = CustomButtonState.Focused;
else
m_ButtonState = CustomButtonState.Normal;
OnStateChange(EventArgs.Empty);
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown (e);
if (e.Button == MouseButtons.Left)
{
this.Focus();
m_ButtonState = CustomButtonState.Pressed;
}
OnStateChange(EventArgs.Empty);
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp (e);
m_ButtonState = CustomButtonState.Focused;
OnStateChange(EventArgs.Empty);
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove (e);
if (new Rectangle(Point.Empty, this.Size).Contains(e.X, e.Y) && e.Button == MouseButtons.Left)
m_ButtonState = CustomButtonState.Pressed;
else
{
if(keyPressed)
return;
m_ButtonState = CustomButtonState.Hot;
}
OnStateChange(EventArgs.Empty);
}
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus (e);
m_ButtonState = CustomButtonState.Focused;
this.NotifyDefault(true);
}
protected override void OnLostFocus(EventArgs e)
{
base.OnLostFocus (e);
if (this.FindForm().Focused)
this.NotifyDefault(false);
m_ButtonState = CustomButtonState.Normal;
}
protected override void OnEnabledChanged(EventArgs e)
{
base.OnEnabledChanged (e);
if (this.Enabled)
m_ButtonState = CustomButtonState.Normal;
else
m_ButtonState = CustomButtonState.Disabled;
OnStateChange(EventArgs.Empty);
}
protected override void OnClick(EventArgs e)
{
//Click gets fired before MouseUp which is handy
if (this.ButtonState == CustomButtonState.Pressed)
{
this.Focus();
this.PerformClick();
}
}
protected override void OnDoubleClick(EventArgs e)
{
if (this.ButtonState == CustomButtonState.Pressed)
{
this.Focus();
this.PerformClick();
}
}
protected override bool ProcessMnemonic(char charCode)
{
if (IsMnemonic(charCode, this.Text))
{
base.OnClick(EventArgs.Empty);
return true;
}
return base.ProcessMnemonic(charCode);
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
this.Invalidate();
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
//Simulate Transparency
System.Drawing.Drawing2D.GraphicsContainer g = pevent.Graphics.BeginContainer();
Rectangle translateRect = this.Bounds;
pevent.Graphics.TranslateTransform(-this.Left, -this.Top);
PaintEventArgs pe = new PaintEventArgs(pevent.Graphics, translateRect);
this.InvokePaintBackground(this.Parent, pe);
this.InvokePaint(this.Parent, pe);
pevent.Graphics.ResetTransform();
pevent.Graphics.EndContainer(g);
pevent.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
Color shadeColor, fillColor;
Color darkColor = DarkenColor(this.BackColor, 10);
Color darkDarkColor = DarkenColor(this.BackColor, 25);
Color lightColor = LightenColor(this.BackColor, 25);
Color lightLightColor = LightenColor(this.BackColor, 60);
if (this.ButtonState == CustomButtonState.Hot)
{
fillColor = lightColor;
shadeColor = darkDarkColor;
}
else if (this.ButtonState == CustomButtonState.Pressed)
{
fillColor = this.BackColor;
shadeColor = this.BackColor;
}
else
{
fillColor = this.BackColor;
shadeColor = darkDarkColor;
}
Rectangle r = this.ClientRectangle;
System.Drawing.Drawing2D.GraphicsPath path = RoundRectangle(r, this.CornerRadius, this.RoundCorners);
System.Drawing.Drawing2D.LinearGradientBrush paintBrush = new System.Drawing.Drawing2D.LinearGradientBrush(r, fillColor, shadeColor, System.Drawing.Drawing2D.LinearGradientMode.Vertical);
//We want a sharp change in the colors so define a Blend for the brush
System.Drawing.Drawing2D.Blend b = new System.Drawing.Drawing2D.Blend();
b.Positions = new float[] {0, 0.45F, 0.55F, 1};
b.Factors = new float[] {0, 0, 1, 1};
paintBrush.Blend = b;
//Draw the Button Background
pevent.Graphics.FillPath(paintBrush, path);
paintBrush.Dispose();
//...and border
Pen drawingPen = new Pen(darkDarkColor);
pevent.Graphics.DrawPath(drawingPen, path);
drawingPen.Dispose();
//Get the Rectangle to be used for Content
bool inBounds = false;
//We could use some Math to get this from the radius but I'm
//not great at Math so for the example this hack will suffice.
while (!inBounds && r.Width >= 1 && r.Height >= 1)
{
inBounds = path.IsVisible(r.Left, r.Top) &&
path.IsVisible(r.Right, r.Top) &&
path.IsVisible(r.Left, r.Bottom) &&
path.IsVisible(r.Right, r.Bottom);
r.Inflate(-1, -1);
}
contentRect = r;
}
protected override void OnPaint(PaintEventArgs e)
{
DrawImage(e.Graphics);
DrawText(e.Graphics);
DrawFocus(e.Graphics);
base.OnPaint (e);
}
protected override void OnParentBackColorChanged(EventArgs e)
{
base.OnParentBackColorChanged (e);
this.Invalidate();
}
protected override void OnParentBackgroundImageChanged(EventArgs e)
{
base.OnParentBackgroundImageChanged (e);
this.Invalidate();
}
#endregion
#region Internal Draw Methods
private void DrawImage(Graphics g)
{
if (this.ImageList == null || this.ImageIndex == -1)
return;
if (this.ImageIndex < 0 || this.ImageIndex >= this.ImageList.Images.Count)
return;
Image _Image = this.ImageList.Images[this.ImageIndex];
Point pt = Point.Empty;
switch (this.ImageAlign)
{
case ContentAlignment.TopLeft:
pt.X = contentRect.Left;
pt.Y = contentRect.Top;
break;
case ContentAlignment.TopCenter:
pt.X = (Width - _Image.Width) / 2;
pt.Y = contentRect.Top;
break;
case ContentAlignment.TopRight:
pt.X = contentRect.Right - _Image.Width;
pt.Y = contentRect.Top;
break;
case ContentAlignment.MiddleLeft:
pt.X = contentRect.Left;
pt.Y = (Height - _Image.Height) / 2;
break;
case ContentAlignment.MiddleCenter:
pt.X = (Width - _Image.Width) / 2;
pt.Y = (Height - _Image.Height) / 2;
break;
case ContentAlignment.MiddleRight:
pt.X = contentRect.Right - _Image.Width;
pt.Y = (Height - _Image.Height) / 2;
break;
case ContentAlignment.BottomLeft:
pt.X = contentRect.Left;
pt.Y = contentRect.Bottom - _Image.Height;
break;
case ContentAlignment.BottomCenter:
pt.X = (Width - _Image.Width) / 2;
pt.Y = contentRect.Bottom - _Image.Height;
break;
case ContentAlignment.BottomRight:
pt.X = contentRect.Right - _Image.Width;
pt.Y = contentRect.Bottom - _Image.Height;
break;
}
if (this.ButtonState == CustomButtonState.Pressed)
pt.Offset(1, 1);
if (this.Enabled)
this.ImageList.Draw(g, pt, this.ImageIndex);
else
ControlPaint.DrawImageDisabled(g, _Image, pt.X, pt.Y, this.BackColor);
}
private void DrawText(Graphics g)
{
SolidBrush TextBrush = new SolidBrush(this.ForeColor);
RectangleF R = (RectangleF)contentRect;
if (!this.Enabled)
TextBrush.Color = SystemColors.GrayText;
StringFormat sf = new StringFormat(StringFormatFlags.NoWrap | StringFormatFlags.NoClip);
if (ShowKeyboardCues)
sf.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show;
else
sf.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Hide;
switch (this.TextAlign)
{
case ContentAlignment.TopLeft:
sf.Alignment = StringAlignment.Near;
sf.LineAlignment = StringAlignment.Near;
break;
case ContentAlignment.TopCenter:
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Near;
break;
case ContentAlignment.TopRight:
sf.Alignment = StringAlignment.Far;
sf.LineAlignment = StringAlignment.Near;
break;
case ContentAlignment.MiddleLeft:
sf.Alignment = StringAlignment.Near;
sf.LineAlignment = StringAlignment.Center;
break;
case ContentAlignment.MiddleCenter:
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
break;
case ContentAlignment.MiddleRight:
sf.Alignment = StringAlignment.Far;
sf.LineAlignment = StringAlignment.Center;
break;
case ContentAlignment.BottomLeft:
sf.Alignment = StringAlignment.Near;
sf.LineAlignment = StringAlignment.Far;
break;
case ContentAlignment.BottomCenter:
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Far;
break;
case ContentAlignment.BottomRight:
sf.Alignment = StringAlignment.Far;
sf.LineAlignment = StringAlignment.Far;
break;
}
if (this.ButtonState == CustomButtonState.Pressed)
R.Offset(1, 1);
if (this.Enabled)
g.DrawString(this.Text, this.Font, TextBrush, R, sf);
else
ControlPaint.DrawStringDisabled(g, this.Text, this.Font, this.BackColor, R, sf);
}
private void DrawFocus(Graphics g)
{
Rectangle r = contentRect;
r.Inflate(1, 1);
if (this.Focused && this.ShowFocusCues && this.TabStop)
ControlPaint.DrawFocusRectangle(g, r, this.ForeColor, this.BackColor);
}
#endregion
#region Helper Methods
private System.Drawing.Drawing2D.GraphicsPath RoundRectangle(Rectangle r, int radius, Corners corners)
{
//Make sure the Path fits inside the rectangle
r.Width -= 1;
r.Height -= 1;
//Scale the radius if it's too large to fit.
if (radius > (r.Width))
radius = r.Width;
if (radius > (r.Height))
radius = r.Height;
System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();
if (radius <= 0 )
path.AddRectangle(r);
else
if ((corners & Corners.TopLeft) == Corners.TopLeft)
path.AddArc(r.Left, r.Top, radius, radius, 180, 90);
else
path.AddLine(r.Left, r.Top, r.Left, r.Top);
if ((corners & Corners.TopRight) == Corners.TopRight)
path.AddArc(r.Right - radius, r.Top, radius, radius, 270, 90);
else
path.AddLine(r.Right, r.Top, r.Right, r.Top);
if ((corners & Corners.BottomRight) == Corners.BottomRight)
path.AddArc(r.Right - radius, r.Bottom - radius, radius, radius, 0, 90);
else
path.AddLine(r.Right, r.Bottom, r.Right, r.Bottom);
if ((corners & Corners.BottomLeft) == Corners.BottomLeft)
path.AddArc(r.Left, r.Bottom - radius, radius, radius, 90, 90);
else
path.AddLine(r.Left, r.Bottom, r.Left, r.Bottom);
path.CloseFigure();
return path;
}
//The ControlPaint Class has methods to Lighten and Darken Colors, but they return a Solid Color.
//The Following 2 methods return a modified color with original Alpha.
private Color DarkenColor(Color colorIn, int percent)
{
//This method returns Black if you Darken by 100%
if (percent < 0 || percent > 100)
throw new ArgumentOutOfRangeException("percent");
int a, r, g, b;
a = colorIn.A;
r = colorIn.R - (int)((colorIn.R / 100f) * percent);
g = colorIn.G - (int)((colorIn.G / 100f) * percent);
b = colorIn.B - (int)((colorIn.B / 100f) * percent);
return Color.FromArgb(a, r, g, b);
}
private Color LightenColor(Color colorIn, int percent)
{
//This method returns White if you lighten by 100%
if (percent < 0 || percent > 100)
throw new ArgumentOutOfRangeException("percent");
int a, r, g, b;
a = colorIn.A;
r = colorIn.R + (int)(((255f - colorIn.R) / 100f) * percent);
g = colorIn.G + (int)(((255f - colorIn.G) / 100f) * percent);
b = colorIn.B + (int)(((255f - colorIn.B) / 100f) * percent);
return Color.FromArgb(a, r, g, b);
}
#endregion
private CustomButtonState currentState;
private void OnStateChange(EventArgs e)
{
//Repaint the button only if the state has actually changed
if (this.ButtonState == currentState)
return;
currentState = this.ButtonState;
this.Invalidate();
}
}
#region Custom TypeEditor for RoundCorners property
[PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted=true)]
[PermissionSetAttribute(SecurityAction.InheritanceDemand, Unrestricted=true)]
public class RoundCornersEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.DropDown;
}
public override Object EditValue(ITypeDescriptorContext context, IServiceProvider provider, Object value)
{
if (value != typeof(Corners) || provider == null)
return value;
IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
if (edSvc != null)
{
CheckedListBox lb = new CheckedListBox();
lb.BorderStyle = BorderStyle.None;
lb.CheckOnClick = true;
lb.Items.Add("TopLeft", (((CustomButton)context.Instance).RoundCorners & Corners.TopLeft) == Corners.TopLeft);
lb.Items.Add("TopRight", (((CustomButton)context.Instance).RoundCorners & Corners.TopRight) == Corners.TopRight);
lb.Items.Add("BottomLeft", (((CustomButton)context.Instance).RoundCorners & Corners.BottomLeft) == Corners.BottomLeft);
lb.Items.Add("BottomRight", (((CustomButton)context.Instance).RoundCorners & Corners.BottomRight) == Corners.BottomRight);
edSvc.DropDownControl(lb);
Corners cornerFlags = Corners.None;
foreach (object o in lb.CheckedItems)
{
cornerFlags = cornerFlags | (Corners)Enum.Parse(typeof(Corners), o.ToString());
}
lb.Dispose();
edSvc.CloseDropDown();
return cornerFlags;
}
return value;
}
}
#endregion
}
by: Mick Dohertys'













0 comments:
Post a Comment