实例说明
在填表单的时候,每一个空缺项都是以下划线的形式出现,此时如果在空缺的位置放置一个TextBox控件,看起来既不美观也不符合常理。本实例可以很好的解决此问题,实例运行结果如图:

技术要点
本实例主要用到自定义类CustomTextBoxGroup,该类的功能是用来继承TextBox控件,并拓展原有控件的一些功能。在其构造方法中,定义控件的外观和位置。在重写Windows处理消息的函数中,捕获处理TextBox控件编辑颜色和绘制的消息,然后用自定义方法DrawBottomLines 绘制TextBox控件的下划线。本实例在实现绘制TextBox控件下划线的过程中,需要获取整个窗口的设备场景,因此用到Windows提供的API函数GetWindowDC和用于释放设备场景的API函数ReleaseDC,下面介绍本实例中用到的技术。
(1)CustomTextBoxGroup方法
该方法为类CustomTextBoxGroup的构造方法。其语法格式如下:
public CustomTextBoxGroup()
(2)WndProc方法
该方法用来捕获处理TextBox控件编辑颜色和绘制的消息。其语法格式如下:
protected override void WndProc(ref Message m)
{
base.WndProc(ref m); //处理消息
}
(3)DrawBottomLines方法
该方法用来绘制TextBox控件的底端横线。其语法格式如下:
public void DrawBottomLines(Graphics g)
参数说明如下。
g:表示GDI+绘图图面对象。
返回值:该方法无返回值。
(4)GetWindowDC方法
该方法用来获取当前窗口的设备场景。其语法格式如下:
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
参数说明如下。
hWnd:表示要释放的设备场景相关的窗口句柄。
hDC:表示要释放的设备场景句柄。
返回值:执行成功返回1,否则,返回0。
注意:在本实例中用到Windows提供的API函数,因此用到System.Runtime.InteropServices命名空间。在进行TextBox控件的绘制操作时需要引用System.Drawing命名空间。
实现过程
(1)创建一个项目,将其命名为VisionUnderLine,修改默认窗体为VisionUnderLine。
(2)主要程序代码。
在窗体加载时,直接调用自定义类中的内容声明一个TextBox控件goal,并指定自定义控件goal的父容器为当前窗体,然后将控件添加至当前窗体。代码如下:
private void VisionUnderLine_Load(object sender,EventArgs e)
{
System.Windows.Forms.TextBox goal = new CustomTextBoxGroup(); //定义一个TextBox对象goal
goal.Parent = this; //获取或设置自定义TextBox控件的父容器
this.Controls.Add(goal); //向窗体中添加自定义TextBox控件goal
}
在自定义类CustomTextBoxGroup的构造方法中,首先定义TextBox控件的位置和大小。代码如下:
public CustomTextBoxGroup()
{
this.Width = 180; //设置控件的宽度
this.Height = 100; //设置控件的高度
this.BorderStyle = BorderStyle.None; //设置控件为无边框状态
this.Top = 60; //设置控件上边缘与其容器工作区上边缘之间的距离
this.Left = 100; //设置控件左边缘与其容器工作区左边缘之间的距离
}
在该类的内部需要对用到的一些变量进行声明。代码如下:
public const int WM_PAINT = 0x000F; //该变量标识绘制TextBox控件
public const int WM_CTLCOLOREDIT = 0x0133; //该变量表示开始编辑TextBox控件的颜色
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);// 获取整个窗口的设备场景
[DllImport("user32.dll")]
// 释放由调用GetDC或GetWindowDC函数获取的指定设备场景,它对类或私有设备场景无效
public static extern int ReleaseDC(IntPtr hWnd,IntPtr hDC);
重载Windows处理消息的函数WndProc,对相应的消息捕获后进行处理。代码如下:
protected override void WndProc(ref Message m)
{
base.WndProc(ref m); //处理消息
switch(m.Msg) //截获有关TextBox控件的绘制信息
{
case WM_CTLCOLOREDIT: //当开始编辑TextBox控件的颜色时
goto case WM_PAINT; //跳转到TextBox控件的绘制
case WM_PAINT: //当开始绘制TextBox控件时
IntPtr hDC = GetWindowDC(this.Handle); //获取当前窗口的设备场景
if(hDC.ToInt32() != 0) //当此场景存在时
{
using(Graphics g = Graphics.FromHdc(hDC)) //声明一个GDI+绘图图面类对象g所占用的资源
{
DrawBottomLines(g); //绘制TextBox控件的底端横线
g.Dispose(); //释放GDI+绘图图面类对象g所占用的资源
}
}
m.Result = IntPtr.Zero; //指定在当前条件下的返回值
ReleaseDC(m.HWnd,hDC); //释放指定设备的场景
break;
}
}
当捕获绘制TextBox控件的消息后,需要绘制它的底端边框,此时用到DrawBottomLines方法。代码如下:
public void DrawBottomLines(Graphics g)
{
Pen p = new Pen(this.BackColor,2); //定义一个用于绘制直线和曲线的对象并设定它的颜色和宽度
g.DrawRectangle(p,1,1,this.Width - 2,this.Height - 2); //绘制由坐标对、宽度和高度指定的矩形
p = new Pen(Color.FromArgb(0,0,0),1); //定义一个用于绘制直线和曲线的对象并设定它的颜色和宽度
g.DrawLine(p,0,this.Height - 1,this.Width,this.Height - 1); //绘制TextBox的底端横线
p.Dispose(); //释放画笔p所占用的资源
}