The following code is the simplified version of my problem:
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 PropertyChangedEventHa开发者_如何学编程ndler 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);
}
}
What happens is that when I click the button1 (which invokes button1_Click
) the text in label1
doesn't update. This is because the label1.Text
property is internally trying to be changed from my BackgroundWorker
's thread, which internally causes an exception. What would it be the best way, in general, to fix this kind of problem? Which part of this code would you change if you must update my Class.Text
property from a different thread but must also have a control binded to it?
Try this:
//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();}
}
this routine executes on the background worker thread.
Fundamentally, you'll have to use either Invoke
or BeginInvoke
from within your worker thread to perform the actual update. It's illegal to interact directly with a control from within a thread other than the main UI thread (unless it's been written to account for this). With lambda syntax, using Invoke
or BeginInvoke
is somewhat cleaner than before. For example:
void bw_DoWork(object sender, DoWorkEventArgs e)
{
Invoke(new Action(() => myClass.Text = "Hi"));
}
This will execute the code on the UI thread. However, the BackgroundWorker
class (which I'm assuming is what you're using, based on the names here) has a ReportProgress
function, which raises the ProgressChanged
event on the UI thread. It uses the same mechanism that I described above, but it might be a little cleaner looking. The choice is yours really.
Only the UI thread is allowed to change properties of UI controls. Your BackgroundWorker shouldn't be doing it.
Fortunately BackgroundWorker supports a ProgressChanged event. Your DoWork should raise that, and THAT can update the UI because the backgroundworker will marshal it back to the UI thread for you.
精彩评论