Windows Forms应用程序中的闪烁问题

22

我的应用程序有很多控件,尤其是在启动时闪烁非常严重。

我对它应用了这个修复方法

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

这个方法很好用 - 闪烁现象减少了很多。然而,副作用是窗口右上角的最小化、最大化和关闭按钮在鼠标移动或点击时不会有动画效果(但它们仍然可以使用)。这给应用程序带来了一种挂起的感觉。

如何保持WS_EX_COMPOSITED的同时仍保留最大化、最小化和关闭按钮的可用性?

这个问题出现在Windows XP上。正如@fallenidol所指出的那样,在Windows 7上不存在这个问题。


6
完美问题(我想要这个功能,我已经尝试了这个解决方案,如何在这个新的限制条件下实现这个功能)。 - Matt Mitchell
顺便提一下,我在运行在Windows 7上的测试应用程序中尝试了这个修复方法,但最大化、最小化和关闭按钮似乎仍然会动画显示。 - pmcilreavy
@fallenidol。知道这点真是太好了。我所有的客户都在使用XP操作系统。 - AngryHacker
1
我知道这是一个旧帖子,但当在Win 7 SP1上启用WS_EX_COMPOSITED时,最大化、最小化和关闭按钮上的动画消失了。 - Ignacio
5个回答

14

@Hossein,是的,博客现在已经关闭了...希望这个月能够恢复。 - AngryHacker
3
同时,在这里获取它:https://web.archive.org/web/20160405111506/http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx - AngryHacker
嗨@AngryHacker!我尝试了你建议的方法,但在Win 10中使用MDI表单时出现闪烁问题。在Win 7中,最小化、恢复和关闭按钮上的动画仍然无法正常工作。你能否给我一些提示?非常感谢! - Ignacio

8
我知道这个问题有点老了,但是迟到总比不到好。我使用您提供的原始示例来创建一个在调整大小时切换它的示例,然后将其切换回来以完美地绘制其他所有内容。希望它能帮助其他人寻找解决此问题的方法。正如OP所知,仅使用DoubleBuffering属性无法解决闪烁问题。
以下是一个解决方案,可以在用户调整窗体大小时停止闪烁,但不会破坏诸如DataGridView、NumericUpDown等控件的绘制。假设您的窗体名称为“Form1”:
int intOriginalExStyle = -1;
bool bEnableAntiFlicker = true;

public Form1()
{
    ToggleAntiFlicker(false);
    InitializeComponent();
    this.ResizeBegin += new EventHandler(Form1_ResizeBegin);
    this.ResizeEnd += new EventHandler(Form1_ResizeEnd);
}

protected override CreateParams CreateParams
{
    get
    {
        if (intOriginalExStyle == -1)
        {
            intOriginalExStyle = base.CreateParams.ExStyle;
        }
        CreateParams cp = base.CreateParams;

        if (bEnableAntiFlicker)
        {
            cp.ExStyle |= 0x02000000; //WS_EX_COMPOSITED
        }
        else
        {
            cp.ExStyle = intOriginalExStyle;
        }

        return cp;
    }
} 

private void Form1_ResizeBegin(object sender, EventArgs e)
{
    ToggleAntiFlicker(true);
}

private void Form1_ResizeEnd(object sender, EventArgs e)
{
    ToggleAntiFlicker(false);
}

private void ToggleAntiFlicker(bool Enable)
{
    bEnableAntiFlicker = Enable;
    //hacky, but works
    this.MaximizeBox = true;
}

好的,你说得对。我在我的应用程序中有这段代码,但是忘记在博客文章中添加它了。我会在几天内更新它。 - AngryHacker
不错,但如果我应用这个解决方案,那么当窗体被调整大小时,闪烁问题会再次出现。如果我只是像问题中使用CreateParams,即使窗体被调整大小,闪烁也会消失。也许我漏掉了什么... - Ignacio

4
请尝试以下代码。这应该放在主表单和任何其他自定义用户控件中。
        // Enable double duffering to stop flickering.
        this.SetStyle(ControlStyles.DoubleBuffer, true);
        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        this.SetStyle(ControlStyles.UserPaint, true);
        this.SetStyle(ControlStyles.SupportsTransparentBackColor, false);
        this.SetStyle(ControlStyles.Opaque, false);
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        this.SetStyle(ControlStyles.ResizeRedraw, true);

我发布这段代码的整个意图是让你只需要执行一次,而不是针对每个单独的用户控件(其中有数百个+大量的第三方控件)。 - AngryHacker
3
对于那些可能无法访问整个应用程序且只是在单独开发用户控件的其他人,这是另一个选择。下次您开发单个用户控件时,可以使用我上面的代码片段。这样就不会出现数百个闪烁控件的情况了。 - pmcilreavy

0

我刚看到这篇文章,意识到它有点旧了。然而,我遇到了与我的表单相同的问题,并发现(至少对于XP来说)一个不太优雅的解决方案似乎是不启用视觉样式。


作为后续,当我重写OnPaintBackground时,似乎没有闪烁问题。如果我不想绘制背景,我会调用e.Graphics.Clear([适当的颜色])并返回,否则将调用基本事件方法。正如我所说,这“似乎”缓解了问题。然而,我在想会有什么未预见的后果出现。 - Tebc
更正一下。上述的覆盖似乎只有在窗体的最小大小属性被设置为当前大小时才起作用。 - Tebc

0

那只在每个控件的基础上起作用。我提供的示例将双缓冲强制应用于表单上的每个控件。 - AngryHacker
你可以在应用程序开始时使用反射来填充每个控件的此属性。 - Kru
我无法为不公开此属性的第三方控件执行此操作。 - AngryHacker
嗯,任何第三方控件都必须从Windows.Forms.Control继承。即使此属性已关闭,您仍然可以使用反射访问任何私有或受保护的属性。 - Kru

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接