Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way in WPF MVVM to start a threaded lookup task

So I've got a task that can be preformed by my GUI that will pull information to populate a ViewModel with SQL database query response. Assume I want to start this task and keep my gui free to proceed with other things, and in the meantime play a "searching" animation, what is the proper way to do this in WPF/MVVM? I'm assuming you need to start an asynchronous process and set a bool tied to a datatrigger that starts the animation storyboard. But what do I use to start the process? Thread? I'm still new to WPF and just want to make sure i'm using the right classes available to me.

like image 456
Firoso Avatar asked Aug 18 '09 16:08

Firoso


3 Answers

I use the BackgroundProcess thread to do things like this.

Here's a link to MSDN on this: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

Additional details related to this.

You have three EVENTS associated with your BackgroundProcess object: DoWork, ReportProgress and WorkCompleted.

Now, to use this--and to use it with an observablecollection--you'll want to tell the BackgroundProcess object to be able to REPORT PROGRESS (this is a boolean property that I always explicitly set, along with the allows cancellation).

Now to start off a process, you'll call the RunWorkerAsync method. This method has the ability to accept an OBJECT variable in case you need to pass it data (if you want more than 1 value, create a struct to be passed into RunWorkerAsync).

The RunWorkerAsync fires off the DoWork event, so control heads over to your DoWork event handler. Here's (sanitized) code from where I'm utilizing it:

Dim dt As System.Data.DataTable
dt = da.GetDataTable(sql, System.Data.CommandType.Text, params)
For Each row As System.Data.DataRow In dt.Rows
   If loadQuestionsWorker.CancellationPending Then
       e.Cancel = True
       Exit Sub
   End If
   Dim item As New DataObject
   // Assign Item Values
   backgroundProcessObject.ReportProgress(1, item)
Next

What's happening here, is that I'm getting a datatable from my data layer, and then while this backgroundprocess is not being canceled, I'm walking the data table, and when I build a new DataObject, I report that object as being built.

Now, in my ProgressChanged event handler (the ReportProgress method raises the ProgressChanged event) control is back in the UI thread's hands so I can do things like impact the UI and add the element I'm reporting on to an ObservableCollection.

Finally, in my WorkedCompleted event handler (whose corresponding event is raised when the DoWork event handler method runs to completion) I check to see if my progress was canceled (which sometimes means that I want to dump the ObservableCollection) and I may or may not impact the UI (such as removing a "SEARCHING" animation.

like image 175
Stephen Wrighton Avatar answered Nov 17 '22 15:11

Stephen Wrighton


You can start a new Thread or BackgroundWorker explicitly, or use a ThreadPool. I'm not aware of any recommended way of doing this in MVVM...

If your asynchronous operation is populating an ObservableCollection bound to a control, note that you can't add items to it on a different thread. You need to do it on the control's dispatcher thread. Or you also can use this collection, which raises the CollectionChanged event on the adequate thread

like image 39
Thomas Levesque Avatar answered Nov 17 '22 14:11

Thomas Levesque


var t = new Thread((ThreadStart)delegate
{
    DoWork(...)
    Dispatcher.BeginInvoke((Action)delegate
    {
          SomethingOntheUiThread(...)
          FinishedWork(...);
    }
});

t.Start();
like image 32
Jeremiah Morrill Avatar answered Nov 17 '22 13:11

Jeremiah Morrill