写了一个多线程的程序,界面添加了多个DataGridView但是在不同的TabPage里面,动态的按行添加数据但是只保留39行,超过39行后移除第一行在末尾再添加一行。由于添加数据频率比较快,会发生父窗体刷新窗体的时候出现部分界面刷新慢卡住,但DataGridView仍然在添加数据,线程操作DataGridView是通过Control.Invoke添加数据的。请大牛们帮忙分析出一个办法。如果有可以替代的不收费的控件也是可以的。

解决方案 »

  1.   

    设置双缓冲
    //设置窗体的双缓冲
                this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint, true);
                this.UpdateStyles();            InitializeComponent();            //利用反射设置DataGridView的双缓冲
                Type dgvType = this.dataGridView1.GetType();
                PropertyInfo pi = dgvType.GetProperty("DoubleBuffered",
                    BindingFlags.Instance | BindingFlags.NonPublic);
                pi.SetValue(this.dataGridView1, true, null);
      

  2.   

    双缓冲已经设置,现在DataGridView本身没有闪烁问题,就是界面上其他控件和滑动条会出现绘制不完全,和卡顿的现象
      

  3.   


    要不自定义个界面,给数据分个页,可以参考下
    https://www.cnblogs.com/qiaoke/p/8507183.html
    里面的UiPageCodeMethod.cs
      

  4.   

    第一次导入数据绑定数据源,后面一行一行加,比如这样
     int index = GetRowsIndexOfIPAndPort(tran.IP,rDgv_AllInfos,tran.Port);
                    if (tran != null && (index > 0))
                    {
                        rDgv_AllInfos.Rows[index].Cells["aIsConnect"].Value = tran.IsConnect;
                        rDgv_AllInfos.Rows[index].Cells["aClientIsInstal"].Value = tran.IsInstal;
                        rDgv_AllInfos.Rows[index].Cells["aMrUnningStatus"].Value = tran.RunState;
                        rDgv_AllInfos.Rows[index].Cells["aMBRAMofRmu"].Value = tran.RmuRam;
                        rDgv_AllInfos.Rows[index].Cells["aServRunType"].Value = tran.Runtype;
                    }
      

  5.   

    办法都给你说了,第一次赋值绑定数据源,后面的给行列赋值而不是重新绑定,就像下面这行代码
    rDgv_AllInfos.Rows[index].Cells["aIsConnect"].Value = tran.IsConnect;
      

  6.   

    Control.Invoke改成 Control.BeginInvoke==========
    欢迎关注微信公众号 “产品技术知与行” ,打造全面的结构化知识库,包括原创文章、免费课程(C#,Java,Js)、技术专题、视野知识、源码下载等内容。 
    最新文章:多种Timer的场景应用 https://mp.weixin.qq.com/s/TJKi7PBj3nznf9FClirXUA
      

  7.   

    winform draw 的卡几乎都是因为图片资源太大....压缩到1K你再试试看。
      

  8.   

    设置一个关联的整体制约机制,如果线程阻塞或者其它原因,令 DataGridView 暂不工作。
      

  9.   

    用RX.net 异步进行处理
      

  10.   

    @half_bucket遮罩的方式不理想因为不确定什么时候会绘图卡顿。@圣殿骑士18BeginInvoke有试过也是一样的效果
    @SoulRed RX.net能具体说说吗?
      

  11.   

    WinForm 的 DataGridView UI刷新 其实已经做得很好的 —— 一般不会因为 重绘不及而卡住。但 DataGridView 加载数据,好像确实有点慢。
    或许是加载数据方法不对,一个Cell 一个 Cell 的 加数据 确实或许会慢一些。我也替你找找 有没有更快的 加数据的方式。
      

  12.   

    DataGridView表格区没有出现重绘卡住,而是同一个界面上其他控件会发生卡顿
      

  13.   


            private void BindByDataSource(DataTable table)
            {
                //3800行 x 26列, 总计 10W 个单元格
                //直接用 DataSource 绑定, 耗时 0.09秒            DateTime time0 = DateTime.Now;
                dataGridView1.DataSource = table;
                DateTime time1 = DateTime.Now;
                Console.WriteLine(string.Format("通过 DataSource 绑定 {0}x{1} 数据, 耗时 {2}秒", table.Rows.Count, table.Columns.Count, (time1 - time0).TotalSeconds));
            }
            private void BindByCell(DataTable table)
            {
                DateTime time0 = DateTime.Now;            foreach (DataColumn col in table.Columns)
                {
                    dataGridView1.Columns.Add(col.ColumnName, col.ColumnName);
                }            //3800行 x 26列, 总计 10W 个单元格
                foreach (DataRow row in table.Rows)
                {
                    //只执行这行代码 耗时 0.6秒
                    int i = dataGridView1.Rows.Add();
                    var row2 = dataGridView1.Rows[i];                //通过 名称赋值, 耗时 0.73-0.6 = 0.13秒
                    foreach (DataColumn col in table.Columns)
                        row2.Cells[col.ColumnName].Value = row[col];                ////通过 索引赋值, 耗时 0.71-0.6 = 0.11秒
                    //for (int j = 0, c = table.Columns.Count; j < c; j++)
                    //    row2.Cells[j].Value = row[j];
                }
                DateTime time1 = DateTime.Now;
                Console.WriteLine(string.Format("通过 Cell 赋值 {0}x{1} 数据, 耗时 {2}秒", table.Rows.Count, table.Columns.Count, (time1 - time0).TotalSeconds));
            }
    DataGridView 很大一部分性能损失,是在  dataGridView1.Rows.Add(); 这行代码。我的示例  3800行 x 26列, 总计 10W 个单元格 
    —— 楼主的示例是 39行?? 我是真看不懂 性能会浪费在什么地方了。
      

  14.   

    首先程序里面有多个DataGridView,其中两个表是通过数据绑定,其他的表是按行添加更新,因为写的程序不只是绑定一次,而是频繁的更新绑定,每次的数据都是新的。大概200ms重新绑定一次数据。每个DataGridView在不同的TabPage里面
      

  15.   

    讨论了这么多,你是不是该贴一下你的重绘gridview的相关代码?不然可能就有问题呢?反正我是没碰到gridview有什么问题。我的代码是和硬件交互,刷新频率不低。
    而且双缓冲建议不要开启,记得看过一个帖子分析过,这是无奈之举,使用常规的异步比它好。
      

  16.   

    private void HandleUpdateDataLog()
            {
                GlobalStaticClass.DataLogViewThreadStopped = false;
                while (true)
                {
                    _DatalogThreadSem.WaitOne();
                    if (GlobalStaticClass.HandleStatisticThreadStopped && GlobalStaticClass.bCloseAllRequest)
                        break;
                    foreach (KeyValuePair<int, DataLogTableClass> _TempKeyValuePair in _DicSite2DataLogTableClass)
                    {
                        if (_DicSite2GridView.TryGetValue(_TempKeyValuePair.Key, out _DataGridView))
                        {
                            if (_TempKeyValuePair.Key == 0 )
                            {
                                continue;
                            }
                            else if (_TempKeyValuePair.Key == -1)//QA
                            {
                                this.Invoke(new Action(() => {
                                    foreach (KeyValuePair<int, DataLogTableClass> _QATempDataLogPair in _DicQASite2DataLogTableClass)
                                    {
                                        if (_QATempDataLogPair.Value._ForShowDataTable.Rows.Count > 1)
                                        {
                                            if (_DataGridView.Rows.Count >= (_FirstDataRowIndex + _KeepDataRowCount))
                                                _DataGridView.Rows.RemoveAt(_FirstDataRowIndex);
                                            _DataGridView.Rows.Add(_QATempDataLogPair.Value._ForShowDataTable.Rows[0].ItemArray);
                                            _QATempDataLogPair.Value._ForShowDataTable.Rows.RemoveAt(0);
                                        }
                                    }
                                    if (_DataGridView.Rows.Count > _FirstDataRowIndex)
                                        _DataGridView.FirstDisplayedScrollingRowIndex = _DataGridView.Rows.Count - 1;
                                }));
                                
                            }
                            else //Site 1,2,3..
                            {
                                this.Invoke(new Action(() =>
                                {
                                    _DataGridView.SuspendLayout();
                                    if (_DataGridView.Rows.Count >= (_FirstDataRowIndex + _KeepDataRowCount))
                                        _DataGridView.Rows.RemoveAt(_FirstDataRowIndex);
                                    _DatalogThreadMutex.WaitOne();
                                    if (_TempKeyValuePair.Value._ForShowDataTable.Rows.Count > 0)
                                    {
                                        _DataGridView.Rows.Add(_TempKeyValuePair.Value._ForShowDataTable.Rows[0].ItemArray);
                                        //_DataGridView.FirstDisplayedScrollingRowIndex = _DataGridView.Rows.Count - 1;
                                        // Monitor.Enter(_TempKeyValuePair.Value._ForShowDataTable);
                                        _TempKeyValuePair.Value._ForShowDataTable.Rows.RemoveAt(0);
                                        //Monitor.Exit(_TempKeyValuePair.Value._ForShowDataTable);
                                    }
                                    _DatalogThreadMutex.ReleaseMutex();
                                    if (_DataGridView.Rows.Count > _FirstDataRowIndex)
                                        _DataGridView.FirstDisplayedScrollingRowIndex = _DataGridView.Rows.Count - 1;
                                    _DataGridView.ResumeLayout();
                                }));
                            }
                        }
                    }
                }
                GlobalStaticClass.DataLogViewThreadStopped = true;
                
            }
    按行添加更新DataGridView
      

  17.   

     private void BindOnceDataSource(DataTable _ArgDataTable)
            {
                _DicSite2GridView[0].BeginInvoke(new Action(() =>
                {
                    _DicSite2GridView[0].DataSource = _ArgDataTable;
                }));
            }
    绑定数据源
      

  18.   

    线程只向全局变量List或dictionary中添加数据,添加时注意用lock锁下。界面0.5秒或1秒定时判断下全局变量是否有新数据,有就更新下界面显示,更新时,先调用DataGridView.BeginUpdate();   添加完数据行后,再DataGridView.EndUpdate();
      

  19.   

    看起来,虽然一些细节看不明白,但总体上,你这代码就很糟糕。1,while (true)
    p哥多次反复提到,不要采用死循环的方式来实现需求,应该多考虑用事件。基本可以肯定,你用死循环,效率肯定不高。2, 看到多个地方用到了 WaitOne(); 
    这种等待操作本来就是要等待的,这不就是卡界面的因素吗?但我没看明白WaitOne()的用途。但如果你解决了第一点,用事件代替死循环,就算有WaitOne,那也不会影响性能,因为它在UI刷新事件的驱动端,和事件的接收端已经分离。3、你还用了SuspendLayout,ResumeLayout
    真是什么重量级的东西都用上了,这些本来都是Designer.cs才会有的东西。我是从来不用这些,因为没必要。4、如果你的HandleUpdateDataLog方法是在独立线程里执行的,那么它里边怎么还有好几个this.Invoke()?还有SuspendLayout,ResumeLayout这些操纵UI的方法,那你在最后刷新grid的时候改BeginInvoke有什么用?所有涉及到界面的修改都应该在BeginInvoke里的!5、最后绑定数据源的方式,也是低效的。一般程序员只会这种,但要用的更好,这是不够的
    _DicSite2GridView[0].DataSource = _ArgDataTable;
    我都是用BindingList来实现部分更新,而不是整体替换数据源。总结起来感觉是:这个代码不忍看。效率低是正常的。==========
    欢迎关注微信公众号 “产品技术知与行” ,打造全面的结构化知识库,包括原创文章、免费课程(C#,Java,Js)、技术专题、视野知识、源码下载等内容。 
    最新文章:解读经典《C#高级编程》 第四章之继承.接口 https://mp.weixin.qq.com/s/fO4C0WAYZUJYEvKJiBCuVA
      

  20.   

    谢谢,因为是菜鸟么所以会的不多。
    1.用到死循环的是在单独的线程里面,信号量的WaitOne是为了没有数据的时候阻塞线程,互斥锁的WaitOne是因为线程同步。
    2.SuspendLayout,ResumeLayout有尝试过去掉,没有什么区别。
    3.因为HandleUpdateDataLog是单独线程所以就使用了Invoke
    4.最后一张表使用BeginInvoke是因为实在UI线程里绑定的数据源。
    5._DicSite2GridView[0].DataSource = _ArgDataTable;这个确实是第一次用,不是很了解BindingList还请您讲解一下
      

  21.   

    只要是涉及到绑定,线程更新、UI刷新,肯定会慢,我一般这种问题都是虚拟表实现,你的应用中,只显示最后N行,数据源可以一直累加,你只在绘制事件做对应就可以了,实际显示的哪一行完全由你来决定,例如table有100行,绘制事件要求你绘制第0行,你可以去100-N的行进行绘制
      

  22.   

    再说一句。你程序的问题,性能的问题,应该主要在于两点
    1. 使用了while(true)死循环2. 你对invoke的处理不彻底,貌似在gridview填充时使用了BeginInvoke,但其他的UI元素还用invoke,而且代码还是在线程内执行?这相当于白搞。总结一句话就是你的异步调用不彻底,所以没用。其他的,影响小,可能不一定是当前性能的瓶颈