Переплет, нити и формы вин - PullRequest
4 голосов
/ 10 апреля 2009

Как связать ProgressBar со свойством класса, обновленного в другом потоке?

В следующем примере кода показана моя первая наивная попытка. Это не работает, потому что я получаю ошибки во время выполнения из-за межпоточного взаимодействия. Я думаю, что мне нужно каким-то образом использовать Invoke, но я не уверен, как это сделать с классом Binding.

using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;

class ProgressForm : Form
{
    private ProgressBar pbProgress;

    public ProgressForm(ref LongOp lo)
    {
        Binding b = new Binding("Value", lo, "Progress");
        pbProgress = new ProgressBar();
        pbProgress.DataBindings.Add(b);
        this.Controls.Add(pbProgress);
    }
}

class Program : Form
{
    private Button btnStart;
    private LongOp lo;

    public Program()
    {
        lo = new LongOp();
        btnStart = new Button();

        btnStart.Text = "Start long operation";
        btnStart.Click += new EventHandler(btnStart_Click);

        this.Controls.Add(btnStart);
    }

    private void btnStart_Click(object sender, EventArgs e)
    {
        ProgressForm pf = new ProgressForm(ref lo);
        lo.DoLongOp();
        pf.ShowDialog();
    }

    [STAThread]
    public static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Program());
    }
}

class LongOp : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private int progress;

    public void DoLongOp()
    {
        Thread thread = new Thread(new ThreadStart(this.run));
        thread.Start();
    }

    public void run()
    {
        for (int i = 0; i < 10; ++i)
        {
            Thread.Sleep(1000);
            Progress++;
        }
    }

    public int Progress
    {
        get
        {
            return progress;
        }

        set
        {
            progress = value;
            NotifyPropertyChanged("Progress");
        }
    }

    private void NotifyPropertyChanged(String field)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(field));
        }
    }
}

Так как мне связать ProgressBar со значением, обновленным в другом потоке?

Заранее спасибо

РЕДАКТИРОВАТЬ: Я перешел на использование реализации ThreadedBinding, которую написал Грэвелл и связался с ней. Хотя я все еще получаю исключение между потоками. Нажатие «Break» в диалоговом окне исключений выделяет строку PropertyChanged(this, new PropertyChangedEventArgs(field)); как строку, вызывающую исключение.

Что еще мне нужно изменить?

РЕДАКТИРОВАТЬ: Похоже, сообщение мистера Грэвелла было удалено. Упомянутая выше реализация ThreadedBinding может быть найдена в конце этого потока: http://groups.google.com/group/microsoft.public.dotnet.languages.csharp/browse_thread/thread/69d671cd57a2c7ab/2f078656d6f1ee1f?pli=1

Я переключился на простой старый Binding в примере для упрощения компиляции другими.

1 Ответ

2 голосов
/ 11 апреля 2009

К сожалению, я думаю, что из-за проблем с многопоточностью правильное связывание данных будет слишком неуклюжим, чтобы использовать его здесь, и, вероятно, будет более сложным, чем вам нужно в любом случае - данные нужно обрабатывать только одним способом.

Вы можете просто заменить привязку на обработчик событий, например так:

private void ProgressPropertyChangedHandler(object sender,
                                            PropertyChangedEventArgs args)
{
    // fetch property on event handler thread, stash copy in lambda closure
    var progress = LongOp.Progress;

    // now update the UI
    pbProgress.Invoke(new Action(() => pbProgress.Value = progress));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...