zoukankan      html  css  js  c++  java
  • 在ComboBox上实现水印效果(WinForms)

    上一篇文章中,我简要说明了如何在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本身,因此可能会造成不正常的行为。要解决这个问题,看来只能从其它方面入手,先卖个关子,稍后的文章中我会提到这个问题的解决办法。

    效果图如下:

    未输入任何内容


    输入了用户名之后

    本文的演示程序和源代码请点击这里下载
  • 相关阅读:
    数据库系列之T-SQL(系统内置函数)
    数据库系列之T-SQL(存储过程)
    数据库系列之T-SQL(作业与维护计划)
    数据库系列之T-SQL(触发器)
    数据库系列之T-SQL(事务)
    数据库系列之T-SQL(基础)
    数据库系列之查询(5)
    Nginx + Apache 公用80端口的配置
    客户端putty, xshell连接linux中vim的小键盘问题
    配置EPEL YUM源
  • 原文地址:https://www.cnblogs.com/effun/p/1550230.html
Copyright © 2011-2022 走看看