我已经完成了一个程序,它做了我想做的事情,但是我觉得我“做错了”,尽管它看起来足够有效。我准备了一个小示例,说明我觉得backgroundworker类处理得不对,我想看看我是否能够更干净地处理这个问题。
首先,我有一个带有1按钮、1 statusstrip、1 toolstripprogressbar和1 toolstriplabel的表单。我在运行statusstrio时更新backgroundworker项,并显示跨类的messageboxes。见下文。
namespace StatusStripTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void prepareToRun()
{
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork += delegate
{
Looping.loop(statusStrip1);
};
backgroundWorker1.RunWorkerAsync();
}
}
private void button1_Click(object sender, EventArgs e)
{
prepareToRun();
}
}
}namespace StatusStripTest
{
public class Looping
{
public static void loop(StatusStrip strip)
{
Form1 Form1 = new Form1();
string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
while (true)
{
MessageBox.Show("Time to loop");
foreach (char c in alphabet)
{
StripHandler.UpdateProgress(strip, alphabet.IndexOf(c), alphabet.Length);
StripHandler.UpdateStatus(strip, c.ToString());
}
}
}
}
}namespace StatusStripTest
{
public class StripHandler
{
public static void UpdateStatus(StatusStrip ss, String Status)
{
ss.Invoke((MethodInvoker)delegate
{
if ((ss.Items[1] as ToolStripStatusLabel) != null)
{
ss.Items[1].Text = Status;
}
});
}
public static void UpdateProgress(StatusStrip ss, long position, long len)
{
ss.Invoke((MethodInvoker)delegate
{
if ((ss.Items[0] as ToolStripProgressBar) != null)
{
ToolStripProgressBar tspb = (ToolStripProgressBar)ss.Items[0];
tspb.Minimum = 0;
tspb.Maximum = (int)len;
tspb.Value = (int)position;
}
});
}
}
}我是不是不正确地通过了statusstrip?
发布于 2014-01-21 20:04:27
这里有一种技术,可以在一个文件和一个类名下完成您的三个类所做的一切。Konrad的代码很好,但是下面的代码展示了如何完成您的任务。
由于您不需要更改BackgroundWorker的事件处理程序,所以在表单的构造函数中对它们进行编码:
private const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
public Form1() {
InitializeComponent();
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
}我还将alphabet定义为常量,因为它永远不会改变。
现在,您需要知道什么是backgroundWorker_DoWork和backgroundWorker_ProgressChanged。
此版本最接近您发布的代码,使用的是字母表字符串上的foreach循环。每次访问下一个字符时,都必须提取该字符的索引,并且必须再次确定固定数组的长度:
private void backgroundWorker_DoWork_obsolete(object sender, DoWorkEventArgs e) {
var obj = (BackgroundWorker)sender;
while (!obj.CancellationPending) {
foreach (char c in alphabet) {
float calc = ((float)alphabet.IndexOf(c) / alphabet.Length) * 100;
obj.ReportProgress(Convert.ToInt32(calc));
}
}
}下面修改的版本将使用作为参数提供的值来创建字符串(因此,现在可以将此代码用于其他字符串值)。由于您的代码基本上只是遍历字符串的长度,所以我修改了这个版本以使用更高效的for(;;)循环(更有效,因为它不必每次提取索引或计算字符串长度):
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
var obj = (BackgroundWorker)sender;
string alphas = e.Argument.ToString();
int len = alphas.Length;
while (!obj.CancellationPending) {
for (int i = 0; i < len; i++) {
float calc = ((float)i / len) * 100;
obj.ReportProgress(Convert.ToInt32(calc));
}
}
}注意,在这两个示例中,我使用的是while (!obj.CancellationPending)而不是while(true)。现在,如果您想在表单中包含一个Cancel按钮,只需将其连接如下:
private void cancel_Click(object sender, EventArgs e) {
backgroundWorker1.CancelAsync();
}这将使您的BackgroundWorker循环停止。
现在,backgroundWorker_ProgressChanged例程很简单:
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) {
toolstripprogressbar.Value = e.ProgressPercentage;
}要执行这段代码,只需确保在调用toolstripprogressbar之前初始化RunWorkerAsync。
private void prepareToRun() {
if (!backgroundWorker1.IsBusy) {
toolstripprogressbar.Minimum = 0;
toolstripprogressbar.Value = 0;
toolstripprogressbar.Maximum = alphabet.Length;
backgroundWorker1.RunWorkerAsync(alphabet);
}
}
private void button1_Click(object sender, EventArgs e) {
prepareToRun();
}一些额外的注意事项:
BackgroundWorker.ReportProgress被重载以接受object userState,而BackgroundWorker.ProgressChangedEventArgs包含这个UserState对象。因此,如果您想将实际项传递给名为lblProgress的Label控件,这是可能的:
private void backgroundWorker_DoWork2(object sender, DoWorkEventArgs e) {
var obj = (BackgroundWorker)sender;
string alphas = e.Argument.ToString();
int len = alphas.Length;
while (!obj.CancellationPending) {
foreach (char c in alphas ) {
float calc = ((float)alphas .IndexOf(c) / len ) * 100;
obj.ReportProgress(Convert.ToInt32(calc), c);
}
}
}
private void backgroundWorker_ProgressChanged2(object sender, ProgressChangedEventArgs e) {
toolstripprogressbar.Value = e.ProgressPercentage;
lblProgress.Text = e.UserState.ToString();
}如果你想变的花花公子,把BackgroundWorker.RunWorkerCompletedEventHandler:连接起来
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);...add这段代码可以隐藏进度栏并启用Cancel:
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
toolstripprogressbar.Visible = false;
btnCancel.Enabled = true;
}...and修改prepareToRun()语句:
private void prepareToRun() {
if (!backgroundWorker1.IsBusy) {
toolstripprogressbar.Minimum = 0;
toolstripprogressbar.Value = 0;
toolstripprogressbar.Maximum = alphabet.Length;
backgroundWorker1.RunWorkerAsync(alphabet);
if (backgroundWorker1.IsBusy) {
toolstripprogressbar.Visible = true;
btnCancel.Enabled = false;
}
}
}你能看出我用了很多BackgroundWorkers吗?
另一个注意事项,基于您提供的评论。DoWorkEventArgs变量e有一个名为参数的对象变量,它对应于您在backgroundWorker1.RunWorkerAsync(any_object)中提供的任何输入。
这意味着any_object可以是这样的类:
class MyParameters {
public int Minimum { get; set; }
public int Index { get; set; }
public int Maximum { get; set; }
public string TextIn { get; set; }
public string TextOut { get; set; }
}要使用它,只需将其转换回传入的内容:
private const int BG_MSG_1 = 1;
private void backgroundWorker_DoWork3(object sender, DoWorkEventArgs e) {
var obj = (BackgroundWorker)sender;
var params = (MyParameters)e.Argument;
params.Minimum = 0;
params.Index = 0;
params.Maximum = params.TextIn.Length;
while (!obj.CancellationPending) {
foreach (char c in params.TextIn ) {
params.Index++;
params.TextOut = string.Format("Processed {0}", c);
obj.ReportProgress(BG_MSG_1, params);
}
}
}注意,我已经重用了ReportProgress的int变量ProgressPercentage,作为在ProgressChanged事件处理程序中提供更多/更深层次选项的一种方法。
现在,您可以在主线程中执行任何需要的计算,方法是从MyParameters中提取UserState:
private void backgroundWorker_ProgressChanged3(object sender, ProgressChangedEventArgs e) {
if (e.ProgressPercentage == BG_MSG_1) {
var params = (MyParameters)e.UserState;
float calc = ((float)params.Index / params.Maximum ) * 100;
toolstripprogressbar.Value = Convert.ToInt32(calc)
lblProgress.Text = params.TextOut;
}
}现在,您只受您想要来回传递的任何类的复杂程度的限制--您可以创建一个类来传递数据,而另一个类用于传递数据。真的,全靠你了。
发布于 2014-01-21 12:26:36
通常,向BackgroundWorker传递控件根本不应该是必要的。想一想,因为它只是有一些工作要做,仅此而已。然而,它当然可以报告其工作的进展情况。在设计BackgroundWorker类时考虑了这两种思想,DoWork和ProgressChanged事件分别反映了这两种思想。还有一个专门的BackgroundWorker.ReportProgress方法来引发ProgressChanged事件。
这个事件应该在GUI级别上处理,这样它就可以访问控件,而不必将它们传递给worker。解决办法草案:
private void prepareToRun()
{
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork += backgroundWorker_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker_ProgressChanged;
backgroundWorker1.RunWorkerAsync();
}
}
public static void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// use worker.ReportProgress(percent, someData) somewhere here
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Update your statusStrip1 here according to data passed in e.ObjectState
}https://codereview.stackexchange.com/questions/39733
复制相似问题