通过透明的Windows窗体防止鼠标点击

8

我正在制作一个小工具,可以在浮动侧边栏中切换“笔”按钮后使用鼠标在屏幕上绘图。

我已经通过使用顶层窗体,并将其背景设置为透明键来覆盖整个屏幕来实现这一点(请不要笑)。

当我处于绘图模式时,我需要使鼠标不能穿透窗体并点击到下面的内容。我尝试了以下方法:

这些方法成功地停止了鼠标的穿透,但是也取消了窗体的最大化,并且使用HTCAPTION IntPtr(2)拖动窗体。我尝试使用MSDN上列出的其他值,但没有成功。

我深感无助,非常感谢任何帮助(请考虑新手友好)。

PS:我现在正在使用这个工具。

        //code for allowing clicking through of menus
        protected override void WndProc(ref Message m)
        {              
            if (penMode && m.Msg == 0x84)
            {
                m.Result = new IntPtr(2);    
            }
            else
                base.WndProc(ref m);
        }

更新:通过另一种方法完全解决了问题。看起来WndProc不起作用,所以我只是创建了一个覆盖整个屏幕的空白窗体,并从其中显示我的主窗体(form.Show(this))。然后调整空白窗体的不透明度,该窗体位于下方,从0%到1%允许/防止点击穿过。行得通!感谢所有答案,让我学到了很多。

2个回答

8
实际上,没必要笑——在我看来,你已经以正确的方式进行了操作。由于你不拥有桌面,所以不应该直接在其上绘图。相反,你需要通过覆盖一个透明的表单来模拟它,然后在其上绘制。因为你拥有透明覆盖表单,所以在其上绘制没有问题。
但除此之外,听起来你只是随意尝试值,没有清楚地理解它们的实际作用。这就像闭着眼睛扔飞镖。你的命中率不会很高。
让我们先了解一下你的代码是做什么的。神奇的值0x84对应WM_NCHITTEST消息,Windows会向窗口发送此消息,以确定如何处理该窗口上的鼠标点击。作为对该消息的响应,你会回复其中一个HT*值,在链接的文档中给出。每个值都有特定的含义,在文档中也有解释。例如:
  • HTCAPTION(值为2)表示窗口的标题栏应被视为被单击的部分。你知道在Windows中,你可以使用标题栏将窗口拖动到屏幕上,因此返回HTCAPTION以响应鼠标单击将允许您的窗口可拖动。您会在无边框窗体(即没有标题栏的窗体)上看到这个用法,以允许它们可移动。

  • HTTRANSPARENT(值为-1)是另一个可用的值。这个很简单。它只是使您的窗口看起来透明。就像说“不要理我,在这里没有窗口!”鼠标单击事件将简单地传递给Z顺序下面的窗口,就像您不存在一样。

  • HTCLIENT(值为1)是单击发生在窗口客户区任何位置时的默认结果。当您希望一切正常工作时,您将返回此值(或简单地调用默认窗口过程)。返回此值的单击事件将由框架正常处理,触发表单的Click事件,或传递给位于表单上的子控件。

因此,在你绘制时,你可能想返回HTTRANSPARENT。在你绘制时,你可能想返回HTCLIENT,这样你的绘图代码就可以看到鼠标事件并绘制结果。

然后修复你的代码:

// Code for allowing clicking through of the form
protected override void WndProc(ref Message m)
{
    const uint WM_NCHITTEST = 0x84;

    const int HTTRANSPARENT = -1;
    const int HTCLIENT      = 1;
    const int HTCAPTION     = 2;
    // ... or define an enum with all the values

    if (m.Msg == WM_NCHITTEST)
    {
        // If it's the message we want, handle it.
        if (penMode)
        {
            // If we're drawing, we want to see mouse events like normal.
            m.Result = new IntPtr(HTCLIENT);
        }
        else
        {
            // Otherwise, we want to pass mouse events on to the desktop,
            // as if we were not even here.
            m.Result = new IntPtr(HTTRANSPARENT);
        }
        return;  // bail out because we've handled the message
    }

    // Otherwise, call the base class implementation for default processing.
    base.WndProc(ref m);
}

1
在一台Windows 8电脑上进行测试时,当鼠标点击透明窗口部分时,该窗口不会向父窗口发送消息(在日志记录和Spy ++中验证)。 - John Koerner
@Cody,哇,非常感谢你提供如此清晰和完整的答案!看起来很不错,但我现在发现,在绘制一个点之后,如果我再次点击那个点,它现在是蓝色的,而不是透明的,那么这个点击事件会穿过去。 - Mr Pie
1
由于John提到的同样的原因,在Windows 10上无法工作。 - Professor of programming
无法帮助你,@bonner。我没有在任何地方安装Windows 8或10。这不是一个很大的优先事项,因为我没有连接触摸屏到我的电脑上。 - Cody Gray
ChronosMOT提出的解决方案虽然是一种hacky的解决方法,但确实有效。 - Professor of programming

2

您可能只需要将窗口的可见度设置为大约5%,并保持透明键未激活。

基本上您不会注意到它,但它确实存在:D

希望这可以帮助您。


谢谢@Chronos,但是有没有办法做到按钮和更重要的是用笔模式绘制的东西不会只有5%可见? - Mr Pie
2
我想我低估了这个问题。我现在有一个使用三种表单的解决方案。第一个只显示GUI,大部分是由于透明度关键字而几乎不可见。第二个仅以5%的可见度控制输入。第三个再次使用透明度关键字进行绘制。抱歉我之前没有考虑周全。这次测试了一下,这里有文件以及包含所有文件的.rar文件 https://drive.google.com/folderview?id=0B-5Qr5swDDp8ZV9CMUVrVHloeWc&usp=sharing - ChronosMOT
谢谢,我已经想出了一个类似的解决方案,使用了两个表单 - 我在与UI相同的表单上绘制,但我还没有更新解决方案,因为我还没有能够在所有目标机器上进行全面测试。很高兴看到我和其他人的思路是一致的! - Mr Pie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接