多线程应用程序上的数据绑定?

时间:2011-02-08 20:01:35

标签: c# .net winforms data-binding

以下代码是我的问题的简化版本:

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属性但是还必须有一个绑定到它的控件,那么您将更改此代码的哪一部分?

3 个答案:

答案 0 :(得分:1)

从根本上说,您必须在工作线程中使用InvokeBeginInvoke来执行实际更新。从主UI线程以外的线程中直接与控件进行交互是非法的(除非为此考虑了这一点)。使用lambda语法,使用InvokeBeginInvoke 有点比以前更干净。例如:

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线程。