开发者

wpf app crashes when BackgroundWorker is used

my view model creates a BackgroundWorker in its constructor. BackgroundWorker updates the model's properties from its DoWork event handler. The code below is a contrived example (ViewModelBase is taken almost verbatim from the MVVM paper).


public class MyViewModel : ViewModelBase
{
    public int MyProperty
    {
        get
        {
            return this.my_property;
        }
        private set
        {
            if (this.my_property != value)
            {
                this.my_property = value;
                this.OnPropertyChanged("MyProperty");
            }
        }
    }

    public MyViewModel()
    {
        this.worker = new BackgroundWorker();
        this.worker.DoWork += (s, e) => { this.MyProperty = 1; };
        this.worker.RunWorkerAsync();
    }
}

my view is bound to this view model:


public partial class MyPage : Page
{
    public MyPage()
    {
        InitializeComponent();
        this.DataContext = new MyViewModel();
        }
}

the problem is that my application crashes intermittently when my page is displayed. this definitely has something to do with the worker thread and data binding in XAML. it seems that if i start the worker from inside the Loaded event handler for the page, the problem开发者_JS百科 goes away but since it is hard to reproduce consistently, i am not sure whether this is the right fix.

does any one have any suggestions or ideas what might be the exact cause?

EDIT: i only can reproduce it if i run it without a debugger. and the error in the system log is InvalidOperationException thrown from ShowDialog, which is not much help. under the debugger it runs fine.

thanks for any help konstantin


Since MyProperty is not thread-safe, strange things can happen when two threads try to access it simultaneously. You should either

  1. make it thread safe (for example, by using the lock keyword) or

  2. make sure that its value is only changed in the UI thread.

I would suggest the second approach, since locking is always a tricky thing. In particular, BackgroundWorker already supports passing values back to the UI thread after the work is done:

public void MyViewModel()
{
    this.worker = new BackgroundWorker();
    this.worker.DoWork += (s, e) => { /* do something */ e.Result = 1; };
    this.worker.RunWorkerCompleted += (s, e) =>
    {
        // all of this happens back in the UI thread
        if (e.Error != null)
        {
            // do some error handling... some exception occurred during DoWork
        }
        else
        {
            this.MyProperty = (int)e.Result;
        }
    };
    this.worker.RunWorkerAsync();
}


Sounds like your background worker is trying to update the XAML markup / bindings from the background thread. You must invokde the Dispatcher to pass value back to the main thread like this:

Inside your do work event:

Dispatcher.BeginInvoke(delegate()
{
//UI element (test.Text) being updated with value i.ToString() from background thread
test.Text += i.ToString();
});
}


It might have to do with updating the UI from a background thread, but WPF is supposed to handle this for you: Making sure OnPropertyChanged() is called on UI thread in MVVM WPF app

Read this too: http://www.wintellect.com/CS/blogs/jlikness/archive/2009/12/16/dispatching-in-silverlight.aspx


Firing INotifyPropertyChanged.PropertyChanged or INotifyCollectionChanged.CollectionChanged (usually from ObservableCollection) events on a data bound object from a background thread is not allowed in WPF.

You can get around this by either using Dispatcher.Invoke to set your property or setting the property in a RunWorkerCompleted handler after returning a value from any long running operations (network or calculations) done in the DoWork handler.


i think the problem was that i was updating my properties from inside DoWork event handler. after I have moved the updates to the ProgressChanged handler the problem seems to have disappeared. I even can now update my ObservableCollection from inside ProgressChanged. so it looks like ProgressChanged event handler is invoked on the UI thread by the framework automatically.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜