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

时间:2016-02-17 15:04:27

标签: c# winforms bitmap paint splitcontainer

我有一个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();
    }

3 个答案:

答案 0 :(得分:0)

  

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

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

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

如果您的绘图操作花费的时间太长,那么临时图形工件将开始出现在“冻结”区域中,直到ResumeLayout()之后。

答案 1 :(得分:0)

查看enter image description here的文档。

从链接:

  

使控件的特定区域无效并导致将油漆消息发送到控件。 (可选)使分配给控件的子控件无效。

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

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

基本上我正在做的是如果他们两个都需要重新绘制,让splitContainer处理它,否则单独检查每一个并在必要时绘制。

来自@ DonBoitnott的评论,而不是使用文档中的Invalidate(true)使用SplitContainer.Invalidate(bool invalidate Children)

  

强制控件使其客户区无效并立即重绘自身和任何子控件。

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

答案 2 :(得分:0)

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

  

如果我回答这个问题,我建议你放弃拆分容器并渲染到单个表面,这样你总是在一个Paint事件中,你只需将其分开并相应地绘制。 - DonBoitnott 2016年2月17日17:51