今天介绍WinForm控件开发和美化系列的第一个控件Panel控件。这个控件本身比较简单,主要就是用到了一些GDI+的知识。作为这个系列的第一个控件,在这里也介绍一些控件开发和GDI+的内容。
在看过这篇系列文章后,如果您有什么好的意见和建议,请给我留言。
一、效果图
二、实现代码
namespace Lt_WinControls
{
[DefaultProperty("Visible"), DefaultEvent("Click")]
[ToolboxItem(true)]
[ToolboxBitmap(typeof(PanelEx), "Resources.PanelEx.bmp")]
[Description("表示一个可以自定义边框样式的Panel控件。")]
//[Designer(typeof(PanelExDesigner))]
//[Docking(DockingBehavior.Ask)]
public class PanelEx : Panel
{
#region 属性
bool _topLeft = true;
[DefaultValue(true)]
[Description("当CornerMode为Round时,是否显示左上角"), Category("自定义外观")]
public bool TopLeft
{
get { return _topLeft; }
set
{
_topLeft = value;
base.Invalidate();
}
}
bool _topRight = true;
[DefaultValue(true)]
[Description("当CornerMode为Round时,是否显示右上角"), Category("自定义外观")]
public bool TopRight
{
get { return _topRight; }
set
{
_topRight = value;
base.Invalidate();
}
}
bool _bottomLeft = true;
[DefaultValue(true)]
[Description("当CornerMode为Round时,是否显示左下角"), Category("自定义外观")]
public bool BottomLeft
{
get { return _bottomLeft; }
set
{
_bottomLeft = value;
base.Invalidate();
}
}
bool _bottomRight = true;
[DefaultValue(true)]
[Description("当CornerMode为Round时,是否显示右下角"), Category("自定义外观")]
public bool BottomRight
{
get { return _bottomRight; }
set
{
_bottomRight = value;
base.Invalidate();
}
}
private Color _borderColor = Color.Black;
[DefaultValue(typeof(Color), "Black")]
[Description("设置边框线的颜色"), Category("自定义外观")]
public Color BorderColor
{
get { return _borderColor; }
set
{
_borderColor = value;
base.Invalidate();
}
}
private GradientMode _gradientMode = GradientMode.None;
[DefaultValue(typeof(GradientMode), "0")]
[Description("用渐变色来填充背景时的填充方式"), Category("自定义外观")]
public GradientMode GradientMode
{
get { return _gradientMode; }
set
{
_gradientMode = value;
base.Invalidate();
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override Color BackColor
{
get
{
return Color.Transparent;
}
}
private Color _backColor1 = Color.White;
[DefaultValue(typeof(Color), "White")]
[Description("控件的背景色1(若同时设置两个背景色,就是会用渐变色来填充背景)"), Category("自定义外观")]
public Color BackColor1
{
get { return _backColor1; }
set
{
_backColor1 = value;
base.Invalidate();
}
}
private Color _backColor2 = Color.White;
[DefaultValue(typeof(Color), "White")]
[Description("控件的背景色2,当GradientMode不为None时有效"), Category("自定义外观")]
public Color BackColor2
{
get { return _backColor2; }
set
{
_backColor2 = value;
base.Invalidate();
}
}
private int _borderWidth = 1;
[DefaultValue(1)]
[Description("设置边框线的宽度"), Category("自定义外观")]
public int BorderWidth
{
get { return _borderWidth; }
set
{
if (value >= 0)
{
_borderWidth = value;
SetPadding();
base.Invalidate();
}
}
}
private DashStyle _borderStyle = DashStyle.Solid;
[DefaultValue(typeof(DashStyle), "0")]
[Description("设置边框线样式"), Category("自定义外观")]
public new DashStyle BorderStyle
{
get { return _borderStyle; }
set
{
_borderStyle = value;
base.Invalidate();
}
}
private int _roundSize = 15;
/// <summary>
/// 设置边角大小
/// </summary>
[DefaultValue(15)]
[Description("当CornerMode为Round时,设置边角大小"), Category("自定义外观")]
public int RoundSize
{
get { return _roundSize; }
set
{
if (value > 0)
{
_roundSize = value;
base.Invalidate();
}
}
}
private CornerMode _cornerMode = CornerMode.Corner;
/// <summary>
/// 设置边角样式
/// </summary>
[DefaultValue(typeof(CornerMode), "2")]
[Description("设置边角样式"), Category("自定义外观")]
public CornerMode CornerMode
{
get { return _cornerMode; }
set
{
_cornerMode = value;
SetPadding();
base.Invalidate();
}
}
private bool _leftBorder = true;
/// <summary>
/// 左边框是否显示
/// </summary>
[DefaultValue(true)]
[Description("当CornerMode为Corner时,是否显示左边框"), Category("自定义外观")]
public bool LeftBorder
{
get { return _leftBorder; }
set
{
_leftBorder = value;
base.Invalidate();
}
}
private bool _rightBorder = true;
/// <summary>
/// 是否显示右边框
/// </summary>
[DefaultValue(true)]
[Description("当CornerMode为Corner时,是否显示右边框"), Category("自定义外观")]
public bool RightBorder
{
get { return _rightBorder; }
set
{
_rightBorder = value;
base.Invalidate();
}
}
private bool _topBorder = true;
/// <summary>
/// 是否显示上边框
/// </summary>
[DefaultValue(true)]
[Description("当CornerMode为Corner时,是否显示上边框"), Category("自定义外观")]
public bool TopBorder
{
get { return _topBorder; }
set
{
_topBorder = value;
base.Invalidate();
}
}
private bool _bottomBorder = true;
/// <summary>
/// 是否显示下边框
/// </summary>
[DefaultValue(true)]
[Description("当CornerMode为Corner时,是否显示下边框"), Category("自定义外观")]
public bool BottomBorder
{
get { return _bottomBorder; }
set
{
_bottomBorder = value;
base.Invalidate();
}
}
#endregion
#region 构造函数
/// <summary>
/// 构造函数
/// </summary>
public PanelEx()
{
base.ResizeRedraw = true;
this.BackColor = Color.Transparent;
}
#endregion
private void SetPadding()
{
int left = this.Padding.Left;
int right = this.Padding.Right;
int top = this.Padding.Top;
int bottom = this.Padding.Bottom;
if (this.CornerMode == CornerMode.None)
{
left = 0;
right = 0;
top = 0;
bottom = 0;
}
else
{
if (this.Padding.Left < this.BorderWidth)
{
left = this.BorderWidth;
}
if (this.Padding.Right < this.BorderWidth)
{
right = this.BorderWidth;
}
if (this.Padding.Top < this.BorderWidth)
{
top = this.BorderWidth;
}
if (this.Padding.Bottom < this.BorderWidth)
{
bottom = this.BorderWidth;
}
}
this.Padding = new Padding(left, top, right, bottom);
}
#region 控件重绘事件
/// <summary>
/// 控件重绘事件
/// </summary>
/// <param name="e"></param>
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
Rectangle rec = new Rectangle(0, 0, this.Width, this.Height);
Pen pen = new Pen(BorderColor, BorderWidth);
pen.DashStyle = BorderStyle;
GraphicsPath path = CreateRound(rec, RoundSize, this.TopLeft, this.TopRight, this.BottomLeft, this.BottomRight);
//绘制背景
if (this.GradientMode == GradientMode.None)
{
using (Brush brush2 = new SolidBrush(this.BackColor1))
{
if (this.CornerMode == CornerMode.Round)
{
g.FillPath(brush2, path);
}
else
{
g.FillRectangle(brush2, rec);
}
}
}
else
{
if (this.CornerMode == CornerMode.Round)
{
DrawGradient(g, rec, path, this.BackColor1, this.BackColor2, this._gradientMode);
}
else
{
DrawGradient(g, rec, this.BackColor1, this.BackColor2, this._gradientMode);
}
}
//绘制边框
if (BorderWidth > 0)
{
switch (CornerMode)
{
case CornerMode.None:
break;
case CornerMode.Corner:
if (LeftBorder)
{
g.DrawLine(pen, 0, 0, 0, this.Height - 1);
}
if (BottomBorder)
{
g.DrawLine(pen, 0, this.Height - 1, this.Width - 1, this.Height - 1);
}
if (RightBorder)
{
g.DrawLine(pen, Width - 1, this.Height - 1, this.Width - 1, 0);
}
if (TopBorder)
{
g.DrawLine(pen, this.Width - 1, 0, 0, 0);
}
break;
case CornerMode.Round:
g.DrawPath(pen, path);
break;
}
}
}
#endregion
public static GraphicsPath CreateRound(Rectangle rect, int radius, bool topLeft, bool topRight, bool bottomLeft, bool bottomRight)
{
rect.Width -= 1;
rect.Height -= 1;
GraphicsPath roundRect = new GraphicsPath();
//顶端
roundRect.AddLine(rect.Left + radius / 2, rect.Top, rect.Right - radius / 2, rect.Top);
//右上角
if (topRight)
{
roundRect.AddArc(rect.Right - radius, rect.Top, radius, radius, 270, 90);
}
else
{
roundRect.AddLine(rect.Right - radius / 2, rect.Top, rect.Right, rect.Top);
roundRect.AddLine(rect.Right, rect.Top, rect.Right, rect.Top + radius / 2);
}
//右边
roundRect.AddLine(rect.Right, rect.Top + radius / 2, rect.Right, rect.Bottom - radius / 2);
//右下角
if (bottomRight)
{
roundRect.AddArc(rect.Right - radius, rect.Bottom - radius, radius, radius, 0, 90);
}
else
{
roundRect.AddLine(rect.Right, rect.Bottom - radius / 2, rect.Right, rect.Bottom);
roundRect.AddLine(rect.Right, rect.Bottom, rect.Right - radius / 2, rect.Bottom);
}
//底边
roundRect.AddLine(rect.Right - radius / 2, rect.Bottom, rect.Left + radius / 2, rect.Bottom);
//左下角
if (bottomLeft)
{
roundRect.AddArc(rect.Left, rect.Bottom - radius, radius, radius, 90, 90);
}
else
{
roundRect.AddLine(rect.Left + radius / 2, rect.Bottom, rect.Left, rect.Bottom);
roundRect.AddLine(rect.Left, rect.Bottom, rect.Left, rect.Bottom - radius / 2);
}
//左边 不需要了
// roundRect.AddLine(rect.Left, rect.Top + radius / 2, rect.Left, rect.Bottom - radius / 2);
//左上角
if (topLeft)
{
roundRect.AddArc(rect.Left, rect.Top, radius, radius, 180, 90);
}
else
{
roundRect.AddLine(rect.Left, rect.Top + radius / 2, rect.Left, rect.Top);
roundRect.AddLine(rect.Left, rect.Top, rect.Left + radius / 2, rect.Top);
}
//闭合图像
roundRect.CloseAllFigures();
return roundRect;
}
public static void DrawGradient(Graphics e, Rectangle rect, GraphicsPath path, Color color1, Color color2, GradientMode mode)
{
LinearGradientMode backwardDiagonal = LinearGradientMode.BackwardDiagonal;
if (mode == GradientMode.Vertical)
{
backwardDiagonal = LinearGradientMode.Vertical;
}
else if (mode == GradientMode.Horizontal)
{
backwardDiagonal = LinearGradientMode.Horizontal;
}
else if (mode == GradientMode.BackwardDiagonal)
{
backwardDiagonal = LinearGradientMode.BackwardDiagonal;
}
else if (mode == GradientMode.ForwardDiagonal)
{
backwardDiagonal = LinearGradientMode.ForwardDiagonal;
}
LinearGradientBrush brush = new LinearGradientBrush(rect, color1, color2, backwardDiagonal);
e.FillPath(brush, path);
brush.Dispose();
}
public static void DrawGradient(Graphics e, Rectangle rect, Color color1, Color color2, GradientMode mode)
{
LinearGradientMode backwardDiagonal = LinearGradientMode.BackwardDiagonal;
if (mode == GradientMode.Vertical)
{
backwardDiagonal = LinearGradientMode.Vertical;
}
else if (mode == GradientMode.Horizontal)
{
backwardDiagonal = LinearGradientMode.Horizontal;
}
else if (mode == GradientMode.BackwardDiagonal)
{
backwardDiagonal = LinearGradientMode.BackwardDiagonal;
}
else if (mode == GradientMode.ForwardDiagonal)
{
backwardDiagonal = LinearGradientMode.ForwardDiagonal;
}
LinearGradientBrush brush = new LinearGradientBrush(rect, color1, color2, backwardDiagonal);
e.FillRectangle(brush, rect);
brush.Dispose();
}
}
public enum GradientMode
{
/// <summary>
/// 无填充样式
/// </summary>
None = 0,
/// <summary>
/// 垂直填充
/// </summary>
Vertical = 1,
/// <summary>
/// 水平填充
/// </summary>
Horizontal = 2,
/// <summary>
/// 右上角到左下角
/// </summary>
BackwardDiagonal = 3,
/// <summary>
/// 左上角到右下角
/// </summary>
ForwardDiagonal = 4
}
public enum CornerMode
{
/// <summary>
/// 没有边框
/// </summary>
None = 0,
/// <summary>
/// 指定圆角
/// 此时的边框设置以上边框为准
/// </summary>
Round = 1,
/// <summary>
/// 指定尖角
/// </summary>
Corner = 2,
}
}
三、控件设计知识介绍
1、类的属性集
定义控件的默认属性和默认事件:
设置默认属性后,在设计时选中该控件的话,属性栏中的默认属性会变成灰色;
设置默认事件后,在设计时双击该控件会自动生成默认的事件代码,比如双击这个控件后会生成Click事件的代码
//[DefaultProperty("Visible"), DefaultEvent("Click")]
下面这个属性设置这个控件是否在控件工具箱中显示
[ToolboxItem(true)]
下面这个属性设置这个控件显示在工具箱中的图标,可以设置成自己定义的图标
[ToolboxBitmap(typeof(PanelEx), "Resources.PanelEx.bmp")]
下面的属性设置在工具箱中显示的控件的提示内容
[Description("表示一个可以自定义边框样式的Panel控件。")]
下面的属性设置控件的设计类,就是在设置时显示在控件右上角的小三角里面的内容,panle控件设置了设计类比较奇怪,无法当做容器来使用。
所以在这里就不介绍控件的设计类了,这部分放到后面的控件来介绍
//[Designer(typeof(PanelExDesigner))]
下面是属性设置控件默认的停靠属性,Ask表示有用户来设置停靠方式
//[Docking(DockingBehavior.Ask)]
public class PanelEx : Panel
{
}
2、属性的属性集
即使设置了默认值,在声明字段的时候或构造函数里面还是要对字段进行赋值,不然默认值的设置就没有意义了
private Color _backColor1 = Color.White;
设置属性的默认值,这个地方给很多人造成了误解,认为设置了默认属性值,在代码生成的时候就自动生成默认的值了,
其实不是这样,设置默认值有两个作用:
一是如果设置的值和默认值相同的话,在控件的属性列表中就不会加粗显示这个值;
而是如果设置的值和默认值相同的话,在控件的生成过程中不生成对该属性的赋值代码(这一点很关键)。
[DefaultValue(typeof(Color), "White")]
下面的属性设置这个属性在控件的属性窗口中的分类和提示信息
[Description("控件的背景色1(若同时设置两个背景色,就是会用渐变色来填充背景)"), Category("自定义外观")]
public Color BackColor1
{
get { return _backColor1; }
set
{
_backColor1 = value;
base.Invalidate();
}
}
由于在自定义控件中,已经设置了控件的背景色,并且我希望控件原来的背景色一直是透明的,所以用下面的方法来屏蔽控件本身的属性BackColor
这个属性设置控件的该属性不显示在在控件的属性窗口中
[Browsable(false)]
表示在控件生成的时候不生成这个属性的代码
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override Color BackColor
{
get
{
让属性的值一直返回透明色,这样你即使在客户端代码中设置了这个属性,也是无效的
return Color.Transparent;
}
}
在这里我只介绍这个项目中用到的一些内容,还有一些内容会在以后用到的时候在给大家介绍。
比如: 一些属性本身就是一个类,这个类本身也包含很多的属性,我们希望在代码生成的时候不生成这个类本身的代码,而是希望生成这个类的子属性的代码。
并且在属性窗口中看到的也是这个类的子属性,这些都会在后面的内容介绍。