我花了4个小时才完全失败了。 我知道我需要使用BackgroundWorker,但所有教程都指的是在运行worker的实际表单上运行进度脚本。
我有一个大型数据网格,用户可以使用复选框来选择所有"然后按"更新所有" 这会使用他们选择的一组选项更新每个网格。 对于一些用户来说,这可能是5条记录,这些记录什么都没有,但是有些可能会更新200条记录,其中包含5个选项,需要大约10-15秒来迭代它们。
我尝试了很多运行BGworker的变种,它加载了FrmLoading.Showdialog
或者试图让BGworker"做好工作"运行代码,然后运行具有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。
我想要做的是运行上面的代码但是有一个带有我的徽标的弹出式叠加层(对话框)+"正在更新X%" 其中X = countupdated / countselected * 100
我现在知道我需要使用后台工作器并调用上面的内容,但实际上根本不知道如何调用网格并从那里开始。 我知道我需要调用我使用的变量 (例如,cmbcycle.SelectedIndex)
我知道迭代150条记录并更新单个单元格可能是错误的,
我的另一个选择是从"选择"创建一个数据表。该数据表上的单元格 然后通过SQL运行更新,而不是遍历绑定表。 然后在SQL之后我可以重新创建表,现在将更新新的单元格值吗? 这会是一个更合适的方法吗?
此表上的最大行数为200.平均值约为70,因此我们永远不会说500或1000
编辑: 因此,选中的答案可以运行后台工作程序并引用表单上的控件。 问题是,如果我这样做:
backgroundWorker1.RunWorkerAsync();
splashy.ShowDialog();
然后在后台工作人员结束后弹出启动画面
如果我这样做:
splashy.ShowDialog();
backgroundWorker1.RunWorkerAsync();
然后弹出窗口半成形并挂起,直到后台工作者结束,此时它关闭 因为RunWorkerCompleted事件。
编辑: 我没有更新DoWork中的代码,并使用Invokes来引用控件。 这有效,代码运行良好。
我现在需要一个弹出窗口,显示更新的进度。
splashy.InvokeBy(() =>
{
splashy.Show();
});
backgroundWorker1.RunWorkerAsync();
不起作用。它导致弹出窗口但冻结
splashy.ShowDialog();
backgroundWorker1.RunWorkerAsync();
允许显示对话框(不是'冻结'和失真)但是Lab(lblprogress)不会更新。
这是因为表单永远不会进入RunWorker方法,而是停留在ShowDialog。
答案 0 :(得分:1)
最好对DataSource本身进行修改,然后将其与DataGridView
绑定。
但是,如果您想要访问控件/用户界面来更新或更改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
课程保留在与您的主要课程相同的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!
答案 1 :(得分:0)
这个答案是基于o_O的回答。
主要问题是我希望UI实际更新,而后台工作者需要提供启动。
而不是运行所有'硬代码'在BGW中,我将其保留在原始线程中,但称为BGW以显示弹出对话框形式。
所以在#34;硬代码开始时#34;我用过:
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();
}
}
我还在splashy中构建了一个标签更新 所以我可以打电话给
splashy.InvokeBy(() =>
{
splashy.SetStatus(countupdated.ToString());
}
);
当我遍历datagridview行时。这更新了启动画面上的标签:)