马上加入IBC程序猿 各种源码随意下,各种教程随便看! 注册 每日签到 加入编程讨论群

C#教程 ASP.NET教程 C#视频教程程序源码享受不尽 C#技术求助 ASP.NET技术求助

【源码下载】 社群合作 申请版主 程序开发 【远程协助】 每天乐一乐 每日签到 【承接外包项目】 面试-葵花宝典下载

官方一群:

官方二群:

请教如何更好的更新某个winform窗体里面的某个控件的内容?

  [复制链接]
查看15668 | 回复11 | 2013-1-13 17:08:08 | 显示全部楼层 |阅读模式
我自己比较小白,我是通过,关闭整个winform窗体 然后new再次打开来更新,我感觉这样好笨啊,管理员提供几种跟新控件显示内容的方法,例如listbox textbox 如何单独更新他们显示的内容呢?我从数据库调用出来显示的,每次发送就希望更新一下这几个控件显示的内容!求帮助,谢谢啦!
尹兴飞 | 2013-1-13 17:11:40 | 显示全部楼层
一、通过对窗体和控件使用双缓冲来减少图形闪烁(当绘制图片时出现闪烁时,使用双缓冲)

对于大多数应用程序,.NET Framework 提供的默认双缓冲将提供最佳效果。默认情况下,标准 Windows 窗体控件是双缓冲的。可以通过两种方法对窗体和所创作的控件启用默认双缓冲。一种方法是将 DoubleBuffered 属性设置为 true,另一种方法是通过调用 SetStyle 方法将 OptimizedDoubleBuffer 标志设置为 true。两种方法都将为窗体或控件启用默认双缓冲并提供无闪烁的图形呈现。建议仅对已为其编写所有呈现代码的自定义控件调用 SetStyle 方法。

在构造函数里加上以下代码:
            this.DoubleBuffered = true;//设置本窗体
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景.
            SetStyle(ControlStyles.DoubleBuffer, true); // 双缓冲
            //SetStyle(ControlStyles.DoubleBuffer | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
            //UpdateStyles();

二、C#控件的闪烁问题解决方法总结
最近对代码作了一些优化,试验后效果还可以,但是发现界面会闪烁,具体是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)
      {
               // 此处书写过滤消息代码
      }
      但是实际实验显示无效果,不知是什么原因,没有细究。

三、个人在一个winfrom中测试利用timer控件对要刷新的控件进行定时刷新,可能也能起到作用。

四、C# winform 局部刷新
做winform界面程序时,经常会遇到后台处理占用大量时间的情况,这就会造成界面假死状态。一般解决界面假死有两种方式:要么把占用大量时间的处理方式放入其他线程;要么把界面显示放入其他线程。第一种方式应该比较简单,开单独的线程,处理数据,将处理数据显示到界面就好。但是我们经常需要在主程序运算一些内容,否则可能会改动比较大。因此,这里讲讲第二种方式。
同样是使用多线程,但是c#在其他线程刷新有一点点问题,即不能跨线程操作界面。这可以使用控件的Invoke方法解决:
                   private delegate void CrossThread();
                    Control control = ....;
                     CrossThread cross = delegate()
                     {
                         control.Refresh();
                     };
                     control.Invoke(cross);
这样可以让控件在其它线程刷新界面。
再加上开新线程后的通用方法:
        private void InvaliateControl(Control control)
        {
            Thread t = new Thread(
                 new ThreadStart(delegate()
                 {
                     CrossThread cross = delegate()
                     {
                         control.Refresh();
                     };
                     control.Invoke(cross);
                 }
             ));
        }
这样就可以在任何时候,调用此方法对控件进行刷新,而不将整个界面刷新。如果对于同一个控件,连续多次刷新,可以添加一个成员变量作为标记,以免同一控件连续多次刷新,提升部分性能。
补充:在主线程调用耗时操作用此方法可能会有问题,经过验证调用Invoke函数,其实是在主线程刷新界面。


这个貌似 太长了  我研究一下
ibcadmin | 2013-1-13 17:16:40 | 显示全部楼层
你这个太麻烦,

两个简单的方法    用listbox textbox做例子

第一个 用线程 (这个你没接触 就直接跳到第二个方法)

第二个  把呈现的方法单独写出来。 当你点击事件更新以后 直接调用一遍这个呈现的方法 就OK
C#论坛 www.ibcibc.com IBC编程社区
C#
C#论坛
IBC编程社区
※→/db | 2013-1-13 21:31:07 | 显示全部楼层
把呈现的方法单独写出来。 当你点击事件更新以后 直接调用一遍这个呈现的方法 就OK  这个实用!
尹兴飞 | 2013-1-13 21:36:40 | 显示全部楼层
ibcadmin 发表于 2013-1-13 17:16
你这个太麻烦,

两个简单的方法    用listbox textbox做例子

呈现的方法如何写?是new出来那个吗?
尹兴飞 | 2013-1-13 21:43:01 | 显示全部楼层
ibcadmin 发表于 2013-1-13 17:16
你这个太麻烦,

两个简单的方法    用listbox textbox做例子

线程如何刷新,举个例子,或者给一个案例看看 链接地址也行
ibcadmin | 2013-1-13 21:46:49 | 显示全部楼层
你把从获取数据库中的值 并且绑定到控件上的这个方法,  单独写个方法 就是新写个public 比如:
public void chengxian()
{
//以下就是呈现方法 就是把数据绑到控件上的方法。  省略了 你自己复制过来就行了


}

然后再你需要点击的事件中 调用这个方法。这个方法就会重新从数据库中取值 每点击一次 从获取一次。

这样就行了
C#论坛 www.ibcibc.com IBC编程社区
C#
C#论坛
IBC编程社区
IoveBC | 2013-2-27 18:14:54 | 显示全部楼层
不是吧,我觉得再来调用的话会有问题呀,难道再次调用的时候是默认清除原有数据吗?
ibcadmin | 2013-2-27 18:22:24 | 显示全部楼层
IoveBC 发表于 2013-2-27 18:14
不是吧,我觉得再来调用的话会有问题呀,难道再次调用的时候是默认清除原有数据吗?

如果你用了实体类的话 不会清除的  实体类就是刚刚所讲的Models层
C#论坛 www.ibcibc.com IBC编程社区
C#
C#论坛
IBC编程社区
IoveBC | 2013-2-27 18:32:58 | 显示全部楼层
哦,我还没有学到这个。。。。。。
*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则