表单获得焦点时 ObjectListView 上的黑色背景和伪像

Black background and artifacts on ObjectListView when form gets focus

我在 MDI 表单上有多个 TableLayoutPanels。每个 table 布局都有多个控件(包括 ObjectListView)并且只有最上面的控件设置为可见。

我实现了这个 SO answer 中给出的解决方案,并且在第一次打开表单时效果很好,因为在调整大小时布局的子控件没有闪烁。

我还补充了:

    protected override void OnShown(EventArgs e)
    {
        base.OnShown(e);

        foreach (CoTableLayoutPanel tlp in this.Controls)
        {
            if (tlp != null)
            {
                tlp.BeginUpdate();
                tlp.Size = firstLayout.Size;
                tlp.EndUpdate();
            }
        }
    }

我这样做是为了当我在表单中切换布局时,它们的尺寸已经全部正确,并且我在调整大小时避免了进一步的闪烁。这也很好用。

然而,当我在这个派生的 table 布局上有一个 ObjectListView 控件,并且我在表单之间切换时,控件被部分绘制(尤其是边框),很快显示黑色背景并且每次都会调整最后一列的大小(列的 FillsFreeSpace 属性)。

如果我使用标准 TableLayoutPanel,列表控件的行为符合预期,即。没有伪影,没有显示黑色背景,没有调整列的大小。但是,当我打开表格时,我又开始闪烁了。

ObjectListView 具有 InvalidateRefresh 等属性,我尝试在表单的 OnGotFocus 方法中调用这些属性。问题依旧。

这是 ObjectListView 的问题还是我可以从派生的 table 布局中解决这个问题?

编辑

问题是由这个方法引起的:

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= WS_EX_COMPOSITED;
        return cp;
    }
}

注释掉此方法将使 ObjectListView 按预期工作,但闪烁 returns.

上述方法有任何解决方法吗?

显示为黑色的只是 window 的 未上漆 部分。如果窗体上有很多控件,那么重新绘制它们将花费足够的时间来产生明显的瑕疵。当您使用分层 window 时它是黑色的,当您使用不透明度或透明键 属性 时它是黑色的。如果你不这样做,那么工件往往不会那么令人反感,因为未上漆的部分往往是白色的。

大多数程序员认为这是一个闪烁问题,但事实并非如此,DoubleBuffered 属性 无法解决它。抑制它需要 double-buffering all 控件,使用相同的缓冲区。 WPF大致采用的方法。

Double-buffering 一切都是 WS_EX_COMPOSITED 样式标志所做的。纯粹由操作系统完成,不涉及.NET。它是 Aero 的早期版本,首先在 XP 上可用。 OS 为顶层 window 创建一个位图并告诉它的控件绘制到该位图中而不是直接绘制到视频帧缓冲区。绘画完成后,它会将位图一次性写入帧缓冲区。不会使绘画变得更快,但用户认为它非常流畅。如果您现在使用 DoubleBuffered 属性,那么您想将其关闭。

顺便说一句,我发现了 Winforms 的技术,10 年前就在 MSDN 论坛上的一个论坛 post 上首次发布了它。从那以后,它被复制了很多很多次,像野火一样传播开来。我没有得到很多关于它的负面反馈,它解决了绝大多数程序员的问题。我所知道的唯一麻烦制造者是 TabControl,特别是当它有太多选项卡并显示 left/right 导航标志符号时。它的视觉样式渲染器从来都不是问题,当这些字形出现时,它又开始重新绘制自己 over-and-over。看起来像一个非常快速的闪烁,你不会错过它。易于解决。

所以不,继续前进并使用我的解决方案。这是一个很好的方法,以任何其他方式进行操作都非常简单。您基本上必须 re-invent WPF 才能完全消除它,使用 windowless 控件在父 window 的表面上绘制自己。顺便说一句,VB6 做到了这一点,这也是 VB6 UI 看起来如此过时的原因之一。用代码替换控件也可以让你达到目的(Label 和 PictureBox 特别浪费)但这往往需要大量代码而且你不可能击败 3 行代码解决方案:)