数据网格迭代更新期间的弹出状态 window

Popup status window during a datagrid iteration update

我为此花了 4 个小时,但完全失败了。 我知道我需要使用 BackgroundWorker,但所有教程都参考了 运行 在您 运行 正在使用的工作人员的实际表单上设置进度脚本。

我有一个很大的数据网格,用户可以使用复选框 "select all" 然后按 "UPDATE ALL" 这会用他们选择的一堆选项更新每个网格。 对于某些用户来说,这可能是 5 条记录,这没什么,但有些用户可能会用 5 个选项更新 200 条记录,这大约需要... 10-15 秒来遍历它们。

我已经尝试了很多 运行ning BGworker 的变体,它加载了 FrmLoading.Showdialog

或者尝试让 BGworker "do work" 运行 连接代码然后主线程有 FrmLoading.Show() 但是没有任何效果。

如果我在后台工作程序中有更新代码,它会失败,因为数据网格和所有内容都在不同的线程中。

相反,它只是挂在 FrmLoading.Show()

任何建议都很好。

我似乎无法理解如何让这个看似简单的想法发挥作用!

当前更新代码:

foreach (DataGridViewRow rowx in dataGridpatients.Rows)
{
    //MessageBox.Show(Convert.ToBoolean(rowx.Cells["clnselected"].Value).ToString());
    if (Convert.ToBoolean(rowx.Cells["clnselected"].Value) == true)
    {
         //if cycle has a value.
         if (cmbcycle.SelectedIndex != -1)
         {
            rowx.Cells["clncycletype"].Value = cycle;
            rowx.Cells["clnpackscollect"].Value = packs;
         }

         //if location has a value
         if (cmblocation.SelectedIndex != -1)
         {
            location = Convert.ToInt32(cmblocation.SelectedValue);
            rowx.Cells["clnlocation1"].Value = location;   
         }

         if (cmbsize.SelectedIndex != -1)
         {
            size = Convert.ToInt32(cmbsize.SelectedValue);
            rowx.Cells["clnpacksize"].Value = size;   
         }

         if (chkDelivery.Checked == true)
         {
            rowx.Cells["clnDelivery"].Value = true;
         }

         if (chkSignSheet.Checked == true)
         {
             rowx.Cells["clnSigningSheet"].Value = true;
         }
    }
    countupdated++;
 }

 foreach (DataGridViewRow row in dataGridpatients.Rows)
 {
     row.Cells["clnselected"].Value = false;
     row.DefaultCellStyle.BackColor = Color.White;
 }

 cmbsize.SelectedIndex = -1;
 cmblocation.SelectedIndex = -1;
 cmbcycle.SelectedIndex = -1;
 chkDelivery.Checked = false;
 chkSignSheet.Checked = false;
 @countupdated++;

我还有@CountSelected。

我想要做的是 运行 上面的这段代码,但是有一个带有我的徽标的弹出式覆盖(对话框)+ "Updating X%" 其中 X = countupdated/countselected * 100

我现在知道我需要使用后台工作者并为上述调用,但实际上不知道如何调用网格并从那里开始。 我知道我需要调用我正在使用的变量 (例如 cmbcycle.SelectedIndex)

我知道遍历 150 条记录并更新单个单元格可能是错误的,

我的另一个选择是从该数据table 上的 "selected" 个单元格创建一个数据table 然后 运行 通过 SQL 更新而不是遍历绑定 table。 然后在 SQL 之后,我可以重新创建 table,它现在将在其中更新新的单元格值? 那会是更合适的方式吗?

此 table 的最大行数为 200。平均约为 70,因此我们永远不会谈论 500 或 1000

编辑: 因此,选中的答案适用于 运行 后台工作人员并引用表单上的控件。 问题是如果我这样做:

backgroundWorker1.RunWorkerAsync();
                splashy.ShowDialog();

然后后台worker结束后弹出splash screen

如果我这样做:

splashy.ShowDialog();
backgroundWorker1.RunWorkerAsync();

然后popup semi-form并挂起,直到后台worker结束,此时关闭 因为 RunWorkerCompleted 事件。

编辑: 我没有更新 DoWork 中的代码并使用 Invokes 来引用控件。 这有效,代码 运行 没问题。

我现在需要一个显示更新进度的弹出窗口。

splashy.InvokeBy(() =>
                {
                    splashy.Show();
                });
                backgroundWorker1.RunWorkerAsync();

无效。它会导致弹出窗口但冻结

splashy.ShowDialog();
backgroundWorker1.RunWorkerAsync();

允许对话框显示(不是 'frozen' 和扭曲)但是实验室 (lblprogress) 不更新。

这是因为窗体永远不会到达 RunWorker 方法,它停留在 ShowDialog。

最好对数据源本身进行修改,然后将其与 DataGridView 绑定。

但是从现有代码来看,如果您想访问 controls/UI 以更新或更改 BackgroundWorker.RunWorkerAsync 方法或任何其他 Thread 调用的值,您可以创建.Invoke() 控件的扩展方法,例如:

public static class MyExtensions
{
    public static void InvokeBy(this Control ctl, MethodInvoker method)
    {
        if (ctl.InvokeRequired)
            ctl.Invoke(method);
        else method();
    }
}

为方便起见,将此 static class 与主 class 保持在相同的 Namespace 下。


因此这段代码:

foreach (DataGridViewRow rowx in dataGridpatients.Rows)
{
  //your codes
}

将成为:

dataGridpatients.InvokeBy(() =>
{
   foreach (DataGridViewRow rowx in dataGridpatients.Rows)
   {
     //your codes
   }
});

同样,

if (cmbcycle.SelectedIndex != -1)
{
   //your codes
}

将成为:

cmbcycle.InvokeBy(() =>
{
   if (cmbcycle.SelectedIndex != -1)
   {
      //your codes
   }
});

通过这种方式,您可以安全地访问您的控件,同时保持您的 UI 响应。以同样的方式更新您的弹出状态 UI!

此答案基于 o_O 的答案。

主要问题是我希望 UI 实际更新并由后台工作人员提供启动画面。

而不是 运行 BGW 中的所有 'hard code',我将其留在原始线程中,但调用 BGW 以显示弹出对话框表单。

所以在 "hard code" 的开头我使用了:

backgroundWorker1.RunWorkerAsync();

这叫:

FrmSplash splashy;
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    splashy = new FrmSplash();
    splashy.ShowDialog();
}

为了删除对话框,在GUI线程的代码末尾,我使用了:

splashy.InvokeBy(() =>
 {
    splashy.Close();
 }
);

backgroundWorker1.CancelAsync();

它使用 O_o

提供的扩展
public static class MyExtensions
{
   public static void InvokeBy(this Control ctl, MethodInvoker method)
   {
       if (ctl.InvokeRequired)
           ctl.Invoke(method);
       else method();
   }
}

我还在 splaspy 中构建了一个标签更新 所以我可以打电话给

splashy.InvokeBy(() =>
  {
     splashy.SetStatus(countupdated.ToString());
  }
);

当我遍历 datagridview 行时。这更新了初始屏幕上的标签 :)