I am designing the architecture for a module where a search takes place. the search takes some time, and I would like to have the UI to be responsive, so I get to delegate the retrieval of the data to a class that will do the search on a separate thread.
Then, I can think of two options :
either 1° : the search method returns a viewmodel with empty data as a return value (place holder), but once the search process is over, the viewmodel's members will be updated and the result displayed on screen thanks to data binding,
2° : the search method doesn't have any return type, but once the search process is over, an event is raised and the viewmodel with its final values is passed in the event args so it can be used by the calling code. (and eventually be consumed by a view)
Any thoughts on this one ?
EDIT: Of course, with solution 1° I'm refering to WPF databinding on the objects returned by the sear开发者_如何学编程ch-results "place-holder" objects
If you use a BackgroundWorker
, the design pattern is done for you. Call your search method in the DoWork
event handler, and put the results in the Results
property of the DoWorkEventArgs
that's passed in.
Update the UI with the results in the RunWorkerCompleted
event handler (they'll be in the RunWorkerCompletedEventArgs
object's Results
property).
If you want to update the UI while the search is in progress, have your search method call ReportProgress
and update the UI in the ProgressChanged
event handler. You can put anything you like into the UserState
property of the ProgressChangedEventArgs
, including intermediate search results, though you do have to be careful not to pass any objects that the background thread is going to touch as it continues executing.
I'd take advantage of data binding and bind your search result presentation control to a observable collection of search results and update that collection in your ViewModel from a background worker thread. Then it doesn't required any callbacks or update messages in you code, it just works as you'd expect taking advantage of the plumbing in place in WPF.
You could also leverage Priority Binding (http://msdn.microsoft.com/en-us/library/system.windows.data.prioritybinding.aspx). It provides the fast / slow options and you don't have to worry about update threads. Unless of course you are looking to add some visuals to let the user know something is happening.
Here is what I did on a recent project.
The IsBusy property is something I have in the base class for all my viewmodels. It is a boolean that a view can bind to if it wants to display some kind of waiting control like a spinner or whatever.
The _retrieveData field is just an Action that I set up during the construction of the viewmodel. I do this because the viewmodel may get its list of Cars in several different ways - so _retrieveData may have different code in it based on the constructor that was used. After _retrieveData gets the data, it will set the private backer, _cars with the data. So after _retrieveData is done, setting the public Cars to the value with the new data in _cars causes the PropertyChangedEvent which lets the view know to update itself.
So the effect is that when the view goes to get the data for the first time, it returns immediately but gets null. Then a few seconds later, it gets the actual data. During that time, the UI is responsive. And also, the IsBusy is true if the UI wants to let the user know that it is working in the background.
Not sure if this is a good way to handle it, but it is working for me so far.
public List<Car> Cars
{
get
{
if (this._cars == null)
{
base.IsBusy = true;
// Start a background thread to get the data...
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object nullObject)
{
this._retrieveData.Invoke();
this.Cars = this._cars;
base.IsBusy = false;
}));
// While waiting on the background thread, return null for now. When the background thread
// completes, the setter will raise OnPropertyChanged and the view will know its time to bind...
return this._cars;
}
return this._cars;
}
set
{
this._cars = value;
base.OnPropertyChanged("Cars");
}
}
精彩评论