C# 在WinC窗体中使用BackgroundWorker刷新数据网格#

C# 在WinC窗体中使用BackgroundWorker刷新数据网格#,c#,multithreading,winforms,backgroundworker,C#,Multithreading,Winforms,Backgroundworker,我遇到了一个表单问题,它使用后台工作线程获取数据并更新数据网格 用例是一个带有进度条的列表,显示数据库中加载了多少表 它的顺序如下: FormA实例化FormB FormB是一个后台工作程序的循环,用于获取数据和 更新datagrid.datasource 它有时工作得很好,但另一些FormB.ShowDialog()返回时出现空引用异常。我不明白为什么 以下是呼叫表单代码: private void button1_Click(object sender, EventArgs e)

我遇到了一个表单问题,它使用后台工作线程获取数据并更新数据网格

用例是一个带有进度条的列表,显示数据库中加载了多少表

它的顺序如下:

  • FormA实例化FormB
  • FormB是一个后台工作程序的循环,用于获取数据和 更新datagrid.datasource
它有时工作得很好,但另一些
FormB.ShowDialog()
返回时出现空引用异常。我不明白为什么

以下是呼叫表单代码:

 private void button1_Click(object sender, EventArgs e)
    {


        using (var f = new FormFactProgress(_dwConnectionString, _sourceConnectionString, _etlConnectionString))
        {
            f.ShowDialog();  \\ THIS IS WHERE THE NULL REFERENCE EXCEPTION ALWAYS HAPPENS!
            labelLoadWarning.Visible = false;
            groupBoxLoadFacts.Enabled = false;
            buttonLoadFacts.BackColor = Color.FromArgb(128, 255, 128);
            buttonLoadFacts.Text = "Loaded";
            if (dynamicWarehouseCatalog.FactsLoaded(_dwConnectionString) && dynamicWarehouseCatalog.DimsLoaded(_dwConnectionString))
            {
                groupBoxSync.Enabled = true;
            }
        }

    }
子表单(它是执行datagrid更新的第二个backgroundworker):


您应该使用
BackgroundWorker.ReportProgress方法(Int32, 对象)
如文档所示。我总是使用第二个参数传递一个
enum
,以确定报告此进度的后台工作人员。您可以使用类似的方法。

因此我找到了一个很好的解决方案。基本上,我将后台工作程序更改为获取数据的任务,然后调用需要更新的dataGridView。例如

Task.Factory.StartNew(() =>
                    UpdateUI()); 
将后台工作程序重命名为UpdateUI和inside:

var dw = DwData(_dwConnectionString);
            var source = SourceData(_sourceConnectionString);
            DataTable prog;
            while (isLoading)
            {
                System.Threading.Thread.Sleep(1000);
                dw = DwData(_dwConnectionString);
                if (dw.Rows.Count > 0)
                {                     
                    prog = GetProgress(dw, source);
                    if (prog.Rows.Count > 0)
                    {
                        dataGridView1.Invoke(new MethodInvoker(() => { dataGridView1.DataSource = prog; dataGridView1.Refresh(); }));
                    }
                }

            }

为什么要使用后台工作程序而不是像async/await这样的现代方法?@rory.ap我碰巧选择了这个作为解决方案。如果有更好的方法,我很乐意改变它。就像我说的,当这个奇怪的错误没有发生时,它工作得很好。不要直接从后台工作线程更新UI:使用
Invoke
backgroundWorker.ReportProgress()
CheckForIllegalCrossThreadCalls=false
是一个非常糟糕的主意!对于例外情况:准确地检查堆栈跟踪,
f
不能为空,或者请参见:在盲目地跳入多线程编程之前,您需要了解多线程编程和可用的方法。委婉地说,这是一个高级主题,如果你不了解自己在做什么,你将面临无休止的问题和不满意的客户。@EvanBarke在任何操作系统中,你都不能从另一个线程修改UI。这意味着您不能从
DoWork
修改网格。您必须处理
RunWorkerCompleted
事件并修改那里的UI。此时使用
async/await
要容易得多。将两个异步操作与BGW结合起来要困难得多,但这并没有帮助。ReportProgress报告一个百分比值。我需要替换DataGridView1的整个数据源。您可以将datatable作为第二个参数传递,实际上不需要关心百分比。
var dw = DwData(_dwConnectionString);
            var source = SourceData(_sourceConnectionString);
            DataTable prog;
            while (isLoading)
            {
                System.Threading.Thread.Sleep(1000);
                dw = DwData(_dwConnectionString);
                if (dw.Rows.Count > 0)
                {                     
                    prog = GetProgress(dw, source);
                    if (prog.Rows.Count > 0)
                    {
                        dataGridView1.Invoke(new MethodInvoker(() => { dataGridView1.DataSource = prog; dataGridView1.Refresh(); }));
                    }
                }

            }