Связывание данных в многопоточном приложении? - PullRequest
2 голосов
/ 08 февраля 2011

Следующий код является упрощенной версией моей проблемы:

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);
    }
}

Что происходит, когда я нажимаю кнопку 1 (которая вызывает button1_Click), текст в label1 не обновляется. Это связано с тем, что свойство label1.Text внутренне пытается измениться из потока моего BackgroundWorker, что внутренне вызывает исключение. В общем, как лучше всего решить эту проблему? Какую часть этого кода вы бы изменили, если вы хотите обновить мое свойство Class.Text из другого потока, но к нему также должен быть привязан элемент управления?

Ответы [ 3 ]

1 голос
/ 08 февраля 2011

Попробуйте:

//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();}

}

эта процедура выполняется в фоновом рабочем потоке.

1 голос
/ 08 февраля 2011

По сути, вам придется использовать либо Invoke, либо BeginInvoke из вашего рабочего потока, чтобы выполнить фактическое обновление. Запрещено напрямую взаимодействовать с элементом управления из потока, отличного от основного потока пользовательского интерфейса (если это не было записано для учета этого). При использовании лямбда-синтаксиса использование Invoke или BeginInvoke делает несколько чище, чем раньше. Например:

void bw_DoWork(object sender, DoWorkEventArgs e)
{
    Invoke(new Action(() => myClass.Text = "Hi"));
}

Это выполнит код в потоке пользовательского интерфейса. Однако класс BackgroundWorker (который, как я предполагаю, используется вами на основе приведенных здесь имен) имеет функцию ReportProgress, которая вызывает событие ProgressChanged в потоке пользовательского интерфейса. Он использует тот же механизм, который я описал выше, но он может выглядеть немного чище. Выбор действительно за вами.

0 голосов
/ 08 февраля 2011

Только поток пользовательского интерфейса может изменять свойства элементов управления пользовательского интерфейса.Ваш BackgroundWorker не должен этого делать.

К счастью, BackgroundWorker поддерживает событие ProgressChanged.Ваш DoWork должен поднять это, и ТА может обновить пользовательский интерфейс, потому что фоновый работник перенаправит его обратно в поток пользовательского интерфейса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...