解决Winform应用程序中窗体背景闪烁的问题

最近对代码作了一些优化,试验后效果还可以,但是发现界面会闪烁,具体是TreeView控件会闪烁,语言为C#,IDE为VS2005。在查阅一些资料,使用了一些基本技术后(如开启双缓冲),发现没什么效果。

        于是使用Profiler工具,查找出瓶颈在于每次更新完界面的EndUpdate操作(使用这个是为了减少界面更新次数,但这里不理想是因为控件中中的元素很多),猜想大概每次更新,.Net底层都会更新重绘每个图元,所以速度会慢,造成闪烁。但是如果这样,使用双缓冲应该会有较好效果。再看代码,发现可能是更新动作太过频繁,于是降低速度,有所好转,但还是不行。

       继续在网上查阅,最终找到一个方案比较合适。原来底层重绘每次会清除画布,然后再全部重新绘制,这才是导致闪烁最主要的原因。于是重载消息发送函数操作,禁掉这条消息。代码如下:

        protected override void WndProc(ref Message m)

        {

            if (m.Msg == 0x0014) // 禁掉清除背景消息

                return;

            base.WndProc(ref m);

        }

        成功!

注:双缓冲还是有用的,在更新不是很频繁且控件内含元素不是特别多的时候。一旦元素过多,每次更新时间都比较长,即便使用了双缓冲,仍解决不了闪烁问题。个人认为最终比较理想的方法还是禁掉清除背景消息。

附:一些尝试过但失败的记录

1)使用setStyle

      网上有说使用setStyle函数去设置该控件的参数,具体为:

      SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);

      这三个选项参数后者是依赖前者的,必须并存,否则无效。并且这个函数本身是protected的,所以首先需要继承某控件再使用。

      这个目标是跟前面正确解决方案一致,也是禁止清除背景并开启双缓冲,但需要使用用户绘制选项,而且是全部交由用户绘制。这需要自己实现控件的全部绘制,比较麻烦。所以这个方法不是完全不可行,但是需要额外工作量,不推荐。我也没有使用。

2)使用BeginUpdate和EndUpdate

      这一对操作对于需要批量操作更新控件的情景有比较好的效果,比如初始化时批量添加了大量节点。坏处就在于不能即时更新。所以,对于频繁的更新节点并希望立即反映到界面的情况不适用。如果使用并且没有禁掉清除界面消息的话,则控件看起来就会不停的闪烁,而且以白底为主,内容几乎不可见(这个视频繁程度而定)。因为界面更新都在EndUpdate处完成,操作太多导致EndUpdate阻塞时间过长,且清空在先,更新在后,导致界面看起来长时间处于空白状态。

3)使用ControlStyles.EnableNotifyMessage选项

      这个选项的作用和正确解决方案也是一致的。使用方法是:

      SetStyle(ControlStyles.EnableNotifyMessage, true);

      protected override void onNotifyMessage(Message m)

      {

               // 此处书写过滤消息代码

      }

      但是实际实验显示无效果,不知是什么原因,没有细究。



我的操作系统是Win7,使用的VS版本是VS2012,文中的代码都是C#代码。

这几天遇到一个问题,即我用一个嵌入图片的Panel作为Winform应用程序的背景,如下图所示:

这是一个Winform窗体,里面放置了一个Panel,Dock属性为Fill,BackgroundImage使用了《少年电世界》2003年第02期的封面图片,BackgroundImageLayout使用了Stretch。

这个界面现在有两个问题:

1、在窗体第一次被打开时,背景图片会出现明显的闪烁

2、在拉动窗体的边界以调整窗体大小时,背景图片非出现明显的闪烁

为了处理这一问题,我查了一些资料,也都逐个试过了,下面先说下其中的两个有代表性方法:

方法1:直接使用双缓冲

SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景.
SetStyle(ControlStyles.DoubleBuffer, true); // 双缓冲

我尝试着将这段代码加到窗体的构造函数中,并不能解决问题,闪烁依然非常明显

在MSDN上还有一篇文章《如何通过对窗体和控件使用双缓冲来减少图形闪烁》

地址:https://msdn.microsoft.com/zh-cn/library/3t7htc9c%28v=vs.80%29.aspx

这篇文章中也介绍了一个方法使用双缓冲:

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

这个方法依然不能解决问题

方法2:重写CreateParams方法

方法2需要将以下这段代码放在Form类的代码内

protected override CreateParams CreateParams
{
    get
    {
        CreateParams paras = base.CreateParams;
        paras.ExStyle |= 0x02000000;用双缓冲绘制窗口的所有子控件
        return paras;
    }
}

这个方法我一开始尝试的时候一度认为是有效的,但使用了一段时间后还是发现了问题:

1、这个方法可以解决问题1,但不能解决问题2

2、这个方法会影响一些其他控件、组件的重绘(这点才是致命的)

因此,这个方法也不能解决问题

上面两个方法都不能解决问题,于是我继续求助度娘,终于在下面这个页面找到了解决方法:

方法3:封装Panel类

http://blog.chinaunix.net/uid-14414741-id-2814313.html

这个方法,需要新建一个PanelEnhanced类继承Panel类,代码如下:

/// <summary>
/// 加强版 Panel
/// </summary>
class PanelEnhanced : Panel
{
    /// <summary>
    /// OnPaintBackground 事件
    /// </summary>
    /// <param name="e"></param>
    protected override void OnPaintBackground(PaintEventArgs e)
    {
        // 重载基类的背景擦除函数,
        // 解决窗口刷新,放大,图像闪烁
        return;
    }

    /// <summary>
    /// OnPaint 事件
    /// </summary>
    /// <param name="e"></param>
    protected override void OnPaint(PaintEventArgs e)
    {
        // 使用双缓冲
        this.DoubleBuffered = true;
        // 背景重绘移动到此
        if (this.BackgroundImage != null)
        {
            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            e.Graphics.DrawImage(
                this.BackgroundImage,
                new System.Drawing.Rectangle(0, 0, this.Width, this.Height),
                0,
                0,
                this.BackgroundImage.Width,
                this.BackgroundImage.Height,
                System.Drawing.GraphicsUnit.Pixel);
        }
        base.OnPaint(e);
    }
}

将之前我们建立窗体中的Panel容器换为我们新封装的PanelEnhanced容器,将程序的背景图片放到里面,再运行程序,程序背景闪烁的问题就完美解决了!

END

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
C知道:C# WinForm应用程序是使用C#语言和WinForms框架构建的Windows桌面应用程序。下面是一些关于C# WinForm应用程序的基本概念和特点: 1. (Form):WinForm应用程序的主要界面是,它是用户与应用程序进行交互的主要界面元素。你可以在上添加各种控件来构建用户界面,如按钮、文本框、标签等。 2. 控件(Controls):WinForm提供了一系列预定义的控件,用于构建用户界面。你可以将这些控件拖放到上,并通过属性口来设置它们的属性和行为。一些常见的控件包括按钮(Button)、文本框(TextBox)、标签(Label)、列表框(ListBox)等。 3. 事件处理(Event Handling):WinForm应用程序通过事件处理来响应用户的操作。你可以为控件的事件(如按钮点击事件)编写事件处理程序,当事件发生时,相应的代码将被执行。例如,你可以在按钮点击事件中执行某个操作或打开另一个。 4. 数据绑定(Data Binding):WinForm支持简单的数据绑定,使你能够将数据源与控件进行绑定,从而实现数据的显示和更新。你可以使用数据绑定来将数据库、集合或其他数据源中的数据显示在控件中,或者将用户输入的数据保存到数据源中。 5. 对话框(Dialogs):WinForm提供了一些预定义的对话框,用于与用户进行交互,如消息框、文件选择框、颜色选择框等。你可以使用这些对话框来获取用户输入、显示提示信息或执行一些特定的操作。 6. 多线程支持(Multithreading Support):WinForm应用程序可以利用多线程来处理耗时操作,以保持界面的响应性。你可以使用后台线程来执行长时间运行的任务,而不会阻塞主线程,从而使用户能够继续与应用程序进行交互。 总而言之,C# WinForm应用程序是一种使用C#语言和WinForms框架构建的Windows桌面应用程序。它提供了一种简单而直观的方式来创建用户界面,并通过事件处理和数据绑定来实现交互和数据操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值