您的位置:首页 > 其它

在ComboBox上实现水印效果(WinForms)

2009-08-20 12:30 169 查看
上一篇文章中,我简要说明了如何在TextBox里实现水印效果。把同样的实现方法搬到ComboBox中不对了,虽然代码运行没有出现错误,但却达不到我们在TextBox上的应用效果,根本看不到水印。这是怎么回事呢?
与TextBox一样,ComboBox是对Windows的原生控件COMBOBOX的封装。通过使用Spy++查看ComboBox控件,不难发现其实ComboBox内还有一个窗口,而这个窗口才是真正用于编辑文字的,它是一个EDIT控件,ComboBox只是实现了下拉列表的功能。因此,要在ComboBox上实现水印的效果,必须要在它内部的EDIT原生控件上绘制,而不是在ComboBox上。由于内部的这个EDIT是Windows原生的,通过Control.Controls集合无法获取到它的,因此只能通过Windows API实现。
在Windows API中,EnumChildWindows这个函数可以通过回调的方式枚举指定窗口的所有子窗口,关于这个函数的使用在这里我就不详细说明了,有兴趣的可以参考MSDN里的相关说明。因为原生的COMBOBOX中只有一个子控件,因此要获取内部的这个EDIT并不困难。具体实现见以下代码,摘自我的提供的WatermarkComboBox类的源码。

/// <summary>
/// 获取内部EDIT的句柄。
/// </summary>
private void RetreiveEditControl()
{
IntPtr handle = new IntPtr();

EnumChildWindows(this.Handle, GetChildCallback, ref handle);

this._editHandle = handle;
}

/// <summary>
/// EnumChildWindows的回调函数。
/// </summary>
private bool GetChildCallback(IntPtr hWnd, ref IntPtr lParam)
{
// 因为原生COMBOBOX只有一个子控件,因此不用作任何判断直接返回。
lParam = hWnd;
return false;
}

从以上代码可以看出,_editHandle就是内部EDIT控件的句柄,这样,与TextBox水印的的绘制代码相比,只要做两个修改就可以了。
第一个修改的地方是获取绘制区域的Rectangle,因为EDIT不是.net控件,因此只能使用API函数GetClientRect,而不能直接使用this.ClientRectangle属性。
第二个修改的地方是Graphics的获取,改为使用Graphics.FromHwnd方法。
修改之后的代码如下:

Brush brush = SystemBrushes.GrayText;
Font font = this.Font;
RECT rect = new RECT();

// 通过API获取EDIT的客户区域大小。
GetClientRect(_editHandle, ref rect);

StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Near;
stringFormat.LineAlignment = StringAlignment.Center;

// Graphics从EDIT的句柄获取。
using (Graphics g = Graphics.FromHwnd(_editHandle))
{
g.DrawString(_watermark, font, brush, rect.ToRectangle(), stringFormat);
}

// 释放非托管资源。
stringFormat.Dispose();

这样,在ComboBox上绘制水印的功能就完成了,不过还存在两个BUG。
1 在窗体出现时水印不会立刻显示,只有鼠标在上面移过以后才会显示。
2 水印的闪烁比较明显,特别是在启用了视觉主题以后。
以上的2个问题,我目前还不清楚是什么原因造成的,估计是和消息有关系。因为在WndProc方法中所处理的消息都是发给这个控件的,而EDIT并不等于ComboBox本身,因此可能会造成不正常的行为。要解决这个问题,看来只能从其它方面入手,先卖个关子,稍后的文章中我会提到这个问题的解决办法。

效果图如下:



未输入任何内容



输入了用户名之后

本文的演示程序和源代码请点击这里下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: