Roy*_*son 105 c# user-controls flicker winforms
在我的应用程序中,我不断从一个控件移动到另一个控件.我创造了没有.用户控件,但在导航过程中我的控件闪烁.更新需要1或2秒.我试着设置这个
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
or
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
Run Code Online (Sandbox Code Playgroud)
但它没有帮助......每个控件都有相同的背景图像和不同的控件.那么它的解决方案是什么..
谢谢.
Han*_*ant 300
双缓冲可以解决的不是那种闪烁.也不是BeginUpdate或SuspendLayout.你有太多的控制,和backgroundImage可以把它很多更坏.
它在UserControl自己绘制时启动.它绘制了BackgroundImage,在子控件窗口的位置留下了空洞.然后每个子控件都会收到一条消息来绘制自己,他们将用他们的窗口内容填充这个洞.当您有很多控件时,用户可以看到这些漏洞一段时间.它们通常是白色的,在黑暗时与BackgroundImage形成鲜明对比.或者如果表单具有Opacity或TransparencyKey属性设置,它们可能是黑色的,几乎与任何事物形成鲜明对比.
这是Windows窗体的一个非常基本的限制,它坚持Windows呈现窗口的方式.由WPF btw修复,它不使用窗口进行子控件.您想要的是对整个表单进行双缓冲,包括子控件.这是可能的,请在此主题中检查我的代码以获得解决方案.它虽然有副作用,但实际上并没有提高绘画速度.代码很简单,将其粘贴到您的表单中(而不是用户控件):
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
Run Code Online (Sandbox Code Playgroud)
你可以做很多事情来提高绘画速度,以至于闪烁不再明显.首先解决BackgroundImage问题.当源图像很大并且需要缩小以适应控件时,它们可能非常昂贵.将BackgroundImageLayout属性更改为"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;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,子控件将自己绘制在背景图像的顶部.你可能仍然会看到他们一个接一个地画自己,但丑陋的中间白洞或黑洞是不可见的.
最后但同样重要的是,减少子控件的数量始终是解决慢速绘画问题的好方法.覆盖UC的OnPaint()事件并绘制现在在孩子中显示的内容.特别是Label和PictureBox 非常浪费.方便点和点击但是它们的轻量级替代(绘制字符串或图像)在OnPaint()方法中只需要一行代码.
这是一个真正的问题,Hans Passant给出的答案非常适合保存闪烁.然而,正如他所提到的那样,有副作用,而且它们可能很难看(UI难看).如上所述,"您可以关闭UC的WS_CLIPCHILDREN样式标志",但这只会为UC关闭它.主表单上的组件仍然存在问题.
例如,面板滚动条不会绘制,因为它在技术上位于子区域.但是子组件不会绘制滚动条,因此在鼠标悬停(或其他事件触发)之前它不会被绘制.
此外,动画图标(在等待循环中更改图标)不起作用.删除tabPage.ImageKey上的图标不会适当地调整其他tabPages的大小/重绘.
所以我一直在寻找一种方法来关闭初始绘制时的WS_CLIPCHILDREN,这样我的Form将加载很好的绘制,或者更好,但只有在用大量组件调整表单大小时才打开它.
诀窍是让应用程序使用所需的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事件的形式调用TurnOnFormLevelDoubleBuffering()调用.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;
}
Run Code Online (Sandbox Code Playgroud)
如果您在控件中进行任何自定义绘制(即重写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);
}
Run Code Online (Sandbox Code Playgroud)
并使用属性使控件无效 NeedRepaint
否则,SuspendLayout和ResumeLayout的上述答案可能就是您想要的.
小智 5
将下面的代码放入构造函数或 OnLoad 事件中,如果您使用某种具有子控件的自定义用户控件,则需要确保这些自定义控件也是双缓冲的(即使在 MS 文档中他们说默认情况下设置为 true)。
如果您正在制作自定义控件,您可能需要将此标志添加到您的构造函数中:
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
Run Code Online (Sandbox Code Playgroud)
您可以选择在表单/控件中使用此代码:
foreach (Control control in Controls)
{
typeof(Control).InvokeMember("DoubleBuffered",
BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, control, new object[] { true });
}
Run Code Online (Sandbox Code Playgroud)
我们迭代表单/控件中的所有控件并访问它们的DoubleBuffered
属性,然后将其更改为 true 以使表单上的每个控件都进行双缓冲。我们在这里进行反射的原因是,假设您有一个控件,该控件具有不可访问的子控件,这样,即使它们是私有控件,我们仍然会将其属性更改为 true。
我通常会重写另一个属性来解决这个问题:
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x00000020; // WS_EX_COMPOSITED
return parms;
}
}
Run Code Online (Sandbox Code Playgroud)
WS_EX_COMPOSITED
- 使用双缓冲按从下到上的绘制顺序绘制窗口的所有后代。
您可以在此处找到更多此类样式标志。
希望有帮助!
归档时间: |
|
查看次数: |
90944 次 |
最近记录: |