学用C#编写组件
软件世界
Visual Studio.NET已经推出好长时间了,它被认为是白领程序员的烈性蓝色染剂,真害怕有一天我家门口修鞋的老伯也会来跟我抢饭碗。不过,没办法,世界发展就是这么快。好,振作精神,经过我几天的努力终于掌握了使用C#开发组件的基本方法。下面我将详细介绍如何使用C#开发组件并且演示如何在程序中调用该组件。
一、编写组件
组件简介
本文所编写的组件名称是FlatButton。该组件的功能与普通按钮一样,只是它的外表与Windows XP按钮的风格相似,当鼠标移入时会高亮显示,当鼠标移出后会恢复正常,当鼠标按下时会有按下的效果,这些效果都是在代码中实现的。
1.打开“Visual Studio”,点击“File→New→Project”,在左边选择“C# Project”,右边的Templates框中选择“Windows Control Library”模板,然后给该Project起个名字“MyControls”,点击OK(见(图1))。

2.在Visual Studio打开这个Project后,请先将右边的Solution Explorer中的“UserControl”删除,因为“User Control”的内容不适合本文的组件开发(见(图2))。

3.点击“Project”菜单,“Project→Add User Control...”,然后在Templates框中选择“Custom Control”模板并且将该组件命名为“FlatButton”(见(图3))。

4.此时.NET已经搭好了一个组件的基本类框架,右键点击右边Solution Explorer中的“FlatButton.cs”并且选择“View Code”可以查看代码,从代码中可以看出:
(1)它属于命名空间MyControls;
(2)该组件从System.Windows.Forms.Control继承下来;
(3)自动重载了OnPaint函数。
5.首先给FlatButton创建一个状态变量,如下:
private enum ButtonStates
{bsUp,
bsDisabled,
bsDown,
bsExclusive}
private ButtonStates ButtonState;
6.为了截获鼠标移入,移出,按下,弹起等事件,我重新定义了这几个鼠标事件,在各种鼠标事件中改变按钮的状态,并且重绘组件。
private void DoMouseEnter(object sender, EventArgs e)
{ButtonState = ButtonStates.bsUp;
Refresh(); }
private void DoMouseLeave(object sender, EventArgs e)
{ButtonState = ButtonStates.bsExclusive;
Refresh(); }
private void DoMouseUp(object sender, MouseEventArgs e)
{ButtonState = ButtonStates.bsUp;
Refresh(); }
private void DoMouseDown(object sender, MouseEventArgs e)
{ButtonState = ButtonStates.bsDown;
Refresh(); }
然后在构造函数中将这几个事件替换成FlatButton相应的事件,代码如下:
public FlatButton()
{this.MouseEnter += new System.EventHandler(this.DoMouseEnter);
this.MouseLeave += new System.EventHandler(this.DoMouseLeave);
this.MouseUp += new MouseEventHandler(this.DoMouseUp);
this.MouseDown += new MouseEventHandler(this.DoMouseDown);
ButtonState = ButtonStates.bsExclusive;}
其中this.MouseXXX是继承与FlatButton父的事件句柄。
7.至此我们已经完成了按钮各种框架的搭建下面就要实现各种状态下按钮的绘制。
在C#中作图使用Graphics类,获得Graphics对象的方法是通过Control的OnPaint函数来实现。OnPaint的参数是一个PaintEventArgs类型的类,它有两个公共属性:ClipRectangle和Graphics,其中ClipRectangle指定了绘制的区域,它是一个只读的属性。
首先在OnPaint中调用各种绘制函数:
protected override void OnPaint(PaintEventArgs pe)
{// TODO: Add custom paint code here
Graphics g = pe.Graphics;
DoDrawButtonBorder(g);
DoDrawButtonFoucs(g);
DoDrawButtonFace(g);
DoDrawButtonText(g);
// Calling the base class OnPaint
base.OnPaint(pe);
}
8.下面将实现各种绘制按钮的函数,首先定义一些颜色私有变量并且初始化。
// 边框颜色
//无效时的边框颜色
private Color DisabledBorderColor = Color.Gray;
//其他状态下的边框颜色
private Color ExclusiveBorderColor = Color.Blue;
//焦点边框颜色
//按钮弹起时焦点边框颜色
private Color UpFoucsColor = Color.Yellow;
//按钮无效时焦点边框颜色
private Color DisabledFoucsColor = Color.White;
//按钮按下时焦点边框颜色
private Color DownFoucsColor=Color.Gray;
//其他状态下焦点边框颜色
private Color ExclusiveFoucsColor = Color.White;
//按钮Face颜色
//按钮弹起时Face颜色
private Color DisabledFaceColor = Color.Gray;
//其他状态下Face颜色
private Color ExclusiveFaceColor = Color.White;
然后定义三个公共属性,这三个属性将会显示在属性面板上。
//边框颜色
public Color BorderColor
{ get
{ return ExclusiveBorderColor; }
set
{ ExclusiveBorderColor = value;
Refresh();}}
//焦点边框颜色
public Color FoucsColor
{ get
{return UpFoucsColor; }
set{ UpFoucsColor = value;
Refresh();}}
//Face颜色
public Color FaceColor
{get
{return ExclusiveFaceColor; }
set
{ ExclusiveFaceColor = value;
Refresh();}}
9.制定完绘制时需要的颜色后,下面将实现各种绘制的函数了。
//绘制边框
private void DoDrawButtonBorder(Graphics g)
{Pen BorderPen;
//初始化BorderPen
BorderPen = new Pen(ExclusiveBorderColor, 1);
//根据目前的按钮状态设置BorderPen
switch (ButtonState)
{case ButtonStates.bsUp:
case ButtonStates.bsExclusive:
case ButtonStates.bsDown:
BorderPen = new Pen(ExclusiveBorderColor, 1);
break;
case ButtonStates.bsDisabled:
BorderPen = new Pen(DisabledBorderColor, 1);
break;}
//绘制
g.DrawRectangle(BorderPen, 0, 0, this.Width - 1, this.Height - 1);}
//绘制焦点边框
private void DoDrawButtonFoucs(Graphics g)
{Pen FoucsPen;
//初始化FoucsPen
FoucsPen = new Pen(UpFoucsColor, 2);
//根据目前的按钮状态设置FoucsPen
switch (ButtonState)
{case ButtonStates.bsUp:
FoucsPen = new Pen(UpFoucsColor, 2);
break;
case ButtonStates.bsDisabled:
FoucsPen = new Pen(DisabledFoucsColor, 2);
break;
case ButtonStates.bsDown:
FoucsPen = new Pen(DownFoucsColor, 2);
break;
case ButtonStates.bsExclusive:
FoucsPen = new Pen(ExclusiveFoucsColor, 2);
break;}
//绘制
g.DrawRectangle(FoucsPen, 2, 2, this.Width - 4, this.Height - 4);}
//绘制Face
private void DoDrawButtonFace(Graphics g)
{SolidBrush FaceBrush;
//初始化SolidBrush
FaceBrush = new SolidBrush(ExclusiveFaceColor);
//根据目前的按钮状态设置SolidBrush
switch (ButtonState)
{case ButtonStates.bsUp:
case ButtonStates.bsExclusive:
case ButtonStates.bsDown:
FaceBrush = new SolidBrush(ExclusiveFaceColor);
break;
case ButtonStates.bsDisabled:
FaceBrush = new SolidBrush(DisabledFaceColor);
break; }
//绘制
g.FillRectangle(FaceBrush, 3, 3, this.Width - 6, this.Height - 6);
}
//绘制文字
private void DoDrawButtonText(Graphics g)
{ //设置文字的颜色
SolidBrush blackBrush = new SolidBrush(Color.Black);
//设置文字的对齐方式
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
//绘制
g.DrawString(this.Text, this.Font, blackBrush, this.ClientRectangle, format); }
10.至此完成了当鼠标移入、移出、按下和弹起时的各种状态下按钮的绘制,但是还缺少按钮无效时的绘制。通过查看FlatButton父类Control的帮助,发现可以重载OnEnabledChanged来捕获Enabled变化时的事件,于是加入以下代码:
protected override void OnEnabledChanged(EventArgs e)
{if (Enabled == false)
{ ButtonState = ButtonStates.bsDisabled; }
else
{ ButtonState = ButtonStates.bsExclusive;}
Refresh();
base.OnEnabledChanged(e); }
注意:必须在后面加入base.OnEnabledChanged(e);否则FlatButton的Enabled=false将会不起作用。该语句的作用就是调用父类的OnEnabledChanged函数。
11.至此所有的代码均已实现,在本例中覆盖了以下几个知识点:类的继承、函数的重载、事件、属性的公布和图形绘制。
总结如下:
(1)一般,所有的可视化组件都可以从System.Windows.Forms.Control继承下来。
(2)可以通过override重载父类的函数,并且可以使用base.XXX调用父类的函数。
(3)可以自定义各种事件的处理函数,以MouseEnter为例:
首先编写一个与MouseEnter事件具有相同参数的函数DoMouseEnter
private void DoMouseEnter(object sender, EventArgs e) {}
然后在构造函数中将MouseEnter事件指向DoMouseEnter
this.MouseEnter += new System.EventHandler(this.DoMouseEnter);
①建立公共的属性可以将它发布在属性面板上。
②图形绘制一般通过重载OnPaint实现,它的参数制定了绘制的区域和绘制的Graphics类,通过调用Refresh可以触发OnPaint。
二、 组件的调用
1.编译
点击菜单“Build→Configuration Manager”,将当前编译方式更改为Release,如(图4):

点击菜单“Build→Build Solution”编译。
2.安装
右键点击左侧的Toolbox,点击Customize Toolbx..,如(图5):

选择“.Net FrameWork Components”,点击“Browse”,选择刚才编译好的MyControls.dll,如(图6):

此时FlatButton将会出现在Toolbox上。
3.调用
将FlatButton拖入Form中,在属性面板上更改BorderColor、FaceColor和FouceColor的颜色值即可。
三、总结
通过上面所说的,大家可以很容易地在C#下编写自己的组件了,.Net已经将几乎所有的事件和消息封装起来,我们只要从Control继承,然后重载它的函数或者重新指定事件函数就可以了。
上面的代码是我从自己的Delphi组件代码中更改而来。大家也可以将自己以前编写组件移植到C#上。