无法在 backgroundworker 中为文本框赋值(c# winform)
Can not assign value for textbox in backgroundworker (c# winform)
public class Form1
{
public delegate void SetStatus (string status);
public event SetStatus SetStatusHandler;
public BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
tbxResult.Text = "Assign text Ok";
SetStatusHandler += delegate(string status)
{
tbxResult.Text = status; // can not assign
};
bw.DoWork += backgroundWorker_DoWork;
bw.RunWorkerAsync();
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
var status = "assign some value";
SetStatusHandler(status);
}
}
首先,我尝试在 BackgroundWorker 内的主线程上为文本框 tbxResult 设置值,但它没有用,然后我使用委托从主线程分配文本框值,但它也不起作用...
请帮帮我...怎么了?
希望逻辑合理。但是委托的声明是错误的,你必须在 SetStatus
的声明中指定 return 类型才能使其工作。所以像下面这样更改声明:
public delegate void SetStatus (string status);
仍然出现异常(IllegalCrossThreadCallException
,如评论中提到的 René Vogt
)意味着您可以像下面这样定义事件处理程序:
SetStatusHandler += delegate(string status)
{
tbxResult.Invoke(new MethodInvoker(delegate
{
tbxResult.Text = status;
}));
};
我们需要使用主 UI 线程再次调用该方法。查看更多信息 Usage of MethodInvoker in C#
tbxResult.Invoke((MethodInvoker) delegate
{
tbxResult.Text = status;
});
总的来说,我认为 BackgroundWorker
已经过时了,最好使用 async/await
模式。但这太宽泛了。
所以直接的答案是:你的事件处理程序不是在UI线程上执行的,而是在后台工作线程上执行的。从后台工作者重新调用 ui 线程上的操作的正常方法是使用它的 ReportProgress
方法:
public Form1()
{
tbxResult.Text = "Assign text Ok";
bw.DoWork += backgroundWorker_DoWork;
bw.WorkerReportsProgress = true;
bw.ProgressChanged += (sender, e) => tbxResult.Text = (string)e.UserState;
bw.RunWorkerAsync();
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
var status = "assign some value";
bw.ReportProgress(0, status);
}
ReportProgress()
invokes the ProgressChanged
UI 线程上的事件,因此您可以将文本分配给文本框。记得将 WorkerReportsProgress
设置为 true
.
像这样使用 InvokeRequred(或与您的委托一起使用):
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (textBox1.InvokeRequired)
{
textBox1.Invoke((MethodInvoker) delegate { textBox1.Text = @"AAAAAA"; });
}
else
{
textBox1.Text = @"AAAAAA";
}
}
ps:另见Anonymous method in Invoke call
public class Form1
{
public delegate void SetStatus (string status);
public event SetStatus SetStatusHandler;
public BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
tbxResult.Text = "Assign text Ok";
SetStatusHandler += delegate(string status)
{
tbxResult.Text = status; // can not assign
};
bw.DoWork += backgroundWorker_DoWork;
bw.RunWorkerAsync();
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
var status = "assign some value";
SetStatusHandler(status);
}
}
首先,我尝试在 BackgroundWorker 内的主线程上为文本框 tbxResult 设置值,但它没有用,然后我使用委托从主线程分配文本框值,但它也不起作用...
请帮帮我...怎么了?
希望逻辑合理。但是委托的声明是错误的,你必须在 SetStatus
的声明中指定 return 类型才能使其工作。所以像下面这样更改声明:
public delegate void SetStatus (string status);
仍然出现异常(IllegalCrossThreadCallException
,如评论中提到的 René Vogt
)意味着您可以像下面这样定义事件处理程序:
SetStatusHandler += delegate(string status)
{
tbxResult.Invoke(new MethodInvoker(delegate
{
tbxResult.Text = status;
}));
};
我们需要使用主 UI 线程再次调用该方法。查看更多信息 Usage of MethodInvoker in C#
tbxResult.Invoke((MethodInvoker) delegate
{
tbxResult.Text = status;
});
总的来说,我认为 BackgroundWorker
已经过时了,最好使用 async/await
模式。但这太宽泛了。
所以直接的答案是:你的事件处理程序不是在UI线程上执行的,而是在后台工作线程上执行的。从后台工作者重新调用 ui 线程上的操作的正常方法是使用它的 ReportProgress
方法:
public Form1()
{
tbxResult.Text = "Assign text Ok";
bw.DoWork += backgroundWorker_DoWork;
bw.WorkerReportsProgress = true;
bw.ProgressChanged += (sender, e) => tbxResult.Text = (string)e.UserState;
bw.RunWorkerAsync();
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
var status = "assign some value";
bw.ReportProgress(0, status);
}
ReportProgress()
invokes the ProgressChanged
UI 线程上的事件,因此您可以将文本分配给文本框。记得将 WorkerReportsProgress
设置为 true
.
像这样使用 InvokeRequred(或与您的委托一起使用):
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (textBox1.InvokeRequired)
{
textBox1.Invoke((MethodInvoker) delegate { textBox1.Text = @"AAAAAA"; });
}
else
{
textBox1.Text = @"AAAAAA";
}
}
ps:另见Anonymous method in Invoke call