views:

61

answers:

3

I'm writing a WPF app using an MVVM approach. In my ViewModel, I have an ObservableCollection. Periodically, my app needs to check for new messages, and, if there are any, it needs to add them to the ObservableCollection.

If I try to add to the ObservableCollection in the DoWork, it doesn't work because of thread synchronization issues. It looks like the easiest way to get this to happen on the UI thread is to do a ReportProgress() and update the collection from there.

My question is: Philosophically and technically, is it ok to update the UI from the ReportProgress handler, even though "by the letter of the law" I'm not actually reporting progress.

Is there a more sound way to do this?

* EDIT: Code works using Dispatcher Timer *

ViewModel

class MyViewModel
{
    public ObservableCollection<string> MyList { get; set; }

    public MyViewModel()
    {
        MyList = new ObservableCollection<string>();
    }
}

"teh codez" - just a sample, not my actual application code.

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {

        MyViewModel mvm = new MyViewModel();
        this.DataContext = mvm;

        DispatcherTimer mytimer = new DispatcherTimer();
        mytimer.Interval = TimeSpan.FromSeconds(5.0);
        mytimer.Tick += new EventHandler(mytimer_Tick);

        mytimer.Start();


    }

    void mytimer_Tick(object sender, EventArgs e)
    {
        ((DispatcherTimer)sender).Stop();

        MyViewModel mvm = this.DataContext as MyViewModel;
        mvm.MyList.Insert(0, DateTime.Now.ToLongTimeString());

        ((DispatcherTimer)sender).Start();
    }

This looks like it will work well for what I need, and doesn't give me additional baggage that I'm not going to use (e.g. cancel, workercompleted, etc.).

A: 

Sure. That's what the UserState of the EventArgs is designed for.

The reason for the BackgroundWorker is to simplify multithreading. Otherwise, you'd have to do something like Invoke (or BeginInvoke) to handle the same thing the BackgroundWorker is being used for.

Wonko the Sane
+1  A: 

I wouldn't do it

It "feels" like something that will cause problems in the future (maybe when someone else tries to add features years from now, maybe when this code is upgraded or ported to an environment with slightly different ReportProgress implementation).

I would just use Dispatcher.BeginInvoke (I also try to avoid using Invoke because it makes one thread wait for the other and reduce the efficiency of using multi-threading in the first place).

But, there are places where just using ReportProgress is the right choice, you need to decide for yourself what is the best thing for your specific situation (the thing I hate most is valuing "best practices" or "architecture" more then producing actual working software)

Nir
I'm not sure I agree that it "feels" dirty. It sounds like he is using it to do a specific task. And, the original question is whether it was ok to update the UI from the ReportProgress handler - I say yes, only because this was what the BackgroundWorker (and the ReportProgress event handler) was designed for - a simplified way to acheive multi-threading while updating the UI. Again, you are certainly correct in YMMV.
Wonko the Sane
+1 for the working software comment. I'd be much happier if the API guys at MSFT had decided to call it "UpdateUI" or "ThreadSynchronizer", but they made the API method signature very single-purpose looking. I'll leave the question open for a couple more days and see if anyone else weighs in. Thanks for the suggestions thus far.
Robaticus
+1  A: 

If you're implementing polling-type functionality in your app, it probably makes more sense to use a DispatcherTimer that fires at an interval instead of constantly looping inside of the BackgroundWorker.

http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchertimer.aspx

Craig Vermeer
Interesting. I like it. Editing code in my message to show what I wound up with.
Robaticus