如何解决用户控件中的闪烁问题

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

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

但它没有帮助…每个控件具有相同的背景图像与不同的控件。 那么它的解决scheme是什么?
谢谢。

双缓冲可以解决的不是那种闪烁。 也不BeginUpdate或SuspendLayout。 你有太多的控制,BackgroundImage可以使它更糟糕。

它在UserControl绘制自己时开始。 它绘制BackgroundImage,留下儿童控制窗口去的地方。 然后每个孩子的控制都会得到一个消息来画自己,他们会用自己的窗口内容来填充这个洞。 当你有很多的控制,这些洞是用户可见的一段时间。 它们通常是白色的,与黑暗时的BackgroundImage形成鲜明对比。 或者,如果表单设置了“不透明度”或“透明度”属性,则它们可能会变黑,与其他任何东西形成鲜明对比。

这是Windows窗体的一个非常基本的限制,它被Windows呈现窗口的方式所困。 由WPF btw修复,它不使用窗口的子控件。 你想要的是双缓冲整个表单,包括子控件。 这是可能的,检查我的代码在这个线程的解决scheme。 虽然有副作用,但实际上并没有增加绘画的速度。 代码很简单,将其粘贴到您的表单中(而不是用户控件):

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

有很多事情可以做,以提高绘画速度,闪烁不再明显。 开始处理BackgroundImage。 当源图像很大并且需要缩小以适应控制时,它们可能非常昂贵。 将BackgroundImageLayout属性更改为“平铺”。 如果这样做会显着提高速度,请返回到您的绘画程序并调整图像大小,以便与典型的控制尺寸更好地匹配。 或者在UC的OnResize()方法中编写代码来创build图像的正确大小的副本,以便每次重新绘制控件时都不必重新resize。 对于该副本使用Format32bppPArgb像素格式,其渲染速度比其他任何像素格式快10倍。

接下来,你可以做的是防止洞如此明显,并与形象形成鲜明对比。 您可以closures UC的WS_CLIPCHILDREN样式标志,该标志阻止UC在子控件所在区域绘制。 将此代码粘贴到UserControl的代码中:

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

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

最后但同样重要的是,减less儿童控制的数量总是解决缓慢的绘画问题的好办法。 覆盖UC的OnPaint()事件并绘制现在在孩子中显示的内容。 特殊的标签和PictureBox是非常浪费的。 方便点和点击,但他们的轻量级select(绘制一个string或图像)只需要在您的OnPaint()方法中的一行代码。

这是一个真正的问题,Hans Passant给出的答案非常适合节省闪烁。 但是,他提到的副作用是丑陋的(丑陋的)。 如上所述,“您可以closuresUC的WS_CLIPCHILDREN样式标志”,但是只有closuresUC才能closures。 主窗体上的组件仍然有问题。

例如,面板滚动条不会绘制,因为它在技术上位于子区域中。 然而,子组件不会绘制滚动条,所以直到鼠标hover(或其他事件触发它)才会被绘制。

此外,animation图标(更改等待循环中的图标)不起作用。 删除tabPage.ImageKey上的图标不会适当调整/重新绘制其他TabPages。

所以我正在寻找一种方法来closuresWS_CLIPCHILDREN在初始绘画,所以我的表单将加载很好的画,或更好的,但只有打开它,同时调整我的窗体与大量的组件。

诀窍是让应用程序调用所需的WS_EX_COMPOSITED / WS_CLIPCHILDREN风格的CreateParams? 我发现这里有一个黑客( http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx )很好。 谢谢AngryHacker!

我以ResizeBegin事件的forms放置了TurnOnFormLevelDoubleBuffering()调用。 TurnOffFormLevelDoubleBuffering()以ResizeEnd事件的forms调用(或者在最初正确绘制后留下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; } 

如果你正在做的任何自定义绘画控制(即覆盖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); } 

并用属性NeedRepaint使您的控件失效

否则上面的SuspendLayout和ResumeLayout的答案可能是你想要的。

尝试BeginUpdate / EndUpdate或SuspendLayout / ResumeLayout方法。 见下文
如何解决嵌套的winform控件闪烁问题
在更新WinForms中的控件时闪烁(例如DataGridView)

在背景图片所在的主窗体或用户控件上,将BackgroundImageLayout属性设置为CenterStretch 。 当用户控件正在渲染时,您会注意到很大的不同。

我试图添加这个作为评论,但我没有足够的观点。 这是唯一有助于我的闪烁的问题,非常感谢汉斯的职位。 对于像我这样使用c ++构build器的人来说,这里就是翻译

将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); } 

reflection是诀窍! 把它放在你的构造函数或OnLoad事件中,如果你使用了一些具有子控件的自定义用户控件,你需要确保那些带有子控件的自定义控件也有这个function。

注意:不要在像OnPaint这样每秒执行数万亿次的事件中使用此function,因为reflection的安静性非常高。

说明:父级Control / Form将遍历表单中的所有控件,并将其属性设置为DoubleBuffered。

码:

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

只是增加了汉斯给出的答案:

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

如果WS_EX_COMPOSITED,DoubleBuffered和WS_CLIPCHILDREN没有解决你的闪烁(对我来说WS_CLIPCHILDREN使它更糟糕),试试这个:遍历所有的控件和所有的代码,以及任何你有任何透明或半透明的BackColor,ForeColor或任何其他颜色,只要删除它,只使用纯色。 在大多数情况下,你认为你只需要使用透明度,而不是。 重新devise您的代码和控件,并使用纯色。 我有可怕的,可怕的闪烁,程序运行缓慢。 一旦我删除透明度,它显着加快,并有0闪烁。

编辑:要进一步添加,我刚刚发现,WS_EX_COMPOSITED不必是窗口范围,它可以适用于特定的控件! 这节省了我很多麻烦。 只需要从您需要的任何控件inheritance自定义控件,并粘贴已经发布的覆盖WS_EX_COMPOSITED。 这样你只能在这个控件上得到低级双缓冲区,避免了应用程序其余部分的令人讨厌的副作用!

我知道这个问题很老,但是想给我一些经验。

我在使用.NET 4.0的Windows 8中使用覆盖OnPaint和/或OnPaintBackGround的窗体中出现了很多Tabcontrol闪烁的问题。

唯一的想法是在OnPaint覆盖中不使用 Graphics.DrawImage方法,换句话说,直接对PaintEventArgs提供的Graphics进行绘制,甚至画出所有的矩形,闪烁消失。 但是,如果调用DrawImage方法,即使画一个剪切的Bitmap(创build双缓冲),也会出现闪烁。

希望它有帮助!

我把这个闪烁修复和这个字体修复相结合,然后我不得不添加一些我自己的代码来启动一个计时器,以防止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(带图标)闪烁的东西。

不同的结果video:香草tabcontrol与tabcontrolex

http://gfycat.com/FineGlitteringDeermouse

PS。 你将需要设置HotTrack = true,因为这也修复了这个bug

你有没有尝试Control.DoubleBuffered属性?

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

此外, 这可能会有所帮助。

有没有需要任何双缓冲和所有的东西人…

简单的解决scheme…

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

这是整个应用程序唯一的最佳解决scheme。 看到代码放在主要的forms:

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