如何修复用户控件中的闪烁

在我的应用程序中,我不断地从一个控件移动到另一个控件。我创造了没有。但是在导航过程中,我的控件会闪烁。它需要1或2秒来更新。我试过设置这个

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
or
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);

但它没有帮助... 每个控件有相同的背景图像与不同的控件。 那么解决办法是什么。
谢谢。

107294 次浏览

尝试 BeginUpdate/EndUpdate 或 susendLayout/ResumeLayout 方法。 请看下面
如何修复嵌套的 winform 控件闪烁问题
在更新 WinForms 中的控件期间闪烁(例如 DataGridView)

如果您正在控件中进行任何自定义绘制(即重写 OnPaint) ,您可以自己尝试双缓冲。

Image image;
protected override OnPaint(...) {
if (image == null || needRepaint) {
image = new Bitmap(Width, Height);
using (Graphics g = Graphics.FromImage(image)) {
// do any painting in image instead of control
}
needRepaint = false;
}
e.Graphics.DrawImage(image, 0, 0);
}

And invalidate your control with a property NeedRepaint

否则,使用 susendLayout 和 ResumeLayout 的上述答案可能就是您想要的。

你试过 Control.DoubleBuffered房产吗?

获取或设置一个值,该值指示此控件是否应使用辅助缓冲区重绘其表面以减少或防止闪烁。

这个这个也可能有帮助。

It is not the kind of flicker that double-buffering can solve. Nor BeginUpdate or SuspendLayout. You've got too many controls, the BackgroundImage can make it a 很多 worse.

It starts when the UserControl paints itself. It draws the BackgroundImage, leaving holes where the child control windows go. Each child control then gets a message to paint itself, they'll fill in the hole with their window content. When you have a lot of controls, those holes are visible to the user for a while. They are normally white, contrasting badly with the BackgroundImage when it is dark. Or they can be black if the form has its Opacity or TransparencyKey property set, contrasting badly with just about anything.

这是 Windows 窗体的一个非常基本的限制,它受制于 Windows 呈现窗体的方式。由 WPF 顺便修正,它不使用子控件窗口。您需要对整个窗体(包括子控件)进行双缓冲。这是可能的,检查我的代码在 这根线的解决方案。不过它有副作用,而且实际上并没有提高绘画速度。代码很简单,将其粘贴到表单中(不是用户控件) :

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

你可以做很多事情来提高绘画速度,直到闪烁不再明显。首先处理 BackoundImage。当源图像很大并且需要缩小以适应控件时,它们可能很昂贵。将 BackoundImageLayout 属性更改为“ Tile”。如果这给了一个明显的加速,返回到您的绘图程序,并调整图像的大小,以更好地匹配典型的控件大小。或者在 UC 的 OnResize ()方法中编写代码来创建一个适当大小的图像副本,这样就不必在每次重新绘制控件时调整图像的大小。对该副本使用 Format32bppPArgb 像素格式,它的渲染速度是其他任何像素格式的10倍左右。

下一步你可以做的是防止漏洞如此明显,并与图像对比很差。您可以 关掉 UC 的 WS _ CLIPCHILDREN 样式标志,该标志阻止 UC 在子控件所在的区域进行绘制。将此代码粘贴到 UserControl 的代码中:

protected override CreateParams CreateParams {
get {
var parms = base.CreateParams;
parms.Style &= ~0x02000000;  // Turn off WS_CLIPCHILDREN
return parms;
}
}

子控件现在将自己绘制在背景图像的顶部。你可能仍然会看到它们一个接一个地画自己,但是丑陋的中间白洞或黑洞是看不见的。

最后但同样重要的是,减少子控件的数量始终是解决绘制速度慢问题的一个好方法。覆盖 UC 的 OnPaint ()事件并绘制现在显示在子元素中的内容。特殊的标签和图片盒是 very浪费。方便的点和点击,但他们的轻量级替代(绘制字符串或图像)只需要一行代码在您的 OnPaint ()方法。

不需要任何双缓冲和所有的东西家伙..。

一个简单的解决办法。

如果您使用的是 MDI 接口,只需将下面的代码粘贴到主窗体中即可。它将删除所有闪烁的页面。然而,有些页面需要更多的时间加载将显示在1或2秒。但这总比显示一个闪烁的页面,其中每个项目一个接一个来好。

这是整个应用程序的唯一最佳解决方案:

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

再加上汉斯给出的答案:

(TLDR 版本: 透明度比你想象的要重,在任何地方只使用纯色)

如果 WS _ EX _ COMPOSITED,DoubleBuffered 和 WS _ CLIPCHILDREN 没有解决你的闪烁(对我来说 WS _ CLIPCHILDREN 使它更糟糕) ,试试这个: 通过所有的控件和所有的代码,无论你有任何透明或半透明的 BackColor,ForeColor,或任何其他颜色,只是删除它,只使用纯色。在大多数情况下,您认为使用 have就可以使用透明度,但实际上并非如此。重新设计代码和控件,并使用纯色。 我有可怕的,可怕的闪烁和程序运行缓慢。一旦我删除透明度,它显着加速,并有0闪烁。

编辑: 为了进一步补充,我刚刚发现 WS _ EX _ COMPOSITED 不一定是窗口范围的,它可以只应用于特定的控件!这给我省了不少麻烦。只需创建一个从您需要的任何控件继承的自定义控件,并粘贴已经发布的 WS _ EX _ COMPOSITED 覆盖。通过这种方式,您只能在此控件上获得低级别的双缓冲区,从而避免了应用程序其余部分的讨厌的副作用!

这是一个真实的问题,Hans Passant 给出的答案对于保存闪烁很有帮助。然而,正如他所提到的,有一些副作用,它们可能很丑陋(UI 丑陋)。如前所述,“您可以关闭的 UC 的 WS_CLIPCHILDREN样式标志”,但这只关闭了 UC。主窗体上的组件仍然有问题。

例如,一个面板滚动条不绘制,因为它在技术上是在子区域。但是子组件不绘制滚动条,所以直到鼠标移过(或其他事件触发)才会绘制它。

此外,动画图标(在等待循环中更改图标)不起作用。移除 tabPage.ImageKey上的图标并不能适当地调整/重新绘制其他的标签页。

因此,我正在寻找一种方法,以关闭 WS_CLIPCHILDREN的初始绘制,使我的形式将加载很好地绘制,或更好的是,只打开它,同时调整我的形式与许多组件。

诀窍是让应用程序使用所需的 WS_EX_COMPOSITED/WS_CLIPCHILDREN样式调用 CreateParams。我找到了一个黑客在这里(https://web.archive.org/web/20161026205944/http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx) ,它工作得很好。谢谢 AngryHacker!

我将 TurnOnFormLevelDoubleBuffering()调用放在表单 ResizeBegin事件中,将 TurnOffFormLevelDoubleBuffering()调用放在表单 ResizeEnd 事件中(或者在最初正确绘制后将其留在 WS_CLIPCHILDREN中)

    int originalExStyle = -1;
bool enableFormLevelDoubleBuffering = true;


protected override CreateParams CreateParams
{
get
{
if (originalExStyle == -1)
originalExStyle = base.CreateParams.ExStyle;


CreateParams cp = base.CreateParams;
if (enableFormLevelDoubleBuffering)
cp.ExStyle |= 0x02000000;   // WS_EX_COMPOSITED
else
cp.ExStyle = originalExStyle;


return cp;
}
}


public void TurnOffFormLevelDoubleBuffering()
{
enableFormLevelDoubleBuffering = false;
this.MaximizeBox = true;
}

在背景图像所在的主窗体或用户控件上,将 BackgroundImageLayout属性设置为 CenterStretch。当用户控件呈现时,您将注意到一个很大的差异。

我知道这个问题已经很老了,但是我想给出我的经验。

在 Windows 8中使用重写 OnPaint和/或 OnPaintBackGround的表单时,我遇到了很多 Tabcontrol闪烁的问题。NET 4.0.

唯一起作用的想法是 不要使用Graphics.DrawImage方法在 OnPaint覆盖,换句话说,当绘制被直接做到由 PaintEventArgs提供的图形,甚至绘制所有的矩形,闪烁消失。但是如果调用 DrawImage方法,甚至绘制一个剪辑过的 Bitmap (为双缓冲创建) ,闪烁就会出现。

希望能有帮助!

我将 这个闪烁修复这个字体修正组合在一起,然后我必须添加一些我自己的代码来启动一个油漆计时器来使 TabControl 在离开屏幕和返回时失效,等等。.

这三个都是:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class TabControlEx:TabControl
{
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
private const int WM_PAINT = 0x0f;
private const int WM_SETFONT = 0x30;
private const int WM_FONTCHANGE = 0x1d;
private System.Drawing.Bitmap buffer;
private Timer timer = new Timer();
public TabControlEx()
{
timer.Interval = 1;
timer.Tick += timer_Tick;
this.SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
}
void timer_Tick(object sender, EventArgs e)
{
this.Invalidate();
this.Update();
timer.Stop();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_PAINT) timer.Start();
base.WndProc(ref m);
}
protected override void OnPaint(PaintEventArgs pevent)
{
this.SetStyle(ControlStyles.UserPaint, false);
base.OnPaint(pevent);
System.Drawing.Rectangle o = pevent.ClipRectangle;
System.Drawing.Graphics.FromImage(buffer).Clear(System.Drawing.SystemColors.Control);
if (o.Width > 0 && o.Height > 0)
DrawToBitmap(buffer, new System.Drawing.Rectangle(0, 0, Width, o.Height));
pevent.Graphics.DrawImageUnscaled(buffer, 0, 0);
this.SetStyle(ControlStyles.UserPaint, true);
}


protected override void OnResize(EventArgs e)
{
base.OnResize(e);
buffer = new System.Drawing.Bitmap(Width, Height);
}
protected override void OnCreateControl()
{
base.OnCreateControl();
this.OnFontChanged(EventArgs.Empty);
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
IntPtr hFont = this.Font.ToHfont();
SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1));
SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
this.UpdateStyles();
}
}

我不是创造者,但从我的理解,位图做所有的错误绕过。

这是唯一能够明确解决我的 TabControl (带图标)闪烁问题的方法。

不同结果视频: 香草制片控制与制片控制

Http://gfycat.com/fineglitteringdeermouse

您需要设置 HotTrack = true,因为这也修复了那个 bug

我试图添加这个作为一个评论,但我没有足够的点。这是唯一的事情,曾经帮助我闪烁的问题这么多感谢汉斯为他的职位。对于任何像我一样使用 c + + 构建器的人,这里有一个翻译

将 CreateParams 声明添加到应用程序的主表单. h 文件中,例如。

class TYourMainFrom : public TForm
{
protected:
virtual void __fastcall CreateParams(TCreateParams &Params);
}

并将其添加到. cpp 文件中

void __fastcall TYourMainForm::CreateParams(TCreateParams &Params)
{
Params.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
TForm::CreateParams(Params);
}

将下面的代码放在你的构造函数或 OnLoad 事件中,如果你正在使用某种拥有子控件的自定义用户控件,你需要确保这些自定义控件也是双缓冲的(即使在 MS 文档中他们说默认设置为 true)。

如果您正在制作一个自定义控件,您可能需要将这个标志添加到 ctor 中:

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

您可选择在表格/控件中使用此代码:

foreach (Control control in Controls)
{
typeof(Control).InvokeMember("DoubleBuffered",
BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, control, new object[] { true });
}

我们遍历表单/控件中的所有控件并访问它们的 DoubleBuffered属性,然后将其更改为 true,以使表单上的每个控件都得到双缓冲。我们在这里做反射的原因,是因为想象你有一个控件,它有无法访问的子控件,这样,即使它们是私有控件,我们仍然会将它们的属性更改为 true。

More information about double buffering technique can be found here.

为了解决这个问题,我通常会重写另一个属性:

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

WS_EX_COMPOSITED - Paints all descendants of a window in bottom-to-top painting order using double-buffering.

You can find more of these style flags 给你.

希望能帮上忙!