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

本文关键字:事件 Paint 何强制 Panel1 Panel2 | 更新日期: 2023-09-27 18:35:06

我有一个SplitContainer(确切地说是NonFlickerSplitContainer),我将它的两个面板都视为一个画布来绘画。我使用Graphics.DrawImage方法在面板上分别绘制位图。我首先刷新 Panel1,然后刷新 Panel2,这会导致垂直/水平撕裂 - Panel1 的绘画结束,然后 Panel2 的绘画开始 - 这就是原因。我的问题的解决方案是什么?我使用 splitContainer 作为具有之前/之后功能的"位图视频流"的输出。也许我可以以某种方式冻结 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();
    }

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

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

从链接:

使控件的特定区域无效,并导致向控件发送绘制消息。(可选)使分配给该控件的子控件失效。

因此,与其单独使每个面板失效,只需调用此方法一次,它应该可以执行您想要的操作。或者只修改一点代码:

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

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

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

强制控件使其

工作区失效,并立即重绘自身和任何子控件。

因此,只需将splitContainer.Invalidate(true)更改为splitContainer.Refresh()即可。

也许我可以以某种方式冻结 UI 直到Panel2_Paint结束?

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

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

如果绘制操作花费的时间过长,则临时图形伪影将开始出现在"冻结"区域中,直到 ResumeLayout() 之后。

我经历过不可能确保两个单独的面板。Paint() 事件在同一时刻完成,至少在 WinForms 项目中不会。唯一对我有用的解决方案是DonBoitnott的建议。我现在使用单个面板并模拟拆分容器行为。

如果我回答这个问题,我建议你放弃拆分容器并渲染到单个表面,这样你总是在单个 Paint 事件中,你只需将其区域化并相应地绘制即可。 – DonBoitnott 二月 17 '16 在 17:51