接着上一篇文章讲,这次来说说C#自定义控件中的扩展控件(Extended Controls)的创建和使用。所谓的扩展控件就是在原有控件的基础上派生出新的控件,为了让大家更好的理解,这次就用C#中最基本的组件——Button来演示。最终的效果如下:
好了,下面来说说创建的步骤:
一. 创建一个Windows窗口控件库项目,命名为MyButton;
二.编写控件代码,代码如下:(请自行添加System.Reflection命名空间)
namespace MyButton
{
[DefaultEvent("Click"), ToolboxBitmap(typeof(MyButton),"Res.MyICO.ico")]
public partial class MyButton : Button
{
private State _buttonState = State.Normal;
//从资源文件载入图片
private Image _normalImg = Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"MyButton.Res.btn_normal.png"));
private Image _highLightImg = Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"MyButton.Res.btn_highlight.png"));
private Image _downImg = Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"MyButton.Res.btn_down.png"));
private Image _focusedImg = Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"MyButton.Res.btn_focus.png"));
private Image _disabledImg = Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(@"MyButton.Res.btn_disabled.png"));
#region Properties
[Category("自定义属性"),Description("控件的正常背景图片")]
public Image NormalImg
{
get
{
return _normalImg;
}
set
{
_normalImg = value;
BackgroundImage = value;
}
}
[Category("自定义属性"), Description("当鼠标进入控件的可见部分是的背景图片")]
public Image HighLightImg
{
get
{
return _highLightImg;
}
set
{
_highLightImg = value;
}
}
[Category("自定义属性"), Description("当控件被按下时的背景图片")]
public Image DownImg
{
get
{
return _downImg;
}
set
{
_downImg = value;
}
}
[Category("自定义属性"), Description("当控件拥有焦点时的背景图片")]
public Image FocusedImg
{
get
{
return _focusedImg;
}
set
{
_focusedImg = value;
}
}
[Category("自定义属性"), Description("控件禁止时的背景图片")]
public Image DisabledImg
{
get
{
return _disabledImg;
}
set
{
_disabledImg = value;
}
}
#endregion Properties
//===============================================================================================
// Constructor
public MyButton(): base()
{
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
}
#region OverrideMethods
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
_buttonState = State.Highlight;
}
protected override void OnMouseLeave(EventArgs e)
{
if (_buttonState == State.Highlight && Focused)
{
_buttonState = State.Focused;
}
else if (_buttonState == State.Focused)
{
_buttonState = State.Focused;
}
else
{
_buttonState = State.Normal;
}
base.OnMouseLeave(e);
}
protected override void OnMouseDown(MouseEventArgs mevent)
{
if (mevent.Button == MouseButtons.Left && mevent.Clicks == 1)
{
_buttonState = State.Down;
base.OnMouseDown(mevent);
}
}
protected override void OnMouseUp(MouseEventArgs mevent)
{
if (mevent.Button == MouseButtons.Left && mevent.Clicks == 1)
{
if (ClientRectangle.Contains(new Point(mevent.X, mevent.Y)))
{
_buttonState = State.Highlight;
}
else
{
_buttonState = State.Focused;
}
base.OnMouseUp(mevent);
}
}
// The event of OnLeave and OnEnter to make object lost focus or get focus
protected override void OnLeave(EventArgs e)
{
_buttonState = State.Normal;
base.OnLeave(e);
}
protected override void OnEnter(EventArgs e)
{
_buttonState = State.Focused;
base.OnEnter(e);
}
protected override void OnPaint(PaintEventArgs pevent)
{
base.OnPaint(pevent);
base.OnPaintBackground(pevent);
Graphics g = pevent.Graphics;
if (Enabled == false)
{
_buttonState = State.Disabled;
}
switch (_buttonState)
{
case State.Highlight:
g.DrawImage(_highLightImg, new Rectangle(0, 0, Width, Height));
break;
case State.Down:
g.DrawImage(_downImg, new Rectangle(0, 0, Width, Height));
break;
case State.Focused:
g.DrawImage(_focusedImg, new Rectangle(0, 0, Width, Height));
break;
case State.Disabled:
g.DrawImage(_disabledImg, new Rectangle(0, 0, Width, Height));
break;
default:
g.DrawImage(_normalImg, new Rectangle(0, 0, Width, Height));
break;
}
DrawText(g);
}
protected override void Dispose(bool disposing)
{
if (_normalImg != null || _highLightImg != null || _downImg != null || _focusedImg != null || _disabledImg != null)
{
_normalImg.Dispose();
_highLightImg.Dispose();
_downImg.Dispose();
_focusedImg.Dispose();
_disabledImg.Dispose();
}
base.Dispose(disposing);
}
#endregion OverrideMethods
#region PrivateMethods
private void DrawText(Graphics g)
{
int x = 0;
int y = 0;
Size s = g.MeasureString(Text, Font).ToSize();
x = (Width - s.Width) / 2;
y = (Height - s.Height) / 2;
g.DrawString(Text, Font, Brushes.Black, new Point(x, y));
}
#endregion PrivateMethods
#region Enum
internal enum State
{
Normal, //正常
Highlight, // 鼠标进入
Down, //鼠标按下
Focused, // 获得焦点
Disabled //控件禁止
}
#endregion Enum
}
}
三.删除有VS设计器自动生成的代码文件MyButton.Designer.cs。
四.此时按F5测试项目时会发现出现异常,因为这个时候把启动项设成了控件的项目,但是启动项必须是可运行的项目(包含Form窗体的),解决方法是找一个包含Form窗体的宿主项目,可以往解决方案中添加已有的项目,再把其设置为启动项,这里我在解决方案中创建了新的Windows窗体项目Test,并设置为启动项。此时按F5即可。
方案布局如下:
五.此时我们的自定义控件就生成了,是个dll文件,这里建议看下我的上一篇文章C#自定义控件简介(一),因为待会可能会用能。好了我们现在用宿主项目来测试下控件是否如设计的那样,把DLL控件加载到宿主项目的工具箱中,或者直接把dll拖放到工具箱中,但是当把控件添加到宿主项目的Form1中,你会发现有错误。这又是什么情况呢???容我慢慢的向你道来。
代码的前几行是从资源文件中导入我们需要的图片,但是在我们的解决方案中却找不到Res这个资源文件夹,所以问题就出现在这里,解决办法和简单,在解决方案中添加名为Res的文件夹,并把需要的图片文件放到文件夹中,有个最重要的步骤别忘了,就是把各个图片的属性栏的生成操作选项全部改成嵌入的资源,这样导入的图片才会被项目所使用。这一步也可以再写代码时同步完成。更改后项目布局如下:
现在自定义的控件可以像一般的控件进行拖放使用了。由于本篇文章的侧重点为如何创建和使用自定义控件,所以关于代码我就简单的说下:
重写基类的方法来对控件的各个状态进行判断与赋值,并根据状态的不同绘制不同的背景图片,设置控件不同状态时图片的属性,便于用户根据自己的喜好更换背景图片,代码中用到了许多自定义控件的的特性,如下表所示:
DefaultEven | 在设计界面时双击控件时产生的默认事件 |
ToolboxBitmap | 设置自定义控件在工具箱中显示的图片 |
Category | 设置在属性栏中属性所处的类别,如“外观”,“设计”,“行为”等 |
Description | 设置控件属性时,在属性窗口底端显示的该属性的描述 |
DefaultValue | 设置控件属性的默认值 |
以上只是实现了button控件的基本属性,起个抛砖引玉的作用,更好的设计还望读者自行编写扩展,如若有不足之处,还请各位批评指正。