BackgroundWorker in Silverlight ViewModel

2019-09-06 17:21发布

问题:

I have a TabControl in a Silverlight View and I'm loading Tabs by binding its ItemsSource to a TabItems ObservableCollection in my ViewModel.

To open a new tab, it takes a little while to create it and load it and I want to run a BusyIndicator meanwhile.

As they are created and loaded in the ViewModel, it doesn't show up if I set IsBusy property before calling the "load" method because it's not asynchronous.

I've tried to set it in a backgroundworker but it crashes when I create the TabItem to add it to the TabItems list (UnauthorizedAccessException).

Any idea??? Thanks in advance.

回答1:

Use a Task (AsyncCtp with Silverlight 4):

public void Load(){
    this.IsBusy = true;
    Task.Factory.StartNew(()=> DoHeavyWork())
        .ContinueWith( t => this.IsBusy = false);
}

It gets even better if you can use the new async/await features with the Async CTP or with VS2012/Silverlight 5

public async void Load(){
    try{
        this.IsBusy = true;

        await Task.Factory.StartNew(()=> DoHeavyWork());
    }
    finally
    {       
        this.IsBusy = false;
    }
}

EDIT

I assume you are updating the ObservableCollection in the background task. That will indeed give you problems because the handlers that deal with the collection update are not run in the UI thread, so the collection update is not thread safe as the binding system is. For that to work, you will have to add the items to the ObservableCollection in the UI thread. If you can fetch all your items at once, you can do this:

public async void Load(){
    try{
        this.IsBusy = true;

        // Returns the fetched items
        var items = await Task.Factory.StartNew(()=> DoHeavyWork());

        // This will happen in the UI thread because "await" returns the
        // control to the original SynchronizationContext
        foreach(var item in items)
            this.observableCollection.Add(item);
    }
    finally
    {       
        this.IsBusy = false;
    }
}

If you have to load in batches, you can add the items to the collection using the current Dispatcher, like I suggested in this answer.



回答2:

This happens when you try to access the elements of UI, or the collections which alters the data which in turn reflects on UI. This is actually the cross thread exception. When you create a background worker, you are basically creating another thread. Therefore, you cannot access the main UI thread's variable from Background worker.