C# 如何强制 Panel1 和 Panel2 的 Paint 事件同时完成?

C# How to force Paint event of Panel1 and Panel2 complete at the same time?

我有一个 SplitContainer(确切地说是 NonFlickerSplitContainer 一个),我将它的两个面板视为一个单独的 canvas 来绘画。我使用 Graphics.DrawImage 方法分别在面板上绘制位图。我先刷新 Panel1,然后刷新 Panel2,这导致 vertical/horizontal 撕裂 - Panel1 的绘制结束,然后 Panel2 的绘制开始 - 这就是原因。我的问题的解决方案是什么?我将 splitContainer 用作具有 before/after 功能的 "bitmap video stream" 的输出。也许我可以冻结 UI 直到 Panel2_Paint 结束?

    private void splitContainer_Panel1_Paint(object sender, PaintEventArgs e)
    {
        if (frameA != null)
        {
            if (ORIGINAL_SIZE_SET)
                e.Graphics.DrawImage(frameA, 0, 0);
            else
                e.Graphics.DrawImage(frameA, 0, 0, ClientSize.Width, ClientSize.Height);
        }
    }

    private void splitContainer_Panel2_Paint(object sender, PaintEventArgs e)
    {
        if (frameB != null)
        {
            //...

            if (ORIGINAL_SIZE_SET)
                e.Graphics.DrawImage(frameB, x, y);
            else
                e.Graphics.DrawImage(frameB, x, y, ClientSize.Width, ClientSize.Height);
        }
    }

    private Bitmap frameA = null;
    private Bitmap frameB = null;

    private void RefreshOutput(bool refreshClipA = true, bool refreshClipB = true)
    {
        if (refreshClipA)
        {
            frameA = GetVideoFrame(...);

            //...
        }

        if (refreshClipB)
        {
            frameB = GetVideoFrame(...);

            //...
        }

        if (refreshClipA)
            splitContainer.Panel1.Refresh();

        if (refreshClipB)
            splitContainer.Panel2.Refresh();
    }

Maybe I can freeze UI somehow until Panel2_Paint ends?

看看:https://msdn.microsoft.com/en-us/library/system.windows.forms.control.suspendlayout(v=vs.110).aspx

在您的控件上调用 SuspendLayout(),执行所有图形操作,然后调用 ResumeLayout() 一次更新所有内容。该文档中有一个示例。

如果您的绘图操作花费的时间太长,那么临时图形工件可能会开始出现在 "frozen" 区域中,直到 ResumeLayout() 之后。

查看 SplitContainer.Invalidate(bool invalidate Children) 的文档。

来自link:

Invalidates a specific region of the control and causes a paint message to be sent to the control. Optionally, invalidates the child controls assigned to the control.

因此,与其分别使每个面板无效,不如调用此方法一次,它应该会执行您想要的操作。或者稍微修改一下代码:

if (refreshClipA && refreshClipB)
{
    splitContainer.Invalidate(true);
}
else
{
    if (refreshClipA)
    {
        splitContainer.Panel1.Refresh();
    }
    else if (refreshClipB)
    {
        splitContainer.Panel2.Refresh();
    }
}

基本上我正在做的是,如果它们都需要重新粉刷,让 splitContainer 处理它,否则单独检查每一个并在需要时进行粉刷。

从@DonBoitnott 的评论开始,而不是使用 Invalidate(true) 使用 Refresh() 文档中的

Forces the control to invalidate its client area and immediately redraw itself and any child controls.

所以只需将 splitContainer.Invalidate(true) 更改为 splitContainer.Refresh()

我遇到过无法确保两个单独的 panel.Paint() 事件同时完成的情况,至少在 WinForms 项目中是这样。唯一对我有用的解决方案是 DonBoitnott 的建议。我现在使用单个面板并模拟 splitcontainer 行为。

If I were answering this, I would suggest you ditch the splitcontainer and render to a single surface, that way you are always in a single Paint event and you simply region it off and draw accordingly. – DonBoitnott Feb 17 '16 at 17:51