频繁刷新DataGridView的DataSource会报错

在winform开发中,我们常常会用DataGridView来展现datatable的数据,单次或者不频繁的异步设置其DataSource属性不会有任何问题,但是如果实时的异步刷新其数据,不管你是通过异步的调用方式还是采用直接修改数据源,都会导致DataGridView报莫名其妙的错误。

比如下面的应用场景,我们需要一个客户端来显示当前CTI服务器的电话排队情况,这种数据是很实时的。针对这种应用场景,我们有一下几种实现方法,但均会不定期报错

其一:

直接设置DataGridView的DataSource属性,然后直接在监听线程里面修改数据源,模拟程序如下

public Form1()
        {
            InitializeComponent();
            source = new DataTable();
            source.Columns.Add("CA");
            source.Columns.Add("CB");
            source.Columns.Add("CC");
        }

        DataTable source = null;
        Thread thmain = null;
        bool run = true;
        private void Form1_Load(object sender, EventArgs e)
        {
            dataGridView1.DataSource = source;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            thmain = new Thread(new ThreadStart(main));
            thmain.Start();
        }

        private void main()
        {
            while (run)
            {
                Thread.Sleep(500);
                if (source.Rows.Count >= 10)
                {
                    source.Rows.Clear();
                }
                DataRow dr = source.NewRow();
                dr["CA"] = "A-" + DateTime.Now.ToFileTime();
                dr["CB"] = "B-" + DateTime.Now.ToFileTime();
                dr["CC"] = "C-" + DateTime.Now.ToFileTime();
                source.Rows.Add(dr);
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            run = false;
        }
此方法会导致GV报如下的错误:


其二:

仍然直接设置datasource属性,但是是通过异步invoke的方式实现

this.Invoke(new UpdateUI(delegate(object o)
{
    gvPhoneList.DataSource = phoneList;
}), "");
通过在线程中使用invoke的方式来设置datasource属性,但仍然会报如下的错误:

System.NullReferenceException: 未将对象引用设置到对象的实例。
   在 System.Windows.Forms.DataGridViewLinkCell.PaintPrivate(Graphics g, Rectangle clipBounds, Rectangle cellBounds, Int32 rowIndex, DataGridViewElementStates cellState, Object formattedValue, String errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts, Boolean computeContentBounds, Boolean computeErrorIconBounds, Boolean paint)
   在 System.Windows.Forms.DataGridViewLinkCell.GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, Int32 rowIndex)
   在 System.Windows.Forms.DataGridViewCell.GetContentBounds(Int32 rowIndex)
   在 System.Windows.Forms.DataGridViewLinkCell.MouseMoveUnsharesRow(DataGridViewCellMouseEventArgs e)
   在 System.Windows.Forms.DataGridView.OnCellMouseMove(DataGridViewCellMouseEventArgs e)
   在 System.Windows.Forms.DataGridView.UpdateMouseEnteredCell(HitTestInfo hti, MouseEventArgs e)
   在 System.Windows.Forms.DataGridView.OnMouseMove(MouseEventArgs e)
   在 System.Windows.Forms.Control.WmMouseMove(Message& m)
   在 System.Windows.Forms.Control.WndProc(Message& m)
   在 System.Windows.Forms.DataGridView.WndProc(Message& m)
   在 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   在 System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
后来在CSDN上找到了另外的一种方法,但是仍然会有错,只是错误的消息不一样了而已:

System.NullReferenceException: 未将对象引用设置到对象的实例。
   在 System.Data.DataView.GetRecord(Int32 recordIndex)
   在 System.Data.DataView.GetRow(Int32 index)
   在 System.Data.DataView.System.Collections.IList.get_Item(Int32 recordIndex)
   在 System.Windows.Forms.CurrencyManager.get_Item(Int32 index)
   在 System.Windows.Forms.DataGridView.DataGridViewDataConnection.GetError(Int32 boundColumnIndex, Int32 columnIndex, Int32 rowIndex)
   在 System.Windows.Forms.DataGridViewCell.GetErrorText(Int32 rowIndex)
   在 System.Windows.Forms.DataGridViewLinkCell.GetErrorIconBounds(Graphics graphics, DataGridViewCellStyle cellStyle, Int32 rowIndex)
   在 System.Windows.Forms.DataGridViewCell.GetErrorIconBounds(Int32 rowIndex)
   在 System.Windows.Forms.DataGridViewCell.UpdateCurrentMouseLocation(DataGridViewCellMouseEventArgs e)
   在 System.Windows.Forms.DataGridViewCell.OnMouseMoveInternal(DataGridViewCellMouseEventArgs e)
   在 System.Windows.Forms.DataGridView.OnCellMouseMove(DataGridViewCellMouseEventArgs e)
   在 System.Windows.Forms.DataGridView.UpdateMouseEnteredCell(HitTestInfo hti, MouseEventArgs e)
   在 System.Windows.Forms.DataGridView.OnMouseMove(MouseEventArgs e)
   在 System.Windows.Forms.Control.WmMouseMove(Message& m)
   在 System.Windows.Forms.Control.WndProc(Message& m)
   在 System.Windows.Forms.DataGridView.WndProc(Message& m)
   在 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   在 System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

最后,我们尝试换一种方法来绑定数据:即手动设置数据

this.BeginInvoke(new System.Action(() =>
                {
                    gvPhoneList.Rows.Clear();
                    foreach (DataRow dr in phoneList.Rows)
                    {
                        gvPhoneList.Rows.Add(dr["NUMBER"], dr["ADDAT"]);
                    }
                }));
至此,风平浪静,没有在出错了。 

猜测通过DataSource属性设置数据源的时候可能起内部实现可能需要过多的处理,导致在最开始设置给DataSource的对象(取到了某些属性)在最终绑定的时候不一致,导致报错。因此通过手动添加行的方式可以避免掉此类问题!

基于上面的猜测,采用下面的方式来处理DataSource仍然可以解决问题。

this.Invoke(new Action(() =>
                {
                    dataGridView1.DataSource = source.Copy();
                }));
至于详细的原因需要分析datagridview的绑定原理了!



  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值