以下代码是我的问题的简化版本:
public partial class Form1 : Form
{
BackgroundWorker bw;
Class myClass = new Class();
public Form1()
{
InitializeComponent();
bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
label1.DataBindings.Add("Text", myClass, "Text", true, DataSourceUpdateMode.Never);
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
myClass.Text = "Hi";
}
private void button1_Click(object sender, EventArgs e)
{
bw.RunWorkerAsync();
}
}
public class Class : INotifyPropertyChanged
{
private string _Text;
private void SetText(string value)
{
if (_Text != value)
{
_Text = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public string Text
{
get { return _Text; }
set { SetText(value); OnPropertyChanged(new PropertyChangedEventArgs("Text")); }
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null) PropertyChanged(this, e);
}
}
当我点击button1(调用button1_Click
)时,label1
中的文字不会更新。这是因为label1.Text
属性在内部尝试从我的BackgroundWorker
的线程更改,这在内部导致异常。一般来说,解决这类问题的最佳方法是什么?如果您必须从另一个线程更新我的Class.Text
属性但是还必须有一个绑定到它的控件,那么您将更改此代码的哪一部分?
答案 0 :(得分:1)
从根本上说,您必须在工作线程中使用Invoke
或BeginInvoke
来执行实际更新。从主UI线程以外的线程中直接与控件进行交互是非法的(除非为此考虑了这一点)。使用lambda语法,使用Invoke
或BeginInvoke
有点比以前更干净。例如:
void bw_DoWork(object sender, DoWorkEventArgs e)
{
Invoke(new Action(() => myClass.Text = "Hi"));
}
这将执行UI线程上的代码。但是,BackgroundWorker
类(我假设您使用的是基于此处的名称)具有ReportProgress
函数,该函数在UI线程上引发ProgressChanged
事件。它使用了我上面描述的相同机制,但看起来可能更清晰。真的是你的选择。
答案 1 :(得分:1)
试试这个:
//This is the handler to execute the thread.
void DoWork(object sender, EventArgs a) {
Action updateControl = ()=>myClass.Text = "Blah";
if(myForm.InvokeRequired) {
myForm.Invoke( updateControl);
}
else {updateControl();}
}
此例程在后台工作线程上执行。
答案 2 :(得分:0)
只允许UI线程更改UI控件的属性。你的BackgroundWorker不应该这样做。
幸运的是,BackgroundWorker支持ProgressChanged事件。您的DoWork应该提高它,并且可以更新UI,因为后台工作人员会将它封送回UI线程。