none
请问如何用用c#实现透明窗体的移动 RRS feed

  • 问题

  • 我想用c#实现“金山词霸-背单词”那样的效果,采用了使TransparencyKey设置窗体背景色相同的办法,但窗体镂空后无法接收鼠标事件了,想调用GetAsyncKeyState()检测鼠标状态,再发送消息的办法,实现也不理想,请问有没有更好的方法。
    谢谢!
    2010年3月17日 11:45

答案

  • 真是谢谢周版主和BorisJineman兄 。这个问题自己想办法解决了。
    其实之前是想复杂了,使用TransparencyKey()后窗体是不能接收鼠标消息了。但可以用一个定时器检测鼠标位置,判断是否在窗体内,若在窗体内且鼠标左键按下,便发送HTCAPTION消息给窗体模拟鼠标拖动标题栏即可。
    google了一下,也有这种说法:
    首先必须了解Windows的消息传递机制,当有鼠标活动消息时,系统发送WM_NCHITTEST 消息给窗体作为判断消息发生地的根据。假如你点击的是标题栏,窗体收到的消息值就是 HTCAPTION ,同样地,若接受到的消息是 HTCLIENT,说明用户点击的是客户区,也就是鼠标消息发生在客户区。

    当重载窗体的 WndProc 方法时,可以截获 WM_NCHITTEST 消息并改些该消息,当判断鼠标事件发生在客户区时,改写改消息,发送 HTCAPTION 给窗体,这样,窗体收到的消息就时 HTCAPTION ,在客户区通过鼠标来拖动窗体就如同通过标题栏来拖动一样。

    具体实现如下:
    void TimerTick(object sender, EventArgs e)
    {
    //先设定浮动区域是否可以响应鼠标事件
    Point pt = new Point(MousePosition.X, MousePosition.Y);
    if(this.Bounds.Contains(pt))
    {
    //先改变背景
    this.BackColor = Color.GhostWhite;

    Control control =new Control();
    if(GetAsyncKeyState(VK_LBUTTON) < 0)
    {
    ReleaseCapture();
    SendMessage(this.Handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
    }

    }
    else
    this.BackColor = Color.White;
    }

    其中需要引用dll:
    [DllImport("user32.dll")]
    private extern static int GetAsyncKeyState(int nIndex);
    [DllImport("User32.dll",EntryPoint="SendMessage")]
    private extern static int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
    [DllImport("user32.dll")]
    public static extern bool ReleaseCapture();

    这个实现可能丑一点,不过实际使用的效率还不错。大家也可以测试一下。

    • 已标记为答案 MagicZhang 2010年3月18日 6:58
    2010年3月18日 6:57

全部回复

  • 你好!
         我也没有想到太好的办法,尝试了一些方法都没有实现你需要的效果!使用TransparencyKey来实现透明窗体,的确就无法接受鼠标事件了!
    周雪峰
    2010年3月17日 14:27
    版主
  • 您好,
    实在不行写个钩子?试试?弄得有点像spy++。呵呵,反正目的是接获消息,。只是个建议,请斟酌,。

    2010年3月17日 15:20
  • 真是谢谢周版主和BorisJineman兄 。这个问题自己想办法解决了。
    其实之前是想复杂了,使用TransparencyKey()后窗体是不能接收鼠标消息了。但可以用一个定时器检测鼠标位置,判断是否在窗体内,若在窗体内且鼠标左键按下,便发送HTCAPTION消息给窗体模拟鼠标拖动标题栏即可。
    google了一下,也有这种说法:
    首先必须了解Windows的消息传递机制,当有鼠标活动消息时,系统发送WM_NCHITTEST 消息给窗体作为判断消息发生地的根据。假如你点击的是标题栏,窗体收到的消息值就是 HTCAPTION ,同样地,若接受到的消息是 HTCLIENT,说明用户点击的是客户区,也就是鼠标消息发生在客户区。

    当重载窗体的 WndProc 方法时,可以截获 WM_NCHITTEST 消息并改些该消息,当判断鼠标事件发生在客户区时,改写改消息,发送 HTCAPTION 给窗体,这样,窗体收到的消息就时 HTCAPTION ,在客户区通过鼠标来拖动窗体就如同通过标题栏来拖动一样。

    具体实现如下:
    void TimerTick(object sender, EventArgs e)
    {
    //先设定浮动区域是否可以响应鼠标事件
    Point pt = new Point(MousePosition.X, MousePosition.Y);
    if(this.Bounds.Contains(pt))
    {
    //先改变背景
    this.BackColor = Color.GhostWhite;

    Control control =new Control();
    if(GetAsyncKeyState(VK_LBUTTON) < 0)
    {
    ReleaseCapture();
    SendMessage(this.Handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
    }

    }
    else
    this.BackColor = Color.White;
    }

    其中需要引用dll:
    [DllImport("user32.dll")]
    private extern static int GetAsyncKeyState(int nIndex);
    [DllImport("User32.dll",EntryPoint="SendMessage")]
    private extern static int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
    [DllImport("user32.dll")]
    public static extern bool ReleaseCapture();

    这个实现可能丑一点,不过实际使用的效率还不错。大家也可以测试一下。

    • 已标记为答案 MagicZhang 2010年3月18日 6:58
    2010年3月18日 6:57