【问题标题】:How can I loop through a DataGridView created on the main form in a BackgroundWorker?如何遍历在 BackgroundWorker 的主窗体上创建的 DataGridView?
【发布时间】:2011-09-01 20:57:30
【问题描述】:

我想遍历在 BackgroundWorker 的主窗体上创建的 DataGridView,以将数据导出到 CSV 文件。 BackgroundWorker 是在单独的表单上创建的,导出进度将通过进度条显示。这是调用BackgroundWorker的导出表单上的代码:

private DataGridView exportGrid;

public void ExportCSV(DataGridView mainGrid)
{
    this.exportGrid = mainGrid;

    //Set progress bar maximum
    progressBar1.Maximum = mainGrid.Rows.Count;

    if (backgroundWorker1.IsBusy != true)
    {
        //Start the asynchronous operation
        backgroundWorker1.RunWorkerAsync();
    }

    //Show the form
    this.ShowDialog();
}

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

    //Write data rows
    foreach (DataGridViewRow row in exportGrid.Rows)
    {
        //Check if the background worker has been cancelled
        if (worker.CancellationPending == true)
        {
            e.Cancel = true;
            break;
        }
        else
        {
            foreach (DataGridViewCell cell in row.Cells)
            {
                if (cell.Visible)
                {
                    //Do CSV writing here...
                }
            }

            //Report current progress to update UI
            worker.ReportProgress(row.Index + 1);
        }
    }
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    //Update progress bar
    this.progressBar1.Value = e.ProgressPercentage;
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    //Close the form once the background worker is complete
    this.Close();
}

此代码导致以下错误:

  • BindingSource 不能是它自己的数据源。不要设置 DataSource 和 DataMember 属性指向返回的值 绑定源。
  • 跨线程操作无效:从 线程不是创建它的线程。

我认为这是因为我在未创建 DataGridView 的线程中访问它。这样做的最佳方法是什么?有没有可能?

更新:

我循环通过 DataGridView 而不是数据源的原因是用户将更改列顺序、排序顺序和显示/隐藏网格的列,他们希望这些更改反映在导出的数据中。有没有其他方法来处理这个问题?

【问题讨论】:

  • 这个 DataGridView 的行数有多大?
  • 目前它有超过 80,000 行。

标签: c# winforms multithreading


【解决方案1】:

在我看来,Jeff 你在这里至少犯了两个错误:

  • 从 UI 控件导出数据,而不是从数据源中导出;
  • 试图从后台线程访问 UI 控件;

我只是不会尝试以一种甚至不是声明后台线程的形式来访问 UI 控件(在您的情况下为 Grid),代码将如此不清楚和不可读...

然后考虑 UI 控件用于在 UI 中呈现数据;每当您需要访问数据以进行除在屏幕中呈现之外的任何其他操作时,您最好直接访问用于填充 UI 的数据源。

【讨论】:

  • 我尝试使用网格而不是数据源的原因是因为用户希望能够在导出之前对列进行重新排序、排序或隐藏,并且生成的数据看起来相同。
  • 我明白了,如果从工作线程 UI 导出时没有被阻止并且用户更改顺序并再次排序,您将如何反应和处理?
  • 从技术上讲,第二个(导出)表单是用ShowDialog() 调用的,因此用户将无法返回主表单。但是,我确实看到使用这样的 UI 控件不是正确的方法。有其他选择吗?
  • 如果您使用 ShowDialog 并且此时不允许在 UI 中执行任何其他操作,我将从 UI 线程导出,因为 UI 无论如何都会被阻止...如果我可以提出真正的替代方案,请获取 DevXpress XtraGrid只需调用方便的方法:myGrid.ExportToExcel(...) ;-)
  • 好吧,主窗体的 UI 被屏蔽了,但辅助(导出表单)的 UI 没有被屏蔽。我更新了此表单上的进度条,以便用户知道导出过程大约需要多长时间。
【解决方案2】:

我以前遇到过这种情况。我是这样做的。这是直接从我所做的项目中提取的。您应该能够了解如何设置线程以使其工作。我没有包括实际编写 CSV 文件的方法,我假设您的主要问题是线程。正如 Davide Piras 所说,直接从控件写入数据可能不是一个好主意。

BackGroundWorker 的事件处理程序:

    #region TableWorker Events
    void TableWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        bool done = false;
        GetSwitch();
        ProgressLabel.Visible = true;
        while (!done)
        {
            for (int i = 1; i <= 100; i++)
            {
                Thread.Sleep(100);
                TableWorker.ReportProgress(i);
            }
            done = Export.ExportDataTable(SaveFile, DataTable);
        }
    }

    void TableWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        Progress.Style = ProgressBarStyle.Blocks;
        Progress.Value = e.ProgressPercentage;
        ProgressLabel.Text = "Writing File: " + e.ProgressPercentage.ToString() + "% Complete";
    }

    void TableWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Progress.Value = 100;
        ProgressLabel.Visible = false;
        Progress.Visible = false;
        //MessageBox.Show("Export Completed!");
        TableWorker.Dispose();
        ExportButton.Enabled = true;
        this.Close();
    }
    #endregion

启动 BackgroundWorker 的事件。

    private void EntireTableButton_Click(object sender, EventArgs e)
    {
        dialogResult = Export.SetSaveDialog(SaveFile, ".csv", "csv file (*.csv)|*.csv");

        if (dialogResult == DialogResult.OK)
        {
            TableWorker.RunWorkerAsync();
            this.Hide();
            ProgressLabel.Visible = true;
            ProgressLabel.Text = "Retrieving Data...";
            Progress.Style = ProgressBarStyle.Marquee;
            Progress.Visible = true;
            ExportButton.Enabled = false;

            while (TableWorker.IsBusy)
            {
                Application.DoEvents();
            }
            Progress.Visible = false;
        }
    }

后台工作人员的 ReportProgress 方法将允许您将进度传递给 ProgressChanged 事件。通过这样做,您可以更新另一个表单上的进度条。

【讨论】:

  • 我相信我的问题是我在后台工作人员中循环通过一个 UI 控件。如果我用常规的for 循环替换这个循环,那么线程的所有内容都可以正常工作。所以我想我正在寻找使用 Grid 的替代方法,即使我认为我需要保留用户设置的格式(顺序、隐藏、排序)。
  • 您的数据源在数据库中吗?当我调用 Export 类时,我基本上是在使用表上的过滤器并创建一个 SQL 命令,因此数据库将返回一个 DataTable,该 DataTable 的过滤与用户过滤 DataGridView 的方式完全相同。
猜你喜欢
  • 2016-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多