0

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

3 回答 3

0

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

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

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

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

于 2016-02-17T15:12:57.237 回答
0

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

如果我要回答这个问题,我建议您放弃拆分容器并渲染到单个表面,这样您始终处于单个 Paint 事件中,您只需将其区域化并相应地绘制。– DonBoitnott 2016年 2 月 17 日在 17:51

于 2017-01-22T09:36:28.757 回答
0

查看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)use Refresh()which:

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

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

于 2016-02-17T15:24:58.350 回答