【问题标题】:Invalidate into own bitmap无效成自己的位图
【发布时间】:2011-12-14 22:54:43
【问题描述】:

我希望在屏幕外将控件渲染到某个位图,以便我可以快速访问它。

不幸的是Control.DrawToBitmap 似乎绘制了调用它的整个控件,包括它的所有子控件。在内部,它向位图发出带有自备 DC 的 WM_PRINT 消息。然后将该临时位图传送到用户提供的位图。对我来说无法接受,我宁愿根据需要更新此位图,以便在需要位图时将性能损失降至最低。

在理想情况下,我希望表单表现得好像它在屏幕上可见(但它不能)。这意味着,如果某个控件的Text 属性发生了更改,那么表单将部分失效。捕获相关的消息/事件之后,我可以指定我自己的 DC 以用于绘制表单,或者简单地将表单 DC BitBlt 到我自己的。

我看过的一些方向:

  • OnPaint 中的PaintEventArgs 参数似乎保存了一个成员 savedGraphicsState,也许它可以用来找出不需要失效的东西
  • 使表单可见但在屏幕区域之外。但是,控件不会被绘制。
  • 在窗口上手动调用 RedrawWindow(),同样的情况

【问题讨论】:

  • 我不完全清楚为什么 DrawToBitmap(WM_PRINT 消息)不适合您使用。您是否担心性能?绘制到位图不会比绘制到屏幕上慢。
  • 我需要一个缓冲机制。 DrawToBitmap 从头开始​​绘制整个控件,而在普通窗口上使用的无效系统只会更新实际更改的区域。
  • @Frank,你考虑过使用 Bitblt 吗? codeproject.com/KB/GDI-plus/Bitblt_wrapper_class.aspx
  • 是的,但如果窗口实际上在屏幕上不可见,它就不起作用。
  • @FrankRazenberg 似乎正在尝试创建一个控件以在单独的 LCD 上显示。大概他有一个 API 允许他将位图发送到该 LCD,但它不是 Windows 识别的显示器,因此他不能简单地在该屏幕上显示控件。为此,他尝试获取屏幕外控件的图像,并在需要更新时收到通知,因此他可以将位图的更新部分发送到单独的屏幕。大概它是带宽受限的,因此每次发送整个位图都不是一种选择。 对吗???如果是这样,那才是真正的问题。

标签: c# winapi off-screen wm-paint


【解决方案1】:

我认为有两个问题:

  1. 找出控件的无效区域(无需 Windows 帮助)
  2. 仅渲染无效部分。

对于第一个问题,我认为您主要靠自己。您应该跟踪哪些控件发生了变化,并有需要更新的簿记。

对于第二个问题,您可以尝试自己发送 WM_PRINT 消息,并提供仅引用小位图的 DC。原始 DC API 允许您偏移和裁剪 HDC 的有效绘图区域。 如果你很幸运,windows 会从 HDC 中推断出渲染区域,如果没有,大部分完全脱离位图的渲染命令应该是相当便宜的,因为没有需要更改的像素。

您应该能够通过打印到 1x1 位图来验证这一点,并测试它是否更快,和/或验证在 WM_PAINT 中发送的剪辑区域是否减小到位图大小。

【讨论】:

    【解决方案2】:

    如果控件被克隆,我认为它会起作用,以便您获得一个不位于表单上且没有子控件的控件:

    Control ctrl = ControlFactory.CloneCtrl(this.button3);
    Bitmap bmp = new Bitmap(ctrl.Width, ctrl.Height);
    ctrl.DrawToBitmap(bmp, new Rectangle(0, 0, ctrl.Width, ctrl.Height));
    bmp.Save(@"C:\Users\Oli\Desktop\test.bmp");
    

    我使用了The Code Project中的lxwde写的ControlFactory。

    ControlFactory 并不完美,但它足够简单,可以轻松改进。

    【讨论】:

    • 抱歉,我不明白这有什么关系。
    • 您说您的解决方案不幸地绘制了子控件。我的解决方案没有。我已经尝试使用包含按钮的面板。它仅将面板绘制到位图,但没有子控件。当控件在屏幕上不可见时,它也可以工作。
    • 我不太明白你想要达到什么目的。你说:“我希望表单表现得好像它在屏幕上可见(但它不能)”。为什么在屏幕上看不到?为什么要渲染一个在屏幕上不可见的表单?
    • 渲染表单的位图将被发送到单独的 LCD。我希望控件在正常情况下自动验证自身,例如在更改它的窗口文本时。我也想知道什么时候会发生这种情况。
    • Control 中有一个Invalidated 事件。您可以为所有控件订阅它。
    【解决方案3】:

    我为您制作了一个示例项目,其中展示了一些 onPaint 事件。如果您看不到它以这种方式解决,只需更新示例。

    问候!

    在此处下载: http://www.goldengel.ch/temp/OnPaintExample.zip

    Private Sub Button1_Paint(sender As System.Object, e As System.Windows.Forms.PaintEventArgs) Handles Button1.Paint
        Dim bm As New Bitmap(Me.Button1.Width, Me.Button1.Height, PixelFormat.Format32bppRgb)
    
        Button1.DrawToBitmap(bm, New Rectangle(0, 15, bm.Width -5, bm.Height+2))
        Using gr As Graphics = Graphics.FromImage(bm)
            gr.DrawString(DateTime.Now.ToLongTimeString, Me.Font, Brushes.Lime, 0, 0)
        End Using
        Me.PictureBox1.BackgroundImageLayout = ImageLayout.Tile
        Me.PictureBox1.BackgroundImage = bm
    
    End Sub
        Public Class myTextBox
            Inherits System.Windows.Forms.TextBox
    
    
            Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
                MyBase.OnPaint(e)
                e.Graphics.Clear(Color.Yellow)
                e.Graphics.DrawString(DateTime.Now.ToLongTimeString, Me.Font, Brushes.Gray, 0, 0)
            End Sub
    
            Public Sub New()
                SetStyle(ControlStyles.UserPaint, True)
            End Sub
        End Class
    

    【讨论】:

      猜你喜欢
      • 2012-01-24
      • 1970-01-01
      • 1970-01-01
      • 2015-12-20
      • 2016-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多