触摸屏的按钮开发起来非常方便,只要设置好样式和文本,以及绑定的变量,一个开关就做好了。
而用winform要实现同样的功能,要编写许多代码。
花木兰控件库的ToggleButton可以这样做切换,但是有几点不方便的地方。于是修改了以下几点:
- 文本修改成可以换行:
文本居中且可以自动换行,而且通过修改Padding可以设置每一行显示多少个字,比如我要改成每行最多三个字:
- 圆角修改成可以设置
- 点击时切换文本
最终效果就是点击切换状态、背景和文本。
查看按钮的状态。
private void toggleButton1_Click(object sender, EventArgs e)
{
HML.ToggleButton btn = (HML.ToggleButton)sender;
MessageBox.Show(btn.Checked.ToString());
}
控件库源码:添加链接描述
打开解决方案把 HML 项目里面的 ToggleButton 的代码替换成下面的代码,把HML项目重新生成,获得 HML.ComplexityPropertys.dll 和 HML.dll 文件,引入自己的项目即可。
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace HML
{
/// <summary>
/// 开关按钮控件
/// </summary>
[Description("开关按钮控件")]
[ToolboxItem(true)]
[DefaultProperty("Checked")]
[DefaultEvent("Click")]
[TypeConverter(typeof(PropertyOrderConverter))]
[ToolboxBitmap(typeof(ToggleButton), "Controls.ToggleButton.ToggleButton.bmp")]
public class ToggleButton : MainThreadAnimationControl
{
#region 新增事件
private static readonly object EventCheckedChanged = new object();
/// <summary>
/// 开关状态更改事件
/// </summary>
[Description("开关状态更改事件")]
public event EventHandler CheckedChanged
{
add { Events.AddHandler(EventCheckedChanged, value); }
remove { Events.RemoveHandler(EventCheckedChanged, value); }
}
#endregion
#region 停用事件
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler RightToLeftChanged
{
add { base.RightToLeftChanged += value; }
remove { base.RightToLeftChanged -= value; }
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler ImeModeChanged
{
add { base.ImeModeChanged += value; }
remove { base.ImeModeChanged -= value; }
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler BackColorChanged
{
add { base.BackColorChanged += value; }
remove { base.BackColorChanged -= value; }
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler ForeColorChanged
{
add { base.ForeColorChanged += value; }
remove { base.ForeColorChanged -= value; }
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler BackgroundImageChanged
{
add { base.BackgroundImageChanged += value; }
remove { base.BackgroundImageChanged -= value; }
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler BackgroundImageLayoutChanged
{
add { base.BackgroundImageLayoutChanged += value; }
remove { base.BackgroundImageLayoutChanged -= value; }
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler DoubleClick
{
add { base.DoubleClick += value; }
remove { base.DoubleClick -= value; }
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new event MouseEventHandler MouseDoubleClick
{
add { base.MouseDoubleClick += value; }
remove { base.MouseDoubleClick -= value; }
}
#endregion
#region 新增属性
private string textChecked="中转爪已闭合";
/// <summary>
/// 图标Size
/// </summary>
[Description("按钮Checked文本")]
[Category("杂项")]
[DefaultValue(typeof(string), "中转爪已闭合")]
public string TextChecked
{
get { return textChecked; }
set { textChecked = value; Invalidate(); }
}
private string textUnChecked = "中转爪已打开";
/// <summary>
/// 图标Size
/// </summary>
[Description("按钮UnChecked文本")]
[Category("杂项")]
[DefaultValue(typeof(string), "中转爪已打开")]
public string TextUnChecked
{
get { return textUnChecked; }
set { textUnChecked = value; Invalidate(); }
}
private int cornerRadius = 10;
/// <summary>
/// 图标Size
/// </summary>
[Description("按钮圆角角度")]
[Category("杂项")]
[DefaultValue(typeof(int), "10")]
public int CornerRadius
{
get { return cornerRadius; }
set { cornerRadius = value; Invalidate(); }
}
private float paddingLR = 30;
/// <summary>
/// 图标Size
/// </summary>
[Description("按钮左右内边距")]
[Category("杂项")]
[DefaultValue(typeof(float), "30")]
public float PaddingLR
{
get { return paddingLR; }
set
{
paddingLR = value;
this.Invalidate();
}
}
private Color activateColor = Color.White;
/// <summary>
/// 控件激活的虚线框颜色
/// </summary>
[Description("控件激活的虚线框颜色")]
[Category("杂项")]
[PropertyOrder(-200)]
[DefaultValue(typeof(Color), "White")]
public Color ActivateColor
{
get { return this.activateColor; }
set
{
if (this.activateColor == value)
return;
this.activateColor = value;
this.Invalidate();
}
}
private bool roundEnabled = true;
/// <summary>
/// 是否启用圆角
/// </summary>
[Description("是否启用圆角")]
[Category("杂项")]
[PropertyOrder(-198)]
[DefaultValue(true)]
public bool RoundEnabled
{
get { return this.roundEnabled; }
set
{
if (this.roundEnabled == value)
return;
this.roundEnabled = value;
this.SetControlShape();
this.Invalidate();
}
}
private bool autoCheck = true;
/// <summary>
/// 单击时是否自动更改控件状态
/// </summary>
[Description("单击时是否自动更改控件状态")]
[Category("杂项")]
[PropertyOrder(-197)]
[DefaultValue(true)]
public bool AutoCheck
{
get { return this.autoCheck; }
set
{
if (this.autoCheck == value)
return;
this.autoCheck = value;
}
}
private bool _Checked = false;
/// <summary>
/// 开关状态
/// </summary>
[Description("开关状态")]
[Category("杂项")]
[PropertyOrder(-196)]
[DefaultValue(false)]
public bool Checked
{
get { return this._Checked; }
set
{
if (this._Checked == value)
return;
this._Checked = value;
this.Invalidate();
if (!this.DesignMode)
{
this.OnCheckedChanged(new EventArgs());
}
}
}
private Color backUnCheckedColor = Color.FromArgb(33, 90, 106);
/// <summary>
/// 背景颜色(未选中、正常)
/// </summary>
[Description("背景颜色(未选中、正常)")]
[Category("杂项")]
[PropertyOrder(-193)]
[DefaultValue(typeof(Color), "33,90,106")]
public Color BackUnCheckedColor
{
get { return this.backUnCheckedColor; }
set
{
if (this.backUnCheckedColor == value)
return;
this.backUnCheckedColor = value;
this.Invalidate();
}
}
private Color textUnCheckedColor = Color.White;
/// <summary>
/// 文本颜色(未选中、正常)
/// </summary>
[Description("文本颜色(未选中、正常)")]
[Category("杂项")]
[PropertyOrder(-192)]
[DefaultValue(typeof(Color), "White")]
public Color TextUnCheckedColor
{
get { return this.textUnCheckedColor; }
set
{
if (this.textUnCheckedColor == value)
return;
this.textUnCheckedColor = value;
this.Invalidate();
}
}
private Color backCheckedColor = Color.FromArgb(157, 222, 237);
/// <summary>
/// 背景颜色(已选中、正常)
/// </summary>
[Description("背景颜色(已选中、正常)")]
[Category("杂项")]
[PropertyOrder(-188)]
[DefaultValue(typeof(Color), "157,222,237")]
public Color BackCheckedColor
{
get { return this.backCheckedColor; }
set
{
if (this.backCheckedColor == value)
return;
this.backCheckedColor = value;
this.Invalidate();
}
}
private Color textCheckedColor = Color.Black;
/// <summary>
/// 文本颜色(已选中、正常)
/// </summary>
[Description("文本颜色(已选中、正常)")]
[Category("杂项")]
[PropertyOrder(-187)]
[DefaultValue(typeof(Color), "White")]
public Color TextCheckedColor
{
get { return this.textCheckedColor; }
set
{
if (this.textCheckedColor == value)
return;
this.textCheckedColor = value;
this.Invalidate();
}
}
private Color clickColor = Color.FromArgb(106, 100, 98);
/// <summary>
/// 背景颜色(未选中、正常)
/// </summary>
[Description("点击颜色")]
[Category("杂项")]
[PropertyOrder(-193)]
[DefaultValue(typeof(Color), "106,100,98")]
public Color ClickColor
{
get { return this.clickColor; }
set
{
if (this.clickColor == value)
return;
this.clickColor = value;
this.Invalidate();
}
}
#endregion
#region 重写属性
protected override Padding DefaultPadding
{
get { return new Padding(3, 3, 3, 3); }
}
protected override Size DefaultSize
{
get { return new Size(90, 24); }
}
//public override string Text
//{
// get { return base.Text; }
// set
// {
// base.Text = value;
// this.Invalidate();
// }
//}
protected override ImeMode DefaultImeMode
{
get { return ImeMode.Disable; }
}
#endregion
#region 停用属性
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new string Text
{
get { return base.Text; }
set { base.Text = value; }
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new Padding Padding
{
get { return base.Padding; }
set { base.Padding = value; }
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new ImeMode ImeMode
{
get { return base.ImeMode; }
set { base.ImeMode = value; }
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override RightToLeft RightToLeft
{
get { return base.RightToLeft; }
set { base.RightToLeft = value; }
}
[DefaultValue(typeof(Color), "Transparent")]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Color BackColor
{
get { return base.BackColor; }
set { base.BackColor = value; }
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override Color ForeColor
{
get { return base.ForeColor; }
set { base.ForeColor = value; }
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Image BackgroundImage
{
get { return base.BackgroundImage; }
set { base.BackgroundImage = value; }
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new ImageLayout BackgroundImageLayout
{
get { return base.BackgroundImageLayout; }
set { base.BackgroundImageLayout = value; }
}
#endregion
#region 字段
/// <summary>
/// 鼠标按下类型
/// </summary>
private MouseButtons mousedowntype = MouseButtons.None;
/// <summary>
/// 鼠标已进入对象
/// </summary>
private object mouseenterobject = null;
/// <summary>
/// 鼠标单击动画是否进行中
/// </summary>
private bool animationing = false;
/// <summary>
/// 鼠标单击动画动画总时间
/// </summary>
private double animationAllTime = 250;
/// <summary>
/// 鼠标单击动画已使用时间
/// </summary>
private double animationUsedTime = 0;
/// <summary>
/// 鼠标单击动画圆心坐标
/// </summary>
private Point animationpoint = Point.Empty;
#endregion
StringFormat stringFormat = new StringFormat();
public ToggleButton()
{
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.StandardClick, false);//自定义单击事件逻辑
SetStyle(ControlStyles.StandardDoubleClick, false);//停用双加事件
this.SuspendLayout();
this.BackColor = Color.Transparent;
this.SetControlShape();
this.ResumeLayout();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
}
#region 重写
protected override void OnScaleDpiChangedInitialize()
{
this.Invalidate();
}
GraphicsPath graphicsPath = new GraphicsPath();
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.HighQuality; //图片柔顺模式选择
g.InterpolationMode = InterpolationMode.HighQualityBicubic;//高质量
g.CompositingQuality = CompositingQuality.HighQuality;//再加一点
graphicsPath.Reset();
graphicsPath.AddArc(GetCornerRect1(), 270, 90);
var points = GetLine1();
graphicsPath.AddLine(points.Item1, points.Item2);
graphicsPath.AddArc(GetCornerRect2(), 0, 90);
points = GetLine2();
graphicsPath.AddLine(points.Item1, points.Item2);
graphicsPath.AddArc(GetCornerRect3(), 90, 90);
points = GetLine3();
graphicsPath.AddLine(points.Item1, points.Item2);
graphicsPath.AddArc(GetCornerRect4(), 180, 90);
points = GetLine4();
graphicsPath.AddLine(points.Item1, points.Item2);
if (_Checked == false)
{
g.FillPath(new SolidBrush(backUnCheckedColor), graphicsPath);
}
else
{
g.FillPath(new SolidBrush(backCheckedColor), graphicsPath);
}
//SolidBrush back_sb = new SolidBrush(Color.Brown);
//if (this.animationing)//点击动画
//{
// g.SmoothingMode = SmoothingMode.AntiAlias;
// //int animationRadius = (int)Math.Ceiling(ControlHelper.AdjustRectangleMaxRadiusForPoint(this.ClientRectangle, this.animationpoint) * this.animationUsedTime / this.animationAllTime * 10 / 8);
// int animationRadius = 20;
// back_sb.Color = ControlHelper.ConvertToAnimationColor(clickColor);
// g.FillEllipse(back_sb, new RectangleF(this.animationpoint.X - animationRadius, this.animationpoint.Y - animationRadius, animationRadius * 2, animationRadius * 2));
// g.SmoothingMode = SmoothingMode.Default;
//}
// 文本
g.SmoothingMode = SmoothingMode.Default;
SolidBrush text_sb;
if (_Checked == false){
text_sb = new SolidBrush(textUnCheckedColor);
}
else
{
text_sb = new SolidBrush(textCheckedColor);
}
if (_Checked == false)
{
g.DrawString(TextUnChecked, this.Font, text_sb, new RectangleF(paddingLR, 2, Width - paddingLR * 2, Height), stringFormat);
}
else
{
g.DrawString(TextChecked, this.Font, text_sb, new RectangleF(paddingLR, 2, Width - paddingLR * 2, Height), stringFormat);
}
text_sb.Dispose();
//控件激活状态虚线框
if (this.Focused && this.ShowFocusCues)
{
Pen activated_border_pen = new Pen(this.ActivateColor) { DashStyle = DashStyle.Dot, Alignment = PenAlignment.Center };
Rectangle activated_border_rect = new Rectangle(this.ClientRectangle.X + 3, this.ClientRectangle.Y + 3, this.ClientRectangle.Width - 7, this.ClientRectangle.Height - 7);
g.DrawRectangle(activated_border_pen, activated_border_rect);
activated_border_pen.Dispose();
}
}
/// <summary>
/// 右上角弧线
/// </summary>
/// <returns></returns>
private Rectangle GetCornerRect1()
{
return new Rectangle(Width - cornerRadius - 1, 1, cornerRadius, cornerRadius);
}
/// <summary>
/// 右边线段
/// </summary>
/// <returns></returns>
private Tuple<Point, Point> GetLine1()
{
return new Tuple<Point, Point>(new Point(Width - 1, cornerRadius / 2),
new Point(Width - 1, Height - cornerRadius / 2));
}
/// <summary>
/// 右下角弧线
/// </summary>
/// <returns></returns>
private Rectangle GetCornerRect2()
{
return new Rectangle(Width - cornerRadius - 1, Height - cornerRadius - 1, cornerRadius, cornerRadius);
}
/// <summary>
/// 下边线段
/// </summary>
/// <returns></returns>
private Tuple<Point, Point> GetLine2()
{
return new Tuple<Point, Point>(new Point(cornerRadius / 2 + 1, Height - 1),
new Point(Width - cornerRadius / 2 - 1, Height - 1));
}
/// <summary>
/// 左下角弧线
/// </summary>
/// <returns></returns>
private Rectangle GetCornerRect3()
{
return new Rectangle(1, Height - cornerRadius - 1, cornerRadius, cornerRadius);
}
/// <summary>
/// 左边线段
/// </summary>
/// <returns></returns>
private Tuple<Point, Point> GetLine3()
{
return new Tuple<Point, Point>(new Point(1, Height - cornerRadius / 2 - 1),
new Point(1, cornerRadius / 2 + 1));
}
/// <summary>
/// 左上角弧线
/// </summary>
/// <returns></returns>
private Rectangle GetCornerRect4()
{
return new Rectangle(1, 1, cornerRadius, cornerRadius);
}
/// <summary>
/// 上边线段
/// </summary>
/// <returns></returns>
private Tuple<Point, Point> GetLine4()
{
return new Tuple<Point, Point>(
new Point(cornerRadius / 2 + 1, 1), new Point(Width - cornerRadius / 2 - 1, 1));
}
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
this.Invalidate();
}
protected override void OnLostFocus(EventArgs e)
{
base.OnLostFocus(e);
this.mousedowntype = MouseButtons.None;
this.Invalidate();
}
protected override void OnEnabledChanged(EventArgs e)
{
base.OnEnabledChanged(e);
if (!this.Enabled)
{
this.mousedowntype = MouseButtons.None;
this.mouseenterobject = null;
this.Invalidate();
}
}
protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
if (!this.Visible)
{
this.mousedowntype = MouseButtons.None;
this.mouseenterobject = null;
this.Invalidate();
}
}
protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);
if (this.Parent == null)
{
this.mousedowntype = MouseButtons.None;
this.mouseenterobject = null;
this.Invalidate();
}
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
this.mouseenterobject = this;
this.Invalidate();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
this.mouseenterobject = null;
this.Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (!this.Focused)
{
this.Focus();
}
this.mousedowntype |= e.Button;
if (e.Button == MouseButtons.Left)
{
this.animationing = true;
this.animationUsedTime = 0;
this.animationpoint = e.Location;
MainThreadAnimationControl.AnimationStart(this);
this.Invalidate();
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (this.mousedowntype.HasFlag(MouseButtons.Left) && this.ClientRectangle.Contains(e.Location))
{
this.OnClick(e);
this.OnMouseClick(e);
if (this.AutoCheck)
{
this.Checked = !this.Checked;
}
}
}
this.mousedowntype &= ~e.Button;
base.OnMouseUp(e);
}
protected override void OnKeyDown(KeyEventArgs e)
{
switch (e.KeyData)
{
case Keys.Space:
{
this.animationing = true;
this.animationUsedTime = 0;
this.animationpoint = new Point(this.ClientRectangle.X + this.ClientRectangle.Width / 2, this.ClientRectangle.Y + this.ClientRectangle.Height / 2);
MainThreadAnimationControl.AnimationStart(this);
this.Invalidate();
break;
}
}
base.OnKeyDown(e);
}
protected override void OnKeyUp(KeyEventArgs e)
{
switch (e.KeyData)
{
case Keys.Space:
{
this.OnClick(EventArgs.Empty);
break;
}
}
base.OnKeyUp(e);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.SetControlShape();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
MainThreadAnimationControl.AnimationStop(this);
}
base.Dispose(disposing);
}
/// <summary>
/// 动画控件动画中要处理的内容(不能时耗时操作)
/// </summary>
/// <param name="interval">动画定时器间隔时间</param>
protected override void Animationing(int interval)
{
bool finish = false;
this.animationUsedTime += interval;
if (this.animationUsedTime > this.animationAllTime)
{
this.animationUsedTime = this.animationAllTime;
MainThreadAnimationControl.AnimationStop(this);
finish = true;
}
if (finish)
{
this.animationing = false;
this.animationUsedTime = 0;
this.animationpoint = Point.Empty;
}
this.Invalidate();
}
#endregion
#region 虚方法
protected virtual void OnCheckedChanged(EventArgs e)
{
EventHandler handler = Events[EventCheckedChanged] as EventHandler;
if (handler != null)
{
handler(this, e);
}
}
#endregion
#region 私有方法
/// <summary>
/// 设置控件形状
/// </summary>
private void SetControlShape()
{
GraphicsPath gp = new GraphicsPath();
if (this.RoundEnabled)
{
gp = ControlHelper.AdjustRectangleShapePath(this.ClientRectangle);
}
else
{
gp.AddRectangle(this.ClientRectangle);
}
this.Region = new Region(gp);
gp.Dispose();
}
/// <summary>
/// ImageList更改后刷新控件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RecreateImageListHandler(object sender, EventArgs e)
{
if (this.IsHandleCreated)
{
this.Invalidate();
}
}
/// <summary>
/// ImageList释放时解绑清除清除引用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DetachImageListHandler(object sender, EventArgs e)
{
}
#endregion
}
}