WinForm 窗体闪烁怎么办
2022 年 6 月 9 日 · 1 分钟读完
工作中遇到的窗体闪烁问题,记在这里,遇到新的再补充。
关闭 ShowDialog 的窗体时主窗体闪烁

关闭一个用 ShowDialog() 弹出的窗体后,主窗体快速闪了一下(动图里一闪而过的黑色是后面的 VS 调试界面)。这个弹窗隐藏了 WinForm 自带的边框,关闭按钮是自己写的:
private void btnClose_Click(object sender, EventArgs e)
{
this.Close();
this.Dispose(); // 闪烁的原因在这里
}
把 Dispose 注释掉就正常了。
原因:对话框关闭时,Windows 要把激活状态交还给主窗体。在 Close 之后立刻 Dispose,窗口句柄在对话框还没退出完的时候就被销毁,焦点先跳到了别的窗口(VS),再回到主窗体,于是闪了一下。
那不 Dispose 会不会内存泄漏?会有一点:用 ShowDialog() 显示的窗体关闭时不会自动释放(用 Show() 显示的会),因为调用方可能还要读它上面的值。正确做法是由调用方在用完后释放:
using (var frm = new FrmBinAdd(binBox))
{
if (frm.ShowDialog() == DialogResult.OK)
{
// 读取 frm 上用户填的值
}
} // 这里自动 Dispose
弹窗里的关闭按钮只需要设置 DialogResult 或者 Close(),不要自己 Dispose 自己。
延伸:其他几种常见闪烁
自己绘图时闪烁
在 Paint 里画很多东西(图表、示意图、实时曲线)时,每次刷新都会先擦背景再画,看起来一闪一闪。打开双缓冲:先画在内存里,画完一次性贴到屏幕上。
public FrmChart()
{
InitializeComponent();
this.DoubleBuffered = true;
// 自定义控件里的等价写法
// SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
}
控件很多,窗体打开或缩放时闪烁
DoubleBuffered 只对窗体自己的绘制有效,管不到子控件。控件多、带背景图的窗体,可以在窗体里重写 CreateParams,让 Windows 合成整个窗口后再显示:
protected override CreateParams CreateParams
{
get
{
var cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
return cp;
}
}
副作用是某些控件(比如嵌入的相机预览、Halcon 窗口)刷新会变慢或不刷新,有这类控件时慎用。
批量增删控件时闪烁
动态加很多控件、或者改大量布局时,先暂停布局,改完再统一计算一次:
panel1.SuspendLayout();
for (int i = 0; i < 50; i++) panel1.Controls.Add(new Button { Text = i.ToString() });
panel1.ResumeLayout();
DataGridView 大量更新数据时同理,先解绑或者把 DoubleBuffered 通过反射打开(它的这个属性是 protected 的)。
背景图
BackgroundImage 用大图并且 BackgroundImageLayout = Stretch 时,每次重绘都要缩放,很慢。把图片提前缩放成窗体大小,布局设为 None。
原文首发于 blog.csdn.net