【问题标题】:What is a general solution to creating a transparent control?创建透明控件的一般解决方案是什么?
【发布时间】:2012-08-15 14:34:12
【问题描述】:

因此,现在已经查看了一段时间,我已经阅读了大约 20 种不同的实现,但还没有看到一个可靠的、基于控件、基于框架的通用解决方案来实现真正透明的控件。

这种可能过于简单的方法根本没有做任何事情,并创建了一个永远不会被使用的例程 (InvalidateEx):http://bytes.com/topic/c-sharp/answers/248836-need-make-user-control-transparent

这似乎暗示了之前的答案是什么:https://web.archive.org/web/20141227200000/http://bobpowell.net/transcontrols.aspx

但是它使用计时器滴答来执行 InvalidateEx 方法。

这似乎放弃了多层控件并在单个分层面板上处理所有绘图,同时掩盖了标签对象如何实现其自身的透明度:http://www.codeproject.com/Articles/25048/How-to-Use-Transparent-Images-and-Labels-in-Window

这是一个完全不基于框架的方法:http://www.codeproject.com/Articles/30135/General-solution-for-transparent-controls

这个答案中接受的解决方案对我根本不起作用:Transparent Control on Transparent control?

我得到的控件是一个通用的纯色(白色),上面画着透明图像。

所以,我开始我自己的实现。

我宁愿不依赖自底向上的 GDI 绘图,即以最父形式执行所有绘图,我宁愿不对透明位图进行预渲染,我宁愿不从其他东西继承Forms.Control 正在执行一些自己的巧妙技巧来缩短透明度。我真的很想深入了解如何在具有完全透明度支持的控制面上绘图。显然,例如标签控件本身就有一个完整的实现,它是如何工作的?

从逻辑上讲,透明控件不能依赖简单的 1 位剪辑区域,并且需要保持大部分完整。需要正确处理控件的多层,即正确的绘制顺序等,而不会导致无限循环。

第一个也是最常见的建议是:

SetStyle(ControlStyles.SupportsTransparentBackColor, True)
Me.BackColor = Color.Transparent

我理解的第一行是同义词

Protected Overrides ReadOnly Property CreateParams As System.Windows.Forms.CreateParams
    Get
        Dim result As System.Windows.Forms.CreateParams = MyBase.CreateParams
        result.ExStyle = result.ExStyle Or WindowsMessages.WS_EX_TRANSPARENT
        Return result
    End Get
End Property

根据被继承的类,它的行为非常不同。对于 Forms.Control,OnPaintBackground 选择使用父控件的背景颜色清除剪辑矩形。不太聪明。

覆盖和删除基本 OnPaintBackground 调用会导致控件保持“脏”状态,而不会覆盖它下面的内容。使用透明颜色(乐观方法)清除会产生黑色透明像素场,并使控件显示为完全黑色。

从逻辑上讲,这是有道理的,为了提高效率而进行的底层控件不会重新绘制他们预期此控件会绘制的区域。然后表单的该区域永远不会更新并保持表单存在之前的任何内容。在其上绘制透明颜色有点令人困惑,因为这意味着图形缓冲区甚至不包含底层像素,而是实际的虚无,它们被替换为黑色透明的东西以使控件显示为黑色。

第一个挑战似乎是处理控件本身重绘时的处理,让所有底层控件在该区域中重绘自己(不会自动使控件本身无效以开始堆栈溢出),然后在底层和最多-日期像素。我还没有找到一种方法来做到这一点。我最接近的是通过挖掘 .net 代码并找到 ButtonRenderer.DrawParentBackground 方法。这在很大程度上似乎有效,尽管快速测试表明重叠组件的 z 顺序仍未正确处理,底层控件绘制在该控件的顶部。

第二个挑战是当底层控件发生变化时让控件正确地重绘自身。通过在质询一中断开自动失效,很难通知控件使其自身失效并且不使其父项失效。

如果有人完全解决了这个问题,请提供代码,如果没有,请提供一些我如何处理问题的一个或几个部分的经验。

非常感谢。

编辑:

根据WS_EX_TRANSPARENT - What does it actually do?,似乎是关于 WS_EX_TRANSPARENT 的假设详细信息

研究更新: ButtonRenderer.DrawParentBackground 实际上只是由 Control 本身使用,仅当使用 Application.EnableVisualStyles() 并简单地强制父级的背景绘制。它似乎是的同义词

InvokePaintBackground(Parent, e.Graphics)

这将重现父控件的所有背景绘制细节。

【问题讨论】:

  • 查看这篇文章以获得关于这个主题的解释。 techrepublic.com/article/…
  • 这似乎是关于使用 windows 参数的全窗口透明度的讨论,同时可能提供用户事件的传递。具体来说,我不是在寻找 Control 元素的“恒定 alpha”透明度,而是在 Control 上改变 alpha 的能力,允许使用任意形状和混合并暴露底层组件。

标签: vb.net winforms controls transparency


【解决方案1】:

我已经创建了所需的通用透明度控件,解决方案是对控件及其父控件中的所有同级控件进行详细分解,并在依赖于 InvokePaint() 和 InvokePaintBackground 的 OnPaintBackground() 方法上准备递归绘图例程() 方法。通过定义连续较小的剪辑并与较低的控件相交,这种方法可以最大限度地减少绘制开销。

  • 完整的控件包括一种检测和响应同级控件的 Invalidate 方法的方法,有效地绘制透明控件堆栈,并有效地允许在动画底层控件上覆盖完整的 Alpha 通道。

  • 控件可以与子控件和父控件的所有排列交错,同时提供视觉上准确的透明度。

  • 控制中未考虑命中测试。

  • 此控件不会过度绘制在绘制事件期间未执行绘制的控件。这是一个很大的限制。

  • 要使用控件,只需从基础继承并覆盖 OnPaint 方法即可执行自定义绘图。也可以覆盖 OnPaintBackground 方法,只要首先调用基方法即可。

最后,如果有人想要包括失效处理在内的完整代码,请告诉我。如果您有系统绘制组件的解决方案,也请告诉我。此外,如果您有一个更简单的解决方案,这意味着我在这方面浪费了很多时间,我也不会因为收到它而感到不安!

OnPaintBackground 方法上的控件摘录如下:

''' <summary>
''' Recursively paint all underlying controls in their relevant regions, stacking drawing operations as necessary.
''' </summary>
''' <param name="pevent"></param>
''' <remarks></remarks>
Protected Overrides Sub OnPaintBackground(pevent As System.Windows.Forms.PaintEventArgs)
    'Store clip and transform for reversion
    Dim initialClip As Region = pevent.Graphics.Clip
    Dim initialTransform As Drawing2D.Matrix = pevent.Graphics.Transform

    'Develop list of underlying controls
    Dim submarinedControls As New List(Of Control)
    For Each Control As Control In m_Siblings
        If Control.Visible AndAlso Above(Control) AndAlso Me.ClientRectangle.IntersectsWith(Control.RelativeClientRectangle(Me)) Then submarinedControls.Add(Control)
    Next

    'Prepare clip for parent draw
    Dim parentClip As System.Drawing.Region = New System.Drawing.Region(initialClip.GetRegionData)
    For Each Control As Control In submarinedControls
        parentClip.Exclude(Control.RelativeClientRectangle(Me))
    Next
    pevent.Graphics.Clip = parentClip

    'Evaluate control relationship to parent, temporarily adjusting transformation for parent redraw. This translation must be relative since the incoming graphics may already have a meaningful transform applied.
    Dim translation As Point = Parent.RelationTo(Me)
    pevent.Graphics.Transform = New Drawing2D.Matrix(1, 0, 0, 1, initialTransform.OffsetX + translation.X, initialTransform.OffsetY + translation.Y)

    'Fully draw parent background
    InvokePaintBackground(Parent, pevent)
    InvokePaint(Parent, pevent)

    'Reset transform for sibling drawing
    pevent.Graphics.Transform = initialTransform

    'Develop initial clip of submarined siblings
    Dim siblingClip As System.Drawing.Region = New System.Drawing.Region(initialClip.GetRegionData)
    siblingClip.Exclude(parentClip)

    For Each Control As Control In submarinedControls
        'Define relative position of submarined sibling to self
        translation = Control.RelationTo(Me)

        'Define and apply clip *before* transformation
        Dim intersectionClip As New Region(Control.RelativeClientRectangle(Me))
        intersectionClip.Intersect(siblingClip)
        pevent.Graphics.Clip = intersectionClip

        'Apply transformation
        pevent.Graphics.Transform = New Drawing2D.Matrix(1, 0, 0, 1, initialTransform.OffsetX + translation.X, initialTransform.OffsetY + translation.Y)

        'Raise sibling control's paint events
        InvokePaintBackground(Control, pevent)
        InvokePaint(Control, pevent)

        'Revert transformation and exclude region
        pevent.Graphics.Transform = initialTransform
        siblingClip.Exclude(intersectionClip)
    Next

    'Revert transform and clip to pre-drawing state
    pevent.Graphics.Transform = initialTransform
    pevent.Graphics.Clip = initialClip
End Sub

【讨论】:

  • 不幸的是,我无法再访问包含我的解决方案的代码库。在您的实施中什么不起作用?
  • 感谢您的帮助,但我已经通过 codeproject 的 TranspControl 解决了我的问题。
  • 该死,他的实现看起来和我的几乎一模一样,除了他成功地使用了 DrawToBitmap。这可能是获得底层控件图形而不必使用其自己的绘制方法的关键。看起来您链接的文章实际上是在我提出原始问题后的第二天!感谢您的链接。
  • 编辑:这是我指的文章codeproject.com/Articles/26878/…
【解决方案2】:
Me.Visible = False

或

Me.Opacity = 0

或

Me.Hide()

【讨论】:

  • 我可以看到原始问题可能被误解了。问题不在于如何使控件透明,而实际上是关于如何实现完整的 alpha 通道透明性,以便通过此上层控件可以部分地看到底层控件,然后优雅地处理更新。不过还是谢谢。
猜你喜欢
  • 2015-03-13
  • 2018-04-02
  • 2010-10-07
  • 2015-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-03
  • 2019-03-09
相关资源
最近更新 更多