【问题标题】:Using BackgroundWorker to refresh datagrid in winforms C#在winforms C#中使用BackgroundWorker刷新数据网格
【发布时间】:2018-05-18 11:59:34
【问题描述】:

我遇到了一个使用后台工作线程来获取数据和更新数据网格的表单的问题。

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

按以下顺序进行,

  • 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;
            }
        }

    }

子表单(它是第二个进行数据网格更新的后台工作者):

private void buttonStart_Click(object sender, EventArgs e)
    {
        backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
        if (!backgroundWorker1.IsBusy)
        {
            backgroundWorker1.RunWorkerAsync();
        }
        backgroundWorker2.DoWork += new DoWorkEventHandler(backgroundWorker2_DoWork);
        if (!backgroundWorker2.IsBusy)
        {
            backgroundWorker2.RunWorkerAsync();
        }
        buttonStart.Enabled = false;


    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {
            BackgroundWorker worker = (BackgroundWorker)sender;

            sSISDBCatalog.StartFactLoad(_etlConnectionString);

            while (isLoading)
            {
                System.Threading.Thread.Sleep(1000);
                if (dynamicWarehouseCatalog.FactsLoaded(_dwConnectionString))
                {
                    if (dynamicWarehouseCatalog.AllFactsLoaded(_dwConnectionString))
                    {
                        isLoading = false;
                    }
                }
            }
        }
        catch(Exception ex)
        {
            MessageBox.Show(
           "Progress viewing has failed. The ETL is still running though. Please monitor your data warehouse growth manually. Error: " + ex, "Pregress Visualisation Failed",
           MessageBoxButtons.OK,
           MessageBoxIcon.Error);
            return;
        }


    }

    private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {
            Control.CheckForIllegalCrossThreadCalls = false;
            BackgroundWorker worker = (BackgroundWorker)sender;
            var dw = DwData(_dwConnectionString);
            var source = SourceData(_sourceConnectionString);
            dataGridView1.DataSource = GetProgress(dw, source);
            while (isLoading)
            {
                System.Threading.Thread.Sleep(1000);
                dw = DwData(_dwConnectionString);
                dataGridView1.DataSource = GetProgress(dw, source);
                dataGridView1.Refresh();
            }

            MessageBox.Show(
               "Your data warehouse facts are loaded!", "Facts Loaded",
               MessageBoxButtons.OK,
               MessageBoxIcon.Information);
            return;
        }
        catch (Exception ex)
        {
            MessageBox.Show(
           "Progress viewing has failed. The ETL is still running though. Please monitor your data warehouse growth manually. Error: " + ex, "Pregress Visualisation Failed",
           MessageBoxButtons.OK,
           MessageBoxIcon.Error);
            return;
        }

    }

【问题讨论】:

  • 你为什么使用后台工作,而不是像 async/await 这样的现代方法?
  • @rory.ap 我只是碰巧选择了这个作为解决方案。如果有更好的方法,我很乐意改变它。就像我说的,当这个奇怪的错误没有发生时,它工作得很好。
  • 不要直接从后台工作线程更新你的 UI:使用 InvokebackgroundWorker.ReportProgress() 等代替。 CheckForIllegalCrossThreadCalls = false 是个非常糟糕的主意!对于异常:准确检查堆栈跟踪,f 不能为空,或参见:stackoverflow.com/questions/4660142/…
  • 您需要了解多线程编程和可用的方法,然后才盲目地投入其中。说得客气一点,这是一个高级话题,如果你不明白自己在做什么,就会有无穷无尽的问题和不满意的客户。
  • @EvanBarke 你不能在任何操作系统中从另一个线程修改 UI。这意味着您不能DoWork 修改网格。您必须处理 RunWorkerCompleted 事件并在那里修改 UI。此时使用async/await 要容易得多。将两个异步操作与 BGW 结合起来也困难得多

标签: c# multithreading winforms backgroundworker


【解决方案1】:

您应该使用BackgroundWorker.ReportProgress Method (Int32, Object),如文档here 中所示。我总是使用第二个参数传递一个enum 来识别这个进度是从哪个后台工作人员报告的。您可以使用类似的方法。

【讨论】:

  • 谢谢,但这无济于事。 ReportProgress 报告一个百分比值。我需要为 dataGridView1 替换整个数据源
  • 您可以将数据表作为第二个参数传递,实际上您不需要关心百分比。
【解决方案2】:

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

Task.Factory.StartNew(() =>
                    UpdateUI()); 

将后台工作人员重命名为 UpdateUI 和内部:

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(); }));
                    }
                }

            }

【讨论】:

    猜你喜欢
    • 2014-07-29
    • 2015-02-14
    • 2018-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多