C# 如何避免在运行时更新数据源时控件冻结?

C# 如何避免在运行时更新数据源时控件冻结?,c#,winforms,datagridview,freeze,C#,Winforms,Datagridview,Freeze,我在WinForms应用程序中工作,并在应用程序中使用DataGridView控件。最初,我加载了其中的10000行和50列。我的场景是在特定时间更新数据源,对计时器进行插值 问题:在更新数据源时执行actioncell_单击、滚动等操作时,网格已被冻结/绑定 如何解决这个问题?附近有工作吗?请把你的想法告诉我 以下是我目前的代码: private void timer1_Tick(object sender, EventArgs e) { //try {

我在WinForms应用程序中工作,并在应用程序中使用DataGridView控件。最初,我加载了其中的10000行和50列。我的场景是在特定时间更新数据源,对计时器进行插值

问题:在更新数据源时执行actioncell_单击、滚动等操作时,网格已被冻结/绑定

如何解决这个问题?附近有工作吗?请把你的想法告诉我

以下是我目前的代码:

private void timer1_Tick(object sender, EventArgs e)
    {
        //try
        {
            timer.Stop();
            for (int i = 0; i < 10000; i++)
            {
                var row = r.Next() % 10000;
                for (int col = 1; col < 10; col++)
                {
                    var colNum = r.Next() % 55;
                    if (table != null)
                        table.Rows[row][colNum] = "hi";// r.Next().ToString();
                }
            }
            table.AcceptChanges();
            timer.Start();
        }
    }
以下是一个示例输出:

[


谢谢。

解决方案之一是在长时间运行操作期间调用Application.DoEvents。以下是示例

private void timer1_Tick(object sender, EventArgs e)
{
    //try
    {
        timer.Stop();
        for (int i = 0; i < 10000; i++)
        {
            var row = r.Next() % 10000;
            for (int col = 1; col < 10; col++)
            {
                var colNum = r.Next() % 55;
                if (table != null)
                    table.Rows[row][colNum] = "hi";// r.Next().ToString();
            }
            Application.DoEvents(); //add this line
        }
        table.AcceptChanges();
        timer.Start();
    }
}

另一个解决方案是将长时间运行的任务移动到单独的线程。

尝试使用BackgroundWorker

BackgroundWorker是System.ComponentModel中的助手类 用于管理工作线程的命名空间。可以将其视为 EAP的通用实现采用基于事件的异步模式,并提供以下功能 特点:

一种协作对消模型

工作程序完成时安全更新WPF或Windows窗体控件的功能

向完成事件转发异常 报告进展的协议 IComponent的一个实现,允许它位于VisualStudio的设计器中 下面您可以找到一个示例,请根据您的计时器进行调整:

class Program
{
  static BackgroundWorker _bw = new BackgroundWorker();

  static void Main()
  {
    _bw.DoWork += bw_DoWork;
    _bw.RunWorkerAsync ("Message to worker");
    Console.ReadLine();
  }

  static void bw_DoWork (object sender, DoWorkEventArgs e)
  {
    // This is called on the worker thread
    Console.WriteLine (e.Argument);        // writes "Message to worker"
    // Perform time-consuming task...


           //update your grid
            for (int i = 0; i < 10000; i++)
            {
                var row = r.Next() % 10000;
                for (int col = 1; col < 10; col++)
                {
                    var colNum = r.Next() % 55;
                    if (table != null)
                        table.Rows[row][colNum] = "hi";r.Next().ToString();
                }
            }
            table.AcceptChanges();

  }
}

此数据更新的频率是多少?通常需要多长时间才能完成?恐怕DataSet和DataGridView并不是为实时更新的数万行而设计的。您可能需要一个具有虚拟化呈现的数据网格,理想情况下,数据源与UI之间没有如此紧密的联系。您使用的是Bindi吗ngSource作为DataGridView的数据源?不,我使用了DataTable作为数据源。请参考随附的示例。我很确定这会造成问题,因为datagrid似乎是数据绑定到数据表的。因此,您将在非UI线程上获得UI更新。在msdn的示例中,UI元素实际上是从UI线程。这就是通过调用UI线程的ReportProgress函数更新进度的原因。在ProgressChanged事件处理程序中放置一个断点来检查进度,并检查正在执行的线程。您将看到,它是主线程。因此,您的示例将导致InvalidOperationException。@Luaan关于UI更新。DataTable更改触发DGV代码订阅的RowChanged事件。唯一允许此操作的是,DGV代码似乎必须捕获并吃掉InvalidOperationException。您可以将此视为Sys中发生的“System.InvalidOperationException”类型的第一次意外异常调试时,tem.Windows.Forms.dll消息会出现在即时窗口中。或者,您可以将调试器设置为在引发异常时中断,并检查调用堆栈。这实际上会使更新速度变慢。@Zinov,正如TnTinMn刚刚确认的那样。BackgroundWorker在其他线程中执行代码。来自此线程的UI更新会导致入侵lidOperationExcetion。无论您使用BackgroundRoker、线程、任务还是其他什么。只有UI-thead才允许这样做。如果您想更改DGV的数据源,就可以这样做。@sbecker&Zinov,我不想暗示这是一个糟糕的解决方案,而是说它缺少一些步骤。为了避免跨线程访问,您需要o在操作Datatable之前挂起绑定并在操作后恢复绑定。这可以通过检索Datatable的,调用其SuspendBinding并调用其ResumeBinding方法来完成。必须在UI线程上调用ResumeBinding以防止异常。谢谢。Application.DoEvents如何工作?因为如果我使用此选项,我的网格不会更新。但有时网格值仅为几列更改。@Prithiv它基本上运行消息循环的迭代。主要问题是它允许重复,这有点难以解释,可能会导致两个或多个勾号处理程序交替并行运行,例如例如,即使您这样做,并且没有以任何方式限制重复,您也确实不希望在每次迭代中都这样做——这可能会变得非常缓慢。相反,只需要经常这样做,以防止应用程序在相当长的时间内(比如20-200毫秒)没有响应。