Silverlight 4.0 Call Long Running Method Asynchronously (MVVM) - silverlight

My Silverlight MVVM App's ViewModel Properties that are poupulated from a service library (not WCF Service).
The library method does some operation that may take some time to complete. After the operation is completed the return value is assigned to the ViewModel property.
Since the View is bound to the ViewModel property it would refresh automatically when the Property Value changes. But, during the operation as expected the UI becomes non responsive because its synchronous operation.
How to do the following operation Asynchronously?
Service Contract & Implementation:
public class ItemsLoadedEventArgs : EventArgs
{
public List<string> Items { get; set; }
}
public interface ISomeService
{
event EventHandler<ItemsLoadedEventArgs> GetItemsCompleted;
void GetItemsAsync();
}
public class SomeService : ISomeService
{
public event EventHandler<ItemsLoadedEventArgs> GetItemsCompleted;
public void GetItemsAsync()
{
// do something long here
// how to do this long running operation Asynchronously?
// and then notify the subscriber of the Event?
// when the operation is completed fire the event
if(this.GetItemsCompleted != null)
{
this.GetItemsCompleted(this, new ItemsLoadedEventArgs { Items = resulthere });
}
}
}
ViewModel:
public class HomeViewModel : ViewModel
{
private ISomeService service;
private ObservableCollection<string> _items;
// Items property is bound to UI
public ObservableCollection<string> Items
{
get { return this._items; }
set
{
this._items = value;
this.RaisePropertyChanged(() => this.Items);
}
}
// DI
public HomeViewModel(ISomeService service)
{
...
this.service = service;
// load items
this.LoadItems();
}
private void LoadItems()
{
this.service.GetItemsCompleted += (s, ea) =>
{
this.Items = new ObservableCollection<string>(ea.Items);
};
this.service.GetItemsAsync();
}
}
Problem:
Since the data is loaded in the constructor, and the operation is synchronous it makes the UI unresponsive.
How to perform the operation inside the GetItemsAsync method of the SomeService class Asynchronously?

Use Dispatcher.BeginInvoke to call your library method. You have to go back to the UI thread to update your ViewModel when it is done. See this question for info on this.

Related

Datagrid remains empty after asynchronous initialization in view model constructor

I have a WPF application with a view containing a data grid and a view model with an observable collection that is initialized by calling an asynchronous method in the constructor. But the data grid remains empty upon running the code.
The view model class looks like this.
internal class MainWindowViewModel : INotifyPropertyChanged
{
private readonly IBookingRecordService service;
public event PropertyChangedEventHandler? PropertyChanged;
private ObservableCollection<BookingRecord> bookingRecords = new();
public ObservableCollection<BookingRecord> BookingRecords
{
get => bookingRecords;
set
{
bookingRecords = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BookingRecords)));
}
}
public MainWindowViewModel()
{
service = new BookingRecordService();
Task.Run(() => LoadBookingRecords());
}
private async Task LoadBookingRecords()
{
BookingRecords = new ObservableCollection<BookingRecord>(await service.Get());
}
}
I all LoadBookingRecords() in the constructor, so that the data starts loading on initialization of the view model already but I do it asynchronously, so it does not block the UI thread and makes the application unresponsive.
I have tried waiting for the completion of the task in the constructor via
Task.Run(() => LoadBookingRecords()).Wait();
to check that this has something to do with the asynchronous function call. And indeed, if I wait for the method to finish in the constructor, the data grid displays correctly. But I don't want to wait for the task to finish on the UI thread because it blocks the UI.
I have read that you must raise the PropertyChanged event on the UI thread to trigger a UI update and I suppose that is the problem here. I have also read that one can use
Application.Current.Dispatcher.BeginInvoke()
to schedule a delegate to run on the UI thread as soon as possible, so I tried the following.
private async Task LoadBookingRecords()
{
await Application.Current.Dispatcher.BeginInvoke(new Action(async () =>
{
BookingRecords = new ObservableCollection<BookingRecord>(await service.Get());
}));
}
But this leaves the DataGrid empty as well.
"'asynchronous ... in constructor" is something you must avoid.
Async calls must be awaited, which can not be done in a constructor. Declare an awaitable Initialize method instead
public Task Initialize()
{
return LoadBookingRecords();
}
and call it in an async Loaded event handler in your MainWindow:
private static async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
await viewModel.Initialize();
}
Alternatively, create a factory method like
public static async Task<MainWindowViewModel> Create()
{
var viewModel = new MainWindowViewModel();
await viewModel.LoadBookingRecords();
return viewModel;
}
and call that in the Loaded handler:
private static async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
DataContext = await MainWindowViewModel.Create();
}
Building on Clemens' answer, I tried something a little different in order to avoid touching the MainWindow code-behind.
I removed the call on LoadBookingRecords in the constructor and instead created a delegate command as a property that holds this method.
internal class MainWindowViewModel : INotifyPropertyChanged
{
private readonly IBookingRecordService service;
private ObservableCollection<BookingRecord> bookingRecords = new();
public ICommand LoadBookingRecordsCommand { get; set; }
public ObservableCollection<BookingRecord> BookingRecords
{
get => bookingRecords;
set
{
bookingRecords = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BookingRecords)));
}
}
public MainWindowViewModel()
{
service = new BookingRecordService();
LoadBookingRecordsCommand = new DelegateCommand(async _ => await LoadBookingRecords());
}
private async Task LoadBookingRecords()
{
BookingRecords = new ObservableCollection<BookingRecord>(await service.Get());
}
}
I then added the NuGet package Microsoft.Xaml.Behaviors.Wpf to the project and added the following namespace to the MainWindow XAML.
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
Finally, I bound the delegate command to the MainWindow's Loaded event.
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadBookingRecordsCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
Now the data grid displays correctly after being loaded.

Return a async list from SQL Server database and use in a datagrid WPF app

I'm currently facing a pretty strange behaviour I'm uncertain whether I do something wrong or not but here is the problem.
I try to return a list asynchronously from a repository and load it into a datagrid in a WPF application using Prism. When I return the list synchronously, it loads just fine, but as soon as I use it async the program run, but the interface will not display and no error is triggered.
If you need more info don't hesitate - thanks in advance.
Here is the code - shortened for simplicity:
The view
<UserControl>
<Grid>
<DataGrid ItemsSource="{Binding Products}" />
</Grid>
</UserControl>
The view model:
public class ProductViewModel : BindableBase, INavigationAware
{
private readonly IProductRepo _repo = new ProductRepo();
public ObservableCollection<Data.Models.Product> Products { get; set; }
public ProductViewModel()
{
Products = new ObservableCollection<Data.Models.Product>(this._repo.GetAll().Result);
}
}
The DbContext:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext()
{
}
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<Product> Products { get; set; }
}
The IProductRepo interface:
public interface IProductRepo
{
Task<List<Product>> GetAll();
}
The ProductRepo class that implements IProductRepo interface:
public class ProductRepo : IProductRepo
{
private readonly ApplicationDbContext _context;
public ProductRepo()
{
this._context = new ApplicationDbContext();
}
public async Task<List<Product>> GetAll()
{
return await this._context.Products.ToListAsync();
}
}
I tried to create an async private method inside the view model to load the data, then call the function in the constructor, but this doesn't work.
public void LoadData()
{
List<Data.Models.Product> ProductFromDB = this._repo.GetAll().Result;
foreach (var p in ProductFromDB)
{
Products.Add(p);
}
}
If you really believe you need to load all data on initialization, then a simple solution would be to load the data from the view.
The point is that calling Task.Result on an asynchronous method will very likely cause a deadlock.
Additionally, a long-running constructor can interfere with or even break the initialization routine of the controls (UI).
Furthermore, not awaiting an async method will lead to undefined behavior as the caller's context continues to execute while the the async method is still running and pending to return.
For example, the caller can now leave the method scope prematurely and the async method can never return.
It's more than best practice to ensure that constructors return fast and are light weight. They shouldn't allocate expensive resources and should not start background threads, which is generally a surprise for the caller.
A constructor is not meant to execute any kind of operations except to initialize its instance members to a reasonable default. Even advanced instance configuration should take place outside the constructor.
Also be aware that ToList or ToListAsync will force the query to be evaluated client-side. This can hurt significantly the application performance especially when the table is big. On the other hand, the database is highly optimized to execute queries efficiently, which makes server-side query evaluation favorable.
Since you are only fetching the complete table, it doesn't really matter in your scenario. But in case of advanced queries it can make the difference.
The following example shows how to execute the asynchronous initialization from the view. Depending on the exact scenario other solution can be applied.
The example assumes that the ProductViewModel is created and exposed by the main view model class. But the caller doesn't matter. Just let the control that creates the instance call the long-running initialization routine:
IInitializable.cs
Introducing as dedicated interface even allows to anonymously call all async and long-running initialization routines on application composition, for example in a dependency injection context.
This generally works in any composition context, for example when using composition to manage the hierarchy of classes.
In case of this example, the composition root of all view models would be the main view model class.
public interface IInitializable
{
Task<bool> InitializeAsync();
bool IsInitialized { get; }
}
ProductViewModel.cs
public class ProductViewModel : BindableBase, INavigationAware, IInitializable
{
private readonly IProductRepo _repo = new ProductRepo();
public ObservableCollection<Data.Models.Product> Products { get; }
public bool IsInitialized { get; private set; }
public ProductViewModel()
{
// Only initialize to a reasonable default.
// Expensive initialization is deferred.
this.Products = new ObservableCollection<Data.Models.Product>();
}
// Allow to defer long-running initialization routines.
// Such routines can be both, synchronous or asynchronous.
public async Task<bool> InitializeAsync()
{
if (this.IsInitialized)
{
return;
}
// You should rename 'GetAll' to 'GetAllAsync'
List<Product> allProducts = await this._repo.GetAll();
allProducts.ForEach(this.Products.Add);
this.IsInitialized = true;
return this.IsInitialized;
}
}
MainWindowViewModel.cs
public class MainWindowViewModel : BindableBase, INavigationAware, IInitializable
{
public ProductViewModel ProductViewModel { get; }
public bool IsInitialized { get; private set; }
private IList<IInitializable> InitializableDependencies { get; }
public MainWindowViewModel()
{
// The container for polymorphic and anonymous iteration
this.InitializableDependencies = new List<IInitializable>();
// Create the composition tree.
// Because expensive initialization of dependencies is deferred,
// the type construction is cheap and guaranteed to return fast.
this.ProductViewModel = new ProductViewModel();
// Store potential IInitialize implementation for later initialization.
// This allows to use polymorphism to improve the code.
// Additionally, we avoid explicit knowledge whether the instantiated type implements this interface or not.
// This enhances extensibility significantly.
TryAddInitializable(this.ProductViewModel);
}
// Store IInitializable implementations anonymously for later iteration.
private bool TryAddInitializable(object instance)
{
if (instance is IInitializable initializableInstance)
{
this.InitializableDependencies.Add(initializableInstance);
return true;
}
return false;
}
// Allow to defer long-running initialization routines.
// Such routines can be both, synchronous or asynchronous.
public async Task<bool> InitializeAsync()
{
if (this.IsInitialized)
{
return;
}
// Cascade IInitializeAsync calls. In context of dependency injection
// this wouldn't be necessary as the IoC container would allow to filter
// all types that implement IInitializable to call InitializeAsync on each type explicitly.
// Because we only need to know the IINitializable interface, the calls are anonymous, allowing easy extension of the code.
foreach (IInitializable initializableInstance in this.InitializableDependencies)
{
// Trigger the cascade of InitializeAsync calls,
// given that the ProductViewModel is the composition root of other view model classes
// or classes that implement IInitializable.
bool isSuccesfullyInitialized = await initializableInstance.InitializeAsync();
}
this.IsInitialized = true;
return this.IsInitialized;
}
}
MainWindow.xaml.cs
The idea is to start the deferred initialization from the FrameworkElement.Loaded event of a reasonable element (in this case this is the MainWindow).
If ProductViewModel is created by another view model class, then you would have to let this view model implement IInitializable too and cascade the InitializeAsync method calls.
partial class MainWindow : Window
{
private MainWindowViewModel ViewModel { get; }
public MainWindow()
{
InitializeComponent();
this.ViewModel = new MainWindowViewModel();
this.DataContext = this.ViewModel;
this.Loaded += OnLoaded;
}
// Use the Loaded event to execute long-running or resource intensive routines
// outside the constructor.
// Define handler as 'async void' to await the async method calls.
private async void OnLoaded(object sender, RoutedEventArgs e)
{
if (this.ViewModel is IInitializable initializableViewModel)
{
// Trigger the cascade of InitializeAsync calls,
// given that the MainWindowViewModel is the composition root of all view model classes
// or classes that implement IInitializable in general.
_ = await initializableViewModel.InitializeAsync();
}
}
}
MainWindow.xaml
<Window>
<ProductView DataGridItemsSource="{Binding ProductViewModel.Products}" />
</Window>

MVVM & business logic Layer

I have a problem with MVVM pattern and binding collection. My ViewModel provides a collection to the View but to get this collection I use this:
public BindingList<Car> BindingListCars { get; set; }
public CarsVm()
{
BindingListVoiture = carServices.ListCars;
}
When I bind my View on this List it's as if I bind directly my View on the Model because they use the same reference. So when I edit one property of a Car, the model is directly edited without using carServices validation method.
What is the best solution to correct this problem ?
Do I have to expose a copy of my Model to my View to not edit directly my Model from the View?
Do I have to use BindingList in my Model and subsribe to ListChanged in my carServices to validate each change?
You should either perform the validation directly in the Car class itself or expose wrapper objects instead of exposing the "real" Car objects to the view.
The following sample code should give you the idea about what I mean:
//the "pure" model class:
public class Car
{
public string Model { get; set; }
}
public class CarService
{
public List<CarWrapper> ListCar()
{
List<Car> cars = new List<Car>(); //get your Car objects...
return cars.Select(c => new CarWrapper(c, this)).ToList();
}
public bool Validate()
{
//
return true;
}
}
public class CarWrapper
{
private readonly Car _model;
CarService _service;
public CarWrapper(Car model, CarService service)
{
_model = model;
_service = service;
}
//create a wrapper property for each property of the Car model:
public string Model
{
get { return _model.Model; }
set
{
if(_service.Validate())
_model.Model = value;
}
}
}
Obviously if you expose an IEnumerable<Car> from your view model for the view to bind, you are effectively bypassing any validation that is dedined outside of the Car class if the view is able to set any properties of the Car class.
Thanks for your answer mm8,
With this solution I have to create one wrapper per class which need outside validation. It add work and during refactoring we have to edit the class and the Wrapper.
What do you think about this solution :
I put my list of vehicle in a binding list
My service subscribe to ListChanged event of this list
My service implement INotifyDataErrorInfo
For each modification in this list validation is executed
If there is an error ErrorsChanged event is raised
The view model subsribe to this event and retrieve error Data.
The view model subsribe to this event and retrieve error Data.
For example :
My services implementation :
public class VehicleServices : INotifyDataErrorInfo
{
private BindingList<Vehicle> _bindingListCar
public BindingList<Vehicle> BindingListCar
{
get return _bindingListCar;
}
private readonly Dictionary<string, ICollection<string>>
_validationErrors = new Dictionary<string, ICollection<string>>();
//INotifyDataErrorInfo implementation
public IEnumerable GetErrors(string propertyName)
public bool HasErrors
private void RaiseErrorsChanged(string propertyName)
public VehicleServices()
{
_bindingListCar = GetVehicles();
_bindingListCar.ListChanged += BindingListVehicleChanged;
}
private void BindingListVehicleChanged(object sender, ListChangedEventArgs e)
{
//Only modification is managed
if (e.ListChangedType != ListChangedType.ItemChanged) return;
switch(e.PropertyDescriptor.Name)
//Validate each property
//if there is ErrorsChanged is raised
}
}
And my ViewModel
public class CarVm : BindableBase
{
private ICollection<string> _errors;
public ICollection<string> Error
{
get
{
return _errors;
}
set
{
SetProperty(ref _errors, value);
}
}
private VehicleServices _carServices;
public BindingList<Vehicle> BindingListCar { get; set; }
public CarVm(VehicleServices carServices)
{
_carServices = carServices;
BindingListCar = new BindingList<Vehicle>(_carServices.BindingListCar);
_carServices.ErrorsChanged += _carServices_ErrorsChanged;
}
private void _carServices_ErrorsChanged(object sender, DataErrorsChangedEventArgs e)
{
Error = _carServices.ValidationErrors[e.PropertyName];
}
}
Do you think this is a good practice ?

How to Notify That View Should Get New Value of Calculated Field

I am working on a WP7 app that displays some times on one page. I have a code behind that has an ObservableCollection of objects. Each object has a calculated property that uses DateTime.Now to determine the time that's displayed on the page. I can't figure out how to "notify" that the property has changed since the property doesn't change, the current time is changing (just once per second). Any ideas? Here's the jist of what I've got:
//my business object
public class Widget
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private DateTime? _start;
public DateTime? Start
{
get { return _start; }
set { _start = value; }
}
public TimeSpan? TimeSinceStart
{
get { return Start.HasValue ? DateTime.Now - Start.Value : default(TimeSpan); }
}
}
//my viewmodel
public class WidgetDisplayerViewModel : BaseViewModel
{
public WidgetDisplayerViewModel()
{
TimeUpdateTimer.Tick += new EventHandler(TimeUpdateTimer_Tick);
TimeUpdateTimer.Interval = new TimeSpan(0, 0, 1);
TimeUpdateTimer.Start();
}
public WidgetDisplayerViewModel(string selectedCategory) : this()
{
Category = MockDataService.GetCategory(selectedCategory);
Category.Widgets = MockDataService.GetWidgets(selectedCategory).ToObservableCollection();
}
public DispatcherTimer TimeUpdateTimer = new DispatcherTimer();
private DateTime _currentTime;
public DateTime CurrentTime
{
get { return _currentTime; }
set {
_currentTime = value;
NotifyPropertyChanged("CurrentTime");
}
}
public Category Category { get; set; }
void TimeUpdateTimer_Tick(object sender, EventArgs e)
{
CurrentTime = DateTime.Now;
}
}
And then the view is very simple and just needs to display the CurrentTime and then for each Widget in the collection it needs to show the TimeSinceStart. The CurrentTime is getting updated each second by the timer and that gets propogated to the view. That one is easy because the timer is setting it and so I have a chance to call NotifyPropertyChanged("CurrentTime"), but how would I "notify" that all of the TimeSinceStart getters should be called to update the calculated value for each Widget since I'm not setting them?
Thanks!
You'll have to manually refresh the property one way or another. I see you already have a timer ticking every second. So I can suggest you two solutions:
1/ Define a "UpdateTime" method in the Widget object. In this method, call NotifyPropertyChanged("TimeSinceStart"). When the timer is ticking, enumerate the list of widgets, and call the UpdateTime method on each.
2/ Create a global object implementing the INotifyPropertyChanged interface, and holding the value of CurrentTime. Make each of your Widget objects subscribe to the PropertyChanged event of this global class to know when the time is updated. Then, when the event is triggered, call NotifyPropertyChanged("TimeSinceStart").
This can be a tricky one to work out and it can get very messy very fast.
I would suggest you stick with your current approach of having only one timer which is initialised in the main viewmodel. You then have to ask yourself the question - does the age (TimeSinceStart) of the Widget belong on the Widget, or is it purely for display/informational purposes? Is it a core piece of information that each Widget must keep during its lifespan?
This looks to me like it is for display purposes only. So my suggestion is this: once you have called GetWidgets, you could enumerate through each Widget and wrap it in a thin viewmodel of its own. The constructor for that viewmodel takes two parameters - the timer from the main viewmodel, and the Widget. You then subscribe to the timer's Tick event, and from that you notify that the TimeSinceStart property has changed.
public class WidgetWrapper : INotifyPropertyChanged
{
public WidgetWrapper(DispatcherTimer timer, Widget widget)
{
_widget = widget;
timer.Tick += TimerTick;
}
private void TimerTick(object sender, EventArgs e)
{
OnPropertyChanged("TimeSinceStart");
}
public Widget Widget { get { return _widget; } }
public TimeSpan? TimeSinceStart
{
get { return _widget.Start.HasValue ? DateTime.Now - _widget.Start.Value : default(TimeSpan); }
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private readonly Widget _widget;
}
public class WidgetDisplayerViewModel : BaseViewModel
{
public WidgetDisplayerViewModel(string selectedCategory) : this()
{
Category = MockDataService.GetCategory(selectedCategory);
var wrappedWidgets = new ObservableCollection<WidgetWrapper>();
MockDataService.GetWidgets(selectedCategory).ForEach(widget => wrappedWidgets.Add(new WidgetWrapper(TimeUpdateTimer, widget)));
Category.Widgets = wrappedWidgets;
}
}
Wrapping a DTO (entity, Data Transfer Object) with its own viewmodel is a quite common approach when adding functionality to an entity. If you use this appoach you will have to slightly modify any UI bindings that were targetting properties on the Widget, as those UI elements will now be dealing with a WidgetWrapper (or you can just surface the required properties in the WidgetWrapper itself, then no bindings have to change).
Invoke the NotifyPropertyChanged method for the specified property.
public DateTime CurrentTime
{
get { return _currentTime; }
set {
_currentTime = value;
NotifyPropertyChanged("CurrentTime");
NotifyPropertyChanged("TimeSinceStart");
}
}
Subscribe all widgets to CurrentTime PropertyChanged event in Widget constructor
private Widget()
{
App.ViewModel.PropertyChanged += (s, e) =>
{
if (e.PropertyName.Equals("CurrentTime")
{
NotifyPropertyChanged("TimeSinceStart");
}
};
}

making async calls using mvvm in silverlight

I am trying to make a call to a wcf service with my silverlight application and I am having some trouble understanding how the model returns the result back to the view model. Within my view model I have the following command:
public DelegateCommand GetSearchResultCommand
{
get
{
if (this._getSearchResultCommand == null)
this._getSearchResultCommand = new DelegateCommand(GetSearchResultCommandExecute, CanGetSearchResultsCommandExecute);
return this._getSearchResultCommand;
}
}
private void GetSearchResultCommandExecute(object parameter)
{
this.SearchResults = this._DataModel.GetSearchResults(this.SearchTerm);
}
/// <summary>
/// Bindable property for SearchResults
/// </summary>
public ObservableCollection<QueryResponse> SearchResults
{
get
{
return this._SearchResults;
}
private set
{
if (this._SearchResults == value)
return;
// Set the new value and notify
this._SearchResults = value;
this.NotifyPropertyChanged("SearchResults");
}
}
then within my model I have the following code
public ObservableCollection<QueryResponse> GetSearchResults(string searchQuery)
{
//return type cannot be void needs to be a collection
SearchClient sc = new SearchClient();
//******
//TODO: stubbed in placeholder for Endpoint Address used to retreive proxy address at runtime
// sc.Endpoint.Address = (clientProxy);
//******
sc.QueryCompleted += new EventHandler<QueryCompletedEventArgs>(sc_QueryCompleted);
sc.QueryAsync(new Query { QueryText = searchQuery });
return LastSearchResults;
}
void sc_QueryCompleted(object sender, QueryCompletedEventArgs e)
{
ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>();
results.Add(e.Result);
this.LastSearchResults = results;
}
When I insert breakpoints within the model I see where the query is being executed and a result is returned within the model (this.LastSearchResults = results) however I cannot seem to get this collection to update/ notify the view model of the result. I've generated and run a similar test using just a method and dummy class and it seems to work so I suspect the issue is due to the async call /threading. I have INotifyPropertyChanged within the ViewModel to sync the View and ViewModel. Do I need to also implement INotifyPropChng within the model as well? I'm new to mvvm so any help / example of how I should approach this would be appreciated.
Thank you,
UPDATE
In further testing I added INotifyPropertyChanged to the model and changed the Completed event as follows:
void sc_QueryCompleted(object sender, QueryCompletedEventArgs e)
{
ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>();
results.Add(e.Result);
//this.LastSearchResults = results;
SearchResults = results;
}
Placing a watch on Search Results I now see it is updated with results from teh WCF. My question is still around is this teh correct approach? It seems to work right now however I am curious if I am missing something else or if I should not be placing INotify within the Model.
Thank you,
I've found that it's best to encapsulate my WCF services in an additional layer of Service classes. This allows me to more easily Unit Test my ViewModels. There are several patterns when doing this, though this is the simplest I've used. The pattern is to create a method that matches the definition of the service call, though also contains an Action that can be invoked after the service call completes.
public class Service : IService
{
public void GetSearchResults(string searchQuery, Action<ObservableCollection<QueryResponse>> reply)
{
//return type cannot be void needs to be a collection
SearchClient sc = new SearchClient();
//******
//TODO: stubbed in placeholder for Endpoint Address used to retreive proxy address at runtime
// sc.Endpoint.Address = (clientProxy);
//******
sc.QueryCompleted += (s,e) =>
{
ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>();
results.Add(e.Result);
reply(results);
};
sc.QueryAsync(new Query { QueryText = searchQuery });
}
}
You can also provide an interface that your ViewModel can use. This makes Unit Testing even easier, though is optional.
public interface IService
{
void GetSearchResults(string searchQuery, Action<ObservableCollection<QueryResponse>> reply);
}
Your ViewModel would then look something like this:
public class MyViewModel : INotifyPropertyChanged
{
private IService _service;
public MyViewModel()
: this(new Service())
{ }
public MyViewModel(IService service)
{
_service = service;
SearchResults = new ObservableCollection<QueryResponse>();
}
private ObservableCollection<QueryResponse> _searchResults
public ObservableCollection<QueryResponse> SearchResults
{
get { return _searchResults; }
set
{
_searchResults = value;
NotifyPropertyChanged("SearchResults");
}
}
public void Search()
{
_service.GetSearchResults("abcd", results =>
{
SearchResults.AddRange(results);
});
}
protected void NotifyPropertyChanged(string property)
{
var handler = this.PropertyChanged;
if(handler != null)
handler(new PropertyChangedEventArgs(property));
}
}
An additional reason for encapsulating your service calls into another class like this is that it can provide a single place for such things as logging and error handling. That way your ViewModel itself doesn't need to take care of those things specifically related to the Service.
I would likely use something along the lines of:
public class ViewModel : INotifyPropertyChanged
{
private readonly IModel model;
private readonly DelegateCommand getSearchResultsCommand;
public DelegateCommand GetSearchResultsCommand
{
get { return getSearchResultsCommand; }
}
public ObservableCollection<QueryResponse> SearchResults
{
get { return model.SearchResults; }
}
public ViewModel(IModel model)
{
this.model = model;
this.model.SearchResultsRetrieved += new EventHandler(model_SearchResultsRetrieved);
this.getSearchResultsCommand = new DelegateCommand(model.GetSearchResultCommandExecute, model.CanGetSearchResultsCommandExecute);
}
private void model_SearchResultsRetrieved(object sender, EventArgs e)
{
this.NotifyPropertyChanged("SearchResults");
}
}
public interface IModel
{
event EventHandler SearchResultsRetrieved;
void GetSearchResultCommandExecute(object parameter);
bool CanGetSearchResultsCommandExecute(object parameter);
ObservableCollection<QueryResponse> SearchResults { get; }
}
With the SearchResultsRetrieved event being fired by the Model when its SearchResults collection has been filled with the appropriate data. I prefer to have custom events rather than implement INotifyPropertyChanged on my models, particularly if there are only one, or a few, events that need to be communicated to the viewmodel.

Resources