自定义控件学习 《一》
自定义控件学习
目录
一、 前言
- 什么是控件?
- 什么是组件?
- 如何创建与使用自定义控件
控件和组件:
- 本质上都是从类继承出的子类。组件派生于Component类,而控件派生于Control类,用户控件派生于UserControl.
- 表现上:组件Component是没有界面的,控件Control是有界面的。简单的说,组件是在设计器界面上看不到,控件在设计界面上是界面可见的。
- 关系说明:每一个可以重复使用并且可以与其他对象交互的对象都是组件,拥有UI界面的组件就是控件。就是说大家都是组件,但是有界面的组件就是控件。
- 控件的常见分类:复合,扩展和自定义三类。
- 复合控件:将多个已有控件组合成一个新的控件进行开发。
- 扩展控件:从已有的窗体控件中继承来的控件,是在原有基础上简单二次开发的控件。
- 自定义控件:继承自Control类,从头开发的控件。
见如下案例 首先是简单的组件(无UI界面):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FrmMain
{
public partial class ComponentExample : Component
{
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
}
}
编写一个简单的扩展控件案例(有UI界面)(代码基本相同,但是派生于Button类):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FrmMain
{
public partial class ComponentExample : Button
{
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
}
}
二、 使用简单的GDI绘制自定义控件
案例1 绘制一个led指示灯
需要设置属性如下:
- 外环宽度。2.外环间隙。3.内环间隙。4.颜色(五种)。5.当前值
先贴上代码,再补充相应的知识
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FrmMain
{
public partial class ZLed : UserControl
{
public ZLed()
{
InitializeComponent();
//设置控件样式 重绘图像固定加入
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.Selectable, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.SetStyle(ControlStyles.UserPaint, true);
}
#region Fields
//画布
private Graphics g;
//画笔
private Pen p;
//画刷
private SolidBrush sb;
//宽度
private int width;
//高度
private int height;
private float outWidth = 4.0f;
[Browsable(true)]
[Category("自定义属性")]
[Description("外环宽度")]
public float OutWidth
{
get { return outWidth; }
set
{
if (value <= 0 || value > 0.1f * this.Width)
{
return;
}
outWidth = value;
this.Invalidate();
}
}
private float outGap = 3.0f;
[Browsable(true)]
[Category("自定义属性")]
[Description("外环间隙")]
public float OutGap
{
get { return outGap; }
set
{
if (value <= 0 || value > 0.1f * this.Width || inGap <= value)
{
return;
}
outGap = value;
this.Invalidate();
}
}
private float inGap = 8.0f;
[Browsable(true)]
[Category("自定义属性")]
[Description("内环间隙")]
public float InGap
{
get { return inGap; }
set
{
if (value <= 0 || value <= outGap)
{
return;
}
inGap = value;
this.Invalidate();
}
}
private Color color1 = Color.Gray;
[Browsable(true)]
[Category("自定义属性")]
[Description("第一种颜色")]
public Color Color1
{
get { return color1; }
set
{
color1 = value;
this.Invalidate();
}
}
private Color color2 = Color.LimeGreen;
[Browsable(true)]
[Category("自定义属性")]
[Description("第二种颜色")]
public Color Color2
{
get { return color2; }
set
{
color2 = value;
this.Invalidate();
}
}
private Color color3 = Color.Red;
[Browsable(true)]
[Category("自定义属性")]
[Description("第三种颜色")]
public Color Color3
{
get { return color3; }
set
{
color3 = value;
this.Invalidate();
}
}
private Color color4 = Color.DarkGoldenrod;
[Browsable(true)]
[Category("自定义属性")]
[Description("第四种颜色")]
public Color Color4
{
get { return color4; }
set
{
color4 = value;
this.Invalidate();
}
}
private Color color5 = Color.Blue;
[Browsable(true)]
[Category("自定义属性")]
[Description("第五种颜色")]
public Color Color5
{
get { return color5; }
set
{
color5 = value;
this.Invalidate();
}
}
private int currentValue = 0;
[Browsable(true)]
[Category("自定义属性")]
[Description("当前值")]
public int CurrentValue
{
get { return currentValue; }
set
{
if (value > 4 || value < 0)
{
return;
}
currentValue = value;
this.Invalidate();
}
}
#endregion
#region Override
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
g = e.Graphics;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
this.width = this.Width;
this.height = this.Height;
//特殊情况处理
if (inGap > 0.5f * this.width || inGap > 0.5f * this.height)
{
return;
}
Color currentColor = GetCurrentColor();
//绘制外环
p = new Pen(currentColor, outWidth);
RectangleF rec = new RectangleF(outGap, outGap, this.width - 2 * outGap, this.height - 2 * outGap);
g.DrawEllipse(p, rec);
//绘制内圆
sb = new SolidBrush(currentColor);
rec = new RectangleF(inGap, inGap, this.width - 2 * inGap, this.height - 2 * inGap);
g.FillEllipse(sb, rec);
}
#endregion
private Color GetCurrentColor()
{
List<Color> ColorList = new List<Color>();
ColorList.Add(color1);
ColorList.Add(color2);
ColorList.Add(color3);
ColorList.Add(color4);
ColorList.Add(color5);
return ColorList[currentValue];
}
}
}
所需基础知识如下:
第一部分 Pen 画笔类,Graphics画板类与Brush填充类
1. Graphics画板:
Graphics对象
使用Graphics可以将图像直接绘制到设备上。
上述代码中,在绘制外环中使用了该对象
p = new Pen(currentColor, outWidth);
RectangleF rec = new RectangleF(outGap, outGap, this.width - 2 * outGap, this.height - 2 * outGap);
g.DrawEllipse(p, rec);
DrawEllipse方法中传入了画笔对象(设置了绘画的线条样式,传入颜色与线条宽度)与长方体对象(起始坐标,外接矩形的长与宽)。利用外接矩形来绘制圆
Graphics的其他方法如下:
- DrawLine(pen,10,15,111,222) // 绘制直线 起始坐标到终点坐标
- DrawRectangle(pen,10,15,111,222)//绘制矩形 起始坐标,宽度,长度
2. Pen 画笔类
主要属性如下(一般会和画板类一起使用):
- Color
- DashCap 短划线终点形状
- DashStyle(虚线样式)
- EndCap(线尾形状)
- StartCap(线头形状)
- Width(粗细)
3. Brush填充类
常见的使用类如下:
- SolidBrush:使用纯色进行填充
- HatchBrush:使用图案进行填充
- TextureBrush:可以使用纹理进行填充
- LinearGradientBrush :使用沿渐变混合的两种颜色进行绘制
- PathGradientBrush : 基于编程者定义的唯一路径,使用复杂的混合色渐变进行绘制
sb = new SolidBrush(currentColor);
rec = new RectangleF(inGap, inGap, this.width - 2 * inGap, this.height - 2 * inGap);
g.FillEllipse(sb, rec);
第二部分 绘制图形美化
作用如下:消除锯齿,文本平滑化
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
第三部分 案例
案例二
案例2描述如下: 绘制一个可以改变样式滑块(一种是椭圆的,另外一种是圆角矩形的)
大致思路:先编写属性,然后编写重绘方法(重点是 GraphicsPath的使用)
样式大致如下:
代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FrmMain
{
[DefaultEvent("CheckedChanged")]
public partial class ZToggle : UserControl
{
public ZToggle()
{
InitializeComponent();
//设置控件样式
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.Selectable, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.MouseDown += XToggle_MouseDown;
}
#region Fileds
private Graphics g;
private int width;
private int height;
public enum SwitchType
{
//椭圆
Ellipse,
//矩形
Rectangle,
}
private bool _checked = false;
[Browsable(true)]
[Category("自定义属性")]
[Description("是否选中")]
public bool Checked
{
get { return _checked; }
set
{
_checked = value;
this.Invalidate();
//触发一个事件
this.CheckedChanged?.Invoke(this, null);
}
}
private Color falseColor = Color.FromArgb(180, 180, 180);
[Browsable(true)]
[Category("自定义属性")]
[Description("没有选中时的颜色")]
public Color FalseColor
{
get { return falseColor; }
set
{
falseColor = value;
this.Invalidate();
}
}
private Color trueColor = Color.FromArgb(73, 119, 232);
[Browsable(true)]
[Category("自定义属性")]
[Description("选中时的颜色")]
public Color TrueColor
{
get { return trueColor; }
set
{
trueColor = value;
this.Invalidate();
}
}
private string falseText = "关闭";
[Browsable(true)]
[Category("自定义属性")]
[Description("没有选中时的文本")]
public string FalseText
{
get { return falseText; }
set
{
falseText = value;
this.Invalidate();
}
}
private string trueText = "打开";
[Browsable(true)]
[Category("自定义属性")]
[Description("选中时的文本")]
public string TrueText
{
get { return trueText; }
set
{
trueText = value;
this.Invalidate();
}
}
private SwitchType switchtype = SwitchType.Ellipse;
[Browsable(true)]
[Category("自定义属性")]
[Description("切换样式")]
public SwitchType Switchtype
{
get { return switchtype; }
set
{
switchtype = value;
this.Invalidate();
}
}
private int circleDiameter = 10;
[Browsable(true)]
[Category("自定义属性")]
[Description("小圆形直径")]
public int CircleDiameter
{
get { return circleDiameter; }
set
{
if (value >= this.Width || value >= this.Height || value <= 0)
{
return;
}
circleDiameter = value;
this.Invalidate();
}
}
private int circlePosition = 10;
[Browsable(true)]
[Category("自定义属性")]
[Description("小圆形位置")]
public int CirclePosition
{
get { return circlePosition; }
set
{
if (value >= this.Width || value >= this.Height || value <= 0)
{
return;
}
circlePosition = value;
this.Invalidate();
}
}
private Color sliderColor = Color.White;
[Browsable(true)]
[Category("自定义属性")]
[Description("滑块颜色")]
public Color SliderColor
{
get { return sliderColor; }
set
{
sliderColor = value;
this.Invalidate();
}
}
#endregion
#region Event
[Browsable(true)]
[Category("自定义事件")]
[Description("选中改变事件")]
public event EventHandler CheckedChanged;
private void XToggle_MouseDown(object sender, MouseEventArgs e)
{
Checked = !Checked;
}
#endregion
#region Override
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
g = e.Graphics;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
this.width = this.Width;
this.height = this.Height;
//矩形样式绘制
if (this.switchtype == SwitchType.Rectangle)
{
//确定填充色
Color fillColor = this._checked ? trueColor : falseColor;
//画带圆弧的矩形
GraphicsPath path = new GraphicsPath();
int diameter = 5;
// 左上角 起始从0,0. 外接矩形的长度与宽度。开始从180旋转90度
path.AddArc(0, 0, diameter, diameter, 180f, 90f);
// 右上角
path.AddArc(this.width - diameter, 0, diameter, diameter, 270f, 90f);
// 右下角
path.AddArc(this.width - diameter, this.height - diameter, diameter, diameter, 0f, 90f);
// 左下角
path.AddArc(0, this.height - diameter, diameter, diameter, 90f, 90f);
g.FillPath(new SolidBrush(fillColor), path);
string strText = this._checked ? trueText : falseText;
if (_checked)
{
path = new GraphicsPath();
int sliderwidth = this.height - 4;
path.AddArc(this.width - sliderwidth - 2, 2, diameter, diameter, 180f, 90f);
path.AddArc(this.width - diameter - 2, 2, diameter, diameter, 270f, 90f);
path.AddArc(this.width - diameter - 2, this.height - diameter - 2, diameter, diameter, 0f, 90f);
path.AddArc(this.width - sliderwidth - 2, this.height - diameter - 2, diameter, diameter, 90f, 90f);
g.FillPath(new SolidBrush(sliderColor), path);
if (string.IsNullOrEmpty(strText))
{
//绘制小圆形
g.DrawEllipse(new Pen(sliderColor, 2), new RectangleF(circlePosition, (this.height - circleDiameter) * 0.5f, circleDiameter, circleDiameter));
}
else
{
//绘制文本
Rectangle rec = new Rectangle(0, 0, this.width - sliderwidth - 2, this.height);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
g.DrawString(strText, Font, new SolidBrush(sliderColor), rec, sf);
}
}
else
{
path = new GraphicsPath();
int sliderwidth = this.height - 4;
// 左上方
path.AddArc(2, 2, diameter, diameter, 180f, 90f);
// 右上方
path.AddArc(sliderwidth - diameter + 2, 2, diameter, diameter, 270f, 90f);
// 右下方
path.AddArc(sliderwidth - diameter + 2, sliderwidth - diameter + 2, diameter, diameter, 0f, 90f);
// 左下方
path.AddArc(2, sliderwidth - diameter + 2, diameter, diameter, 90f, 90f);
g.FillPath(new SolidBrush(sliderColor), path);
if (string.IsNullOrEmpty(strText))
{
//绘制小圆形
g.DrawEllipse(new Pen(sliderColor, 2), new RectangleF(this.width - circlePosition - circleDiameter, (this.height - circleDiameter) * 0.5f, circleDiameter, circleDiameter));
}
else
{
//绘制文本
Rectangle rec = new Rectangle(sliderwidth + 2, 0, this.width - sliderwidth - 2, this.height);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
g.DrawString(strText, Font, new SolidBrush(sliderColor), rec, sf);
}
}
}
//椭圆样式绘制
else if (this.switchtype == SwitchType.Ellipse)
{
//确定填充色
Color fillColor = this._checked ? trueColor : falseColor;
//画带圆弧的矩形
GraphicsPath path = new GraphicsPath();
path.AddArc(1, 1, this.height - 2, this.height - 2, 90f, 180f);
path.AddArc(this.width - (this.height - 2) - 1, 1, this.height - 2, this.height - 2, 270f, 180f);
g.FillPath(new SolidBrush(fillColor), path);
string strText = this._checked ? trueText : falseText;
if (_checked)
{
int circlewidth = this.height - 6;
g.FillEllipse(new SolidBrush(sliderColor), new Rectangle(this.width - circlewidth - 3, 3, circlewidth, circlewidth));
if (string.IsNullOrEmpty(strText))
{
//绘制小圆形
g.DrawEllipse(new Pen(sliderColor, 2), new RectangleF(circlePosition, (this.height - circleDiameter) * 0.5f, circleDiameter, circleDiameter));
}
else
{
//绘制文本
Rectangle rec = new Rectangle(0, 0, this.width - circlewidth - 3, this.height);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
g.DrawString(strText, Font, new SolidBrush(sliderColor), rec, sf);
}
}
else
{
int circlewidth = this.height - 6;
g.FillEllipse(new SolidBrush(sliderColor), new Rectangle(3, 3, circlewidth, circlewidth));
if (string.IsNullOrEmpty(strText))
{
//绘制小圆形
g.DrawEllipse(new Pen(sliderColor, 2), new RectangleF(this.width - circlePosition - circleDiameter, (this.height - circleDiameter) * 0.5f, circleDiameter, circleDiameter));
}
else
{
//绘制文本
Rectangle rec = new Rectangle(circlewidth + 3, 0, this.width - circlewidth - 3, this.height);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
g.DrawString(strText, Font, new SolidBrush(sliderColor), rec, sf);
}
}
}
}
#endregion
}
}
//确定填充色
Color fillColor = this._checked ? trueColor : falseColor;
//画带圆弧的矩形
GraphicsPath path = new GraphicsPath();
int diameter = 5;
// 左上角 起始从0,0. 外接矩形的长度与宽度。开始从180旋转90度
path.AddArc(0, 0, diameter, diameter, 180f, 90f);
// 右上角
path.AddArc(this.width - diameter, 0, diameter, diameter, 270f, 90f);
// 右下角
path.AddArc(this.width - diameter, this.height - diameter, diameter, diameter, 0f, 90f);
// 左下角
path.AddArc(0, this.height - diameter, diameter, diameter, 90f, 90f);
g.FillPath(new SolidBrush(fillColor), path);
path.AddArc 参数说明(我自己理解的是外接矩形的左上角作为起始点)。首先坐标轴方向是从x轴正半轴开始,顺时针旋转的。所以绘制图像是从180度开始到270度,旋转了90度的那段弧线,这里我用绿色的方框进行了勾勒。绘制右上角的图像。借用下网上的图片
待更新。
如有问题,欢迎讨论留言