Winforms:如何获取透明窗体上透明控件的鼠标事件

Winforms: How to get Mouse Events for Transparent Control on a Transparent Form

我有一个覆盖在 c# .NET winforms 应用程序中的桌面的透明表单。透明度是通过将 BackColor 设置为亮橙色然后将 TransparencyKey 设置为相同的亮橙色来完成的。

到目前为止效果很好,它创建了一个透明的表单。然后我想在透明窗体上创建一个控件,它在桌面上的项目周围绘制一个矩形。所以基本上你可以想到一个带有矩形边框的透明按钮。为了做到这一点,我目前扩展了 Control 并在同样透明的父窗体中设置了这样的控件:

public class CustomControl : Control 
{
   public CustomControl(Size size, Point point)
   {
      SetStyle(ControlStyles.SupportsTransparentBackColor, true);
      this.BackColor = Color.Transparent;
      this.Size = size;
      this.Location = point;
   }

   protected override void OnPaint(PaintEventArgs e)
   {
      base.OnPaint(e);
      Pen pen = new Pen(Color.Red, 2f);
      e.Graphics.DrawRectangle(pen, this.ClientRectangle);
   }
}

这创建了我正在寻找的确切效果,因为在透明窗体中有一个带有实心不透明矩形边框的控件,您可以通过控件和窗体看到桌面(请参见下图左侧) .

问题是控件的透明区域收不到任何鼠标事件。事实上,它基本上不存在。例如,当您的鼠标越过控件的红色矩形边框时,会触发鼠标悬停、鼠标进入和鼠标离开事件,并将光标更改为您为控件设置的任何 this.Cursor。有一次,鼠标在控件的透明部分none这些东西触发。

我怎样才能使控件的外观保持原样,但仍能在透明区域接收鼠标事件。特别是我想接收鼠标悬停并让控件 Cursor 值受到尊重。请注意,如果我将控件的 BackColor 设为 Color.Transparent 以外的任何值,则鼠标事件可以正常工作。

谢谢!

更新

根据 Hans 的评论,我想补充一点,我们已经实现了上述内容,并且它通常有效(即,透明区域确实响应鼠标事件)。出现这个问题的原因是因为我最近用所有 Windows 8.1 更新和最新的 ATI 图形驱动程序重建了我的机器,之后上面的设置不再有效(控件上的透明区域不再接收任何鼠标事件并且出于所有意图和目的都不是控件的一部分)在我同事的机器上它几乎总是有效,尽管我们偶尔注意到它不起作用但我们永远无法始终如一地重现该问题。

我的假设是我们做错了什么导致透明区域不响应鼠标事件。然而,根据 Hans 的评论,上面的代码似乎永远不会起作用,它起作用的唯一原因是 Aero 中的错误。

我们透明键的确切颜色是rgb(255, 128, 0)。此外,我们确实注意到放置在透明表单上的任何标签控件看起来都很糟糕(根据 Han 的评论)。

更新 2

根据 Han 关于 Aero 透明度错误的补充评论,我有以下更新的问题。

  1. 是否有任何关于此错误的信息可以解释错误是什么?
  2. 控件的透明区域的预期行为是什么(假设没有错误)?鼠标事件(例如鼠标悬停)是否应该在透明区域起作用?

最终答案(通常有效)

下面 Reza 提供的答案确实适用于我的某些计算机。然而,我的主桌面继续顽固地拒绝合作。即使在使用相同版本的 windows 和 .NET 的计算机之间复制确切的项目,问题仍然存在。出现时的问题是透明区域不会触发鼠标事件,不被视为控件的一部分。

此外,我注意到 Reza 也注意到了同样的事情,即某些 TransparencyKey 颜色完全无效。尽管我不知道该错误的任何详细信息,但我必须同意 Hans 的观点,即 WinForms 上存在与透明度有关的错误,如果有人从头开始,我会选择 WPF 路线并避免将来可能出现的麻烦。

最后,我们根据 Hans 的一些回答实施了一个解决方案,需要使用计时器来检查鼠标位置并嵌套两个表单(一个设置了不透明度)以便能够管理鼠标光标.该解决方案已在我们的所有系统上运行,并有望在我们转移到 WPF 之前继续有效。我接受了 Reza 的回答,因为它似乎在大多数地方都有效,但请注意,它可能对您不起作用,并且没有押韵或理由来解释为什么。

有关我们实施的两个解决方法的详细信息,请参阅 Hans 的以下问题和答案。

MouseHover and MouseLeave Events controlling

How can I add transparency to a c# form while keeping controls visible?

重要

考虑将表单的 BackgroundColor 设置为红色,将 TransparencyKey 设置为红色,将透明控件的 BackGroundColor 设置为透明,这样就可以了!

我看到的奇怪之处在于,该方法不适用于洋红色,但适用于红色和蓝色。


我认为你应该这样创建你的透明控件:

透明控件代码

public class TransparentControl : Control
{
    public TransparentControl()
    {
        this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
    }

    private const int WS_EX_TRANSPARENT = 0x20;
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle = cp.ExStyle | WS_EX_TRANSPARENT;
            return cp;
        }
    }
}

以及鼠标进入时的鼠标事件和渲染边框,这是我在Windows 8.1上使用.Net 4.5做的示例:

创建一个Form并在其上放置一个我们使用上面代码创建的TransparentControl,然后处理MouseEnterMouseLeavePaint事件并在鼠标处于控制范围内时绘制边框并处理 Click 事件并显示消息。

表单代码

private bool drawBorder;
private void transparentControl1_MouseLeave(object sender, EventArgs e)
{
    drawBorder = false;
    transparentControl1.Invalidate();
}

private void transparentControl1_MouseEnter(object sender, EventArgs e)
{
    drawBorder = true;
    transparentControl1.Invalidate();
}

private void transparentControl1_Paint(object sender, PaintEventArgs e)
{
    if(drawBorder)
    {
        using (var pen = new Pen(this.ForeColor, 5))
        {
            e.Graphics.DrawRectangle(pen, 0, 0, this.transparentControl1.Width - 1, this.transparentControl1.Height - 1);
        }
    }
}

private void transparentControl1_Click(object sender, EventArgs e)
{
    MessageBox.Show("Clicked");
}

截图

鼠标光标位于控件区域,因此黑色边框已绘制。

重要提示

如果绘制与窗体透明键颜色相同的边框,则不会显示边框。

我觉得鼠标光标在那个覆盖框的中间是可以的,并且随着鼠标光标和覆盖框一起移动,让它看起来像一个鼠标光标。

喜欢这张图片

当我在透明窗体上使用图片框控件并在控件上使用鼠标单击事件来触发代码时,我遇到了类似的问题。有时会捕获鼠标点击,有时不会。除了用作表单背景的颜色外,没有其他图案。红色效果很好,但许多其他颜色却不行。即使是黑色也不能始终如一地工作。当 form.backcolor 和 form.transparencykey 设置为这种颜色时,我发现一种近乎黑色的颜色效果很好。根据这个和其他经验,VB Studio 和处理透明度的方式似乎存在错误。

Form1.BackColor = Color.FromArgb(64, 0, 0) ' a color that works with transparency and allows picturebox to be clicked