数据网格迭代更新期间的弹出状态 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 行时。这更新了初始屏幕上的标签 :)
我为此花了 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 行时。这更新了初始屏幕上的标签 :)