【问题标题】:C# WinForms, ToolTip with semi-transparent backgroundC# WinForms,具有半透明背景的工具提示
【发布时间】:2018-07-26 12:30:12
【问题描述】:

我正在开发这个小型 WinForm 应用程序,并决定使用具有半透明背景的自定义绘制工具提示。所以我开始将 ToolTip 上的OwnerDraw 属性设置为True,为Draw 和Popup 事件创建了事件处理程序(参见下面的示例代码。注释版本也不起作用)。

private void toolTip_Popup(object sender, PopupEventArgs e)
{
    e.ToolTipSize = new Size(400, 400);
}

private void toolTip_Draw(object sender, DrawToolTipEventArgs e)
{
    //e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.Red)), new Rectangle(e.Bounds.Location, e.Bounds.Size));
    e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.Red)), new Rectangle(0, 0, 400, 400));
}

现在,当工具提示第一次显示特定控件时,一切都会按预期工作。见下图(400x400 工具提示,半透明红色背景)。

但是当我第二次将鼠标悬停在同一个控件上时,工具提示会失去其半透明性。见下图。为什么会这样?

【问题讨论】:

  • ToolTip 本身不支持透明效果。你可以伪造它,使用 Graphics.CopyFromScreen() 在 Popup 中制作屏幕截图。在 Draw 中,首先绘制该位图,使其看起来透明,然后在其顶部绘制尖端。您的应用需要具备 dpiAware 功能,才能在任何显示器上顺利完成。
  • 好的,但是为什么它在第一次用于特定控件时可以显示透明背景?
  • @HansPassant 我不确定你是否理解我的问题。图片是来自 WinForm 应用程序的屏幕截图(它们不是在某些 SW 中组成的,这就是我想要的效果)。 TooTtip 的半透明背景正在工作,问题是它只显示了一次半透明背景(第一次为控件显示 ToolTip)。
  • 它不会失去其半透明性,它会在您的半红色下方绘制黑色背景。如果您在第二张图片中看到颜色不是红色,而是深红色。
  • 你可以做的是构建一个派生自ToolTip 的类(利用它的鼠标驱动/定时器驱动的事件系统,中止Popup 事件的工具提示(你必须订阅,您不能覆盖它),并在您可以从提供的e.AssociatedControl 派生的位置显示半透明标签(或类似控件),并将替代控件移动到e.AssociatedControl@ 的计算位置987654334@. 其他路径,IMO,令人沮丧。

标签: c# .net winforms


【解决方案1】:

感谢大家的帮助。我很确定 Ben Voigts 或 Jimis cmets 也可以以某种方式解决这个问题(我稍后会尝试并更新答案,如果我能够使用它们)。

我的解决方案基于 Hans Passant 提出的第一条评论,他建议在 Popup 事件处理程序 (toolTipDay_Popup) 中使用 Graphics.CopyFromScreen(),以捕获工具提示下方的图像,然后在 Draw 事件处理程序中(toolTipDay_Draw) 我刚刚画了捕获的图像。

(如 Hans Passant 所述,不同的 DPI 缩放存在问题,但这可以由 Farshid T 在How to get Windows Display settings? 中的回答解决,我没有将它包含在下面的代码中)。

所以我现在使用的解决方案如下:

Bitmap dayToolTipBackground = new Bitmap(200, 200);
private void toolTipDay_Popup(object sender, PopupEventArgs e)
{
     e.ToolTipSize = new Size(200, 200);
     var backGraphics = Graphics.FromImage(dayToolTipBackground);
     var cursorPosition = Cursor.Position;
     backGraphics.CopyFromScreen(new Point(Cursor.Position.X, Cursor.Position.Y + 21), new Point(0, 0), new Size((200, 200)));    
}


private void toolTipDay_Draw(object sender, DrawToolTipEventArgs e)
{
     e.Graphics.DrawImage(dayToolTipBackground, new Point(0, 0));
     e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.Red)), new Rectangle(e.Bounds.Location, e.Bounds.Size));
}

【讨论】:

    【解决方案2】:

    第一次将鼠标悬停在控件上时,会使用透明颜色 (ARGB.120) 创建一个新的工具提示实例。当您在工具提示之外单击时,工具提示对象是完全释放还是您只是将实例设置为不可见?

    当您第二次将鼠标悬停在实例上时,如果旧的工具提示对象未释放,则有可能再次调用相同的对象。因此,现在当您在 color = ARGB.120 的现有工具提示上执行 Graphics.FillRectangle() 时,您只是在其上覆盖了另一层 ARGB.120 颜色,由于颜色级别已更改,这将使其进一步变暗。

    因此,当您在第一次调用工具提示后单击它之外时,您可能需要释放工具提示对象(或 e.Graphics 对象,如果这不会影响应用程序的其他部分)并创建新的每次将鼠标悬停在控件上时的工具提示对象。

    【讨论】:

    • 并非如此。做一个实验,看看结果。将 A=120 更改为 20 这样的低值。它不会逐渐变为“红色”,而是立即变为“黑色”。
    【解决方案3】:

    需要告知窗口弹出窗口下方的窗口需要重绘。这是通过“分层窗口”样式完成的。通过分层,内容以 z 顺序和透明度混合工作绘制。如果没有分层,只有顶部窗口会被发送重绘,它会在 DC 屏幕缓冲区中无意义的剩余数据之上绘制。

    你可以试试p/invoking SetLayeredWindowAttributes

    我强烈建议阅读有关分层 Windows 的 MSDN 文档: Here 和 here

    【讨论】:

    • 是的,但为什么它确实第一次起作用。工具提示没有分层窗口样式
    • @γηράσκωδ'αείπολλάδιδασκόμε:因为你第一次幸运地得到了“剩余数据”。这不能保证,并且可能会根据 Windows 版本、Aero 是否处于活动状态等而开始或停止工作。
    • @BenVoigt 感谢您的帮助,我稍后会尝试仔细研究一下。我最终使用了 Hans Passant 提供的不同解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-12
    • 1970-01-01
    • 2012-07-06
    • 2018-10-05
    • 2015-05-18
    • 1970-01-01
    • 2014-12-19
    相关资源
    最近更新 更多