I am trying to bind data to a grid using MVVM Light. If I do the following it works:
public class GuidePageViewModel : ViewModelBase
{
private ObservableCollection<SimpleChannelData> _simpleChannelDataList;
private IChannelDataService _channelDataService;
public GuidePageViewModel(IChannelDataService channelDataService)
{
_channelDataService = channelDataService;
ATest(); // Create data synchronously here
}
private void ATest()
{
SimpleChannelDataList = new ObservableCollection<SimpleChannelData>();
var record = new SimpleChannelData()
{
Cn = 120,
Csign = "Hey",
Hd = "Y",
Index = 1,
Premium = "y",
TrbId = 9
};
SimpleChannelDataList.Add(record);
}
private async void Start()
{
await LoadChannelData();
}
private async Task LoadChannelData()
{
SimpleChannelDataList = new ObservableCollection<SimpleChannelData>((await _channelDataService.GetChannelData()));
RaisePropertyChanged(() => SimpleChannelDataList);
}
#region Public Properties
public ObservableCollection<SimpleChannelData> SimpleChannelDataList
{
get
{
return _simpleChannelDataList;
}
set
{
if (Set(() => SimpleChannelDataList, ref _simpleChannelDataList, value))
{
RaisePropertyChanged(() => SimpleChannelDataList);
}
}
}
#endregion
}
Now, if I change the constructor to the following instead, using an asynchronous call to Start, the grid does not get the data:
public GuidePageViewModel(IChannelDataService channelDataService)
{
_channelDataService = channelDataService;
Start();
}
I know this some sort of Async issue, but I cannot figure it out. Can someone point out what I am doing wrong?
At first, you should not call async methods in the constructor. Try to change your logic so that that call is not required.
In mean time, try to check AsyncMVVM. It contains some interesting patterns to retrieve in an async way the data while exposing the usual MVVM interface.
Related
I am developing a windows 8 store app, using C#/xaml, with the MVVM pattern.
I want to refresh a page automatically every 30 seconds, after a search I came up with this, but how do I implement it into a MVVM page?
Edit: As of Charleh's answer I came up with this:
var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
var period = TimeSpan.FromSeconds(30);
var timer = ThreadPoolTimer.CreatePeriodicTimer((source) =>
{
await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
RefreshOrdersList();
});
}, period);
But the VS compiler marks an 'Error' under the dispatcher.RunAsync() function: "The 'await' operator can only be used within an async lambda expression". When I remove the 'await' keyword the application runs with a warning saying "because this call is not awaited, execution of the current method continues before the call is completed, Consider apply the 'await' operator to the result of the call".
RefreshOrdersList function -gets all orders and details from WCF service:
private async void RefreshOrdersList()
{
var orders = await proxy.GetAllOrdersAsync();
IList<OrderCart> orderModel = new List<OrderCart>();
foreach (var order in orders)
{
OrderCart oc = new OrderCart { Id = order.Id, FullPrice = Convert.ToDouble(order.Total), TableId = order.TableId, IsDone = false, OrderTime = (DateTime)order.StartOrderTime };
order.OrderDetails = await proxy.GetOrderDetailsByOrderIdAsync(order.Id);
oc.OrderCartItems = new ObservableCollection<OrderCartItem>();
foreach (var orderDetail in order.OrderDetails)
{
var course = await proxy.GetCourseByIdAsync(orderDetail.CourseId);
OrderCartItem oci = new OrderCartItem { Quantity = orderDetail.Amount, Course = new Course(course) };
oc.OrderCartItems.Add(oci);
}
orderModel.Add(oc);
}
var sortOrder = orderModel.OrderByDescending(x => x.Id);
Items = new ObservableCollection<OrderCartViewModel>(sortOrder.
Select(o => new OrderCartViewModel(o))
.ToList());
Items property:
public ObservableCollection<OrderCartViewModel> Items
{
get { return _items; }
private set { SetProperty(ref _items, value); }
}
Anyone??
The dispatcher is found on UI types (controls etc) and it's job is to sync to the UI thread so you will only find this in your views.
Is there any reason the whole view needs to reload?
If you are using the MVVM pattern, you can just do your work asyncronously in the viewmodel, and then update your properties as per usual.
e.g.
class SomeViewModel
{
IEnumerable<Results> Results { get; set; } // Obviously this would actually need to raise PropertyChanged from INotifyPropertyChanged in order to refresh any bindings
public SomeViewModel()
{
var period = TimeSpan.FromMinutes(1);
var timer = ThreadPoolTimer.CreatePeriodicTimer((source) =>
{
// do your query/work here
DoSomeWorkAsync();
},
period);
}
void DoSomeWorkAsync()
{
// Get the data
someService.GetResults((result) => { Results = result.Data; });
}
}
The binding system will take care of the UI update. I assume you just want to go off to the datasource (a web service maybe?) and get new results every 30 seconds, do you need more than just a data/binding update?
Disclaimer: Didn't test this, and you'd also need to be aware of exceptions thrown within the thread pool work item and handle appropriately
Edit: In response to your question about the binding system
The WPF/RT/SL binding system looks for data sources that implement certain interfaces - one of these is INotifyPropertyChanged. As long as the datasource you are binding to implements this (and in this case your datasource is the viewmodel), and you raise a PropertyChanged event for the property when it changes, the binding system will know to refresh
I have a code snippet in Visual Studio (well actually Resharper as it works a bit better) that writes the property for me - I think there is one included in VS but it should look something like this:
private int _someInt;
public int SomeInt
{
get { return _someInt; }
set
{
if(_someInt != value)
{
_someInt = value;
// Helper function in the class which checks to see if propertychanged has any subscribers
OnPropertyChanged("_someInt");
}
}
}
Note: There are better implementations of this using Expressions instead of 'magic strings' or using new language features. If you are using the latest c# version the addition of CallerMemberName attribute means you don't need to specify the property name
Edit 2: Ok a complete example may look like this
public class SomeViewModel : INotifyPropertyChanged
{
#region Properties (that support INotifyPropertyChanged)
private IEnumerable<Result> _results;
public IEnumerable<Result> Results
{
get
{
return _results;
}
set
{
if (value != _results)
{
_results = value;
OnPropertyChanged("Results");
}
}
}
#endregion
// A reference to the service that will get some data
private IDataService _dataService;
public SomeViewModel(IDataService dataService)
{
_dataService = dataService;
var period = TimeSpan.FromMinutes(1);
var timer = ThreadPoolTimer.CreatePeriodicTimer((source) =>
{
// do your query/work here
GetData();
},
period);
}
#region Data fetch method
/// <summary>
/// Method to get some data - waits on a service to return some results
/// </summary>
void GetData()
{
// Get the data
_dataService.GetResults((result) =>
{
// Try this instead
Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.Invoke(() => {
Results = result.Data; }
});
}
#endregion
#region INotifyPropertyChanged
// Note: usually most MVVM frameworks have a base class which implements the INPC interface for you (such as PropertyChangedBase in Caliburn Micro)
// so it might be worth fishing around in your framework for it if you are using one.. If you aren't using a framework then you are a braver man than me
// start using one now!
/// <summary>
/// The property changed event
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// The helper function to raise the event
/// </summary>
/// <param name="propertyName">Name of the property that changed (to tell the binding system which control to update on screen based on bindings)</param>
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Edit: update code above. Let me know if you can't get hold of the dispatcher still
Just need to add the 'async' keyword var timer = ThreadPoolTimer.CreatePeriodicTimer( async (source)=>, and no errors or warning were detected:
var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
var period = TimeSpan.FromSeconds(30);
var timer = ThreadPoolTimer.CreatePeriodicTimer( async (source) =>
{
await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
RefreshOrdersList();
});
}, period);
In my first view model (renamed to MainViewModel) I have a list of ActionViewModels.
In my xaml i have a listbox which is bound to the list, in the listbox i have a template which binds to properties from the ActionViewModel.
So far so good and everything works.
When selecting one of the listitems i navigate to an ActionViewModel and pass the id with it.
The ActionViewModel retrieves information from a static list in memory from which the MainViewModel also retrieved the information to create the list of actionviewmodels.
So far still so good, i can edit the properties, all the bindings do work fine and i'm all happy.
By clicking the save button the information is gathered and stored in the static list.
When i hit the back button i go back to the list, but unfortunately the values showing there are still the same, is there some way to send a command to reload the items in the list? To pass a complete viewmodel as reference to a new ActionViewModel? Or some property which tells the parent 'this viewmodel in your list has been updated'?
I am sure the above text is a bit confusing, so here is some code to clarify it a bit (hopefully)
MainViewModel.cs
private List<ActionViewModel> _actionViewModels;
public List<ActionViewModel> ActionViewModels
{
get { return _actionViewModels; }
set { _actionViewModels = value; RaisePropertyChanged(() => ActionViewModels); }
}
private Cirrious.MvvmCross.ViewModels.MvxCommand<int> _navigateToAction;
public System.Windows.Input.ICommand NavigateToAction
{
get
{
_navigateToAction = _navigateToAction ?? new Cirrious.MvvmCross.ViewModels.MvxCommand<int>((action) => NavigateToTheDesiredAction(action));
return _navigateToAction;
}
}
private void NavigateToTheDesiredAction(int action)
{
ShowViewModel<ActionViewModel>(new { id = action });
}
// Get DTOs from server or from cache and fill the list of ActionViewModels
public async Task Load()
{
ActionService actionService = new ActionService();
List<ActionViewModel> actionViewModels = new List<ActionViewModel>();
MyActions = await actionService.GetMyActions();
foreach (ActionDTO action in MyActions)
{
ActionViewModel actionViewModel = new ActionViewModel();
await actionViewModel.Load(action.id);
actionViewModels.Add(actionViewModel);
}
ActionViewModels = actionViewModels;
}
ActionViewModel.cs
public int ID
{
get { return TheAction.id; }
set { TheAction.id = value; RaisePropertyChanged(() => ID); }
}
public string Title
{
get { return TheAction.Title; }
set { TheAction.Title = value; RaisePropertyChanged(() => Title); }
}
public async Task Load(int actionId)
{
ActionDTO TheAction = await actionService.GetAction(actionId);
this.ID = TheAction.id;
this.Title = TheAction.Title;
}
private Cirrious.MvvmCross.ViewModels.MvxCommand _save;
public System.Windows.Input.ICommand Save
{
get
{
_save = _save ?? new Cirrious.MvvmCross.ViewModels.MvxCommand(PreSaveModel);
return _save;
}
}
private void PreSaveModel()
{
SaveModel();
}
private async Task SaveModel()
{
ValidationDTO result = await actionService.SaveAction(TheAction);
}
ActionService.cs
public static List<ActionDTO> AllActions = new List<ActionDTO>();
public async Task<ActionDTO> GetAction(int actionId)
{
ActionDTO action = AllActions.FirstOrDefault(a => a.id == actionId);
if (action == null)
{
int tempActionId = await LoadAction(actionId);
if (tempActionId > 0)
return await GetAction(actionId);
else
return new ActionDTO() { Error = new ValidationDTO(false, "Failed to load the action with id " + actionId, ErrorCode.InvalidActionId) };
}
return action;
}
private async Task<int> LoadAction(int actionId)
{
ActionDTO action = await webservice.GetAction(actionId);
AllActions.Add(action);
return action.id;
}
public async Task<ValidationDTO> SaveAction(ActionDTO action)
{
List<ActionDTO> currentList = AllActions;
ActionDTO removeActionFromList = currentList.FirstOrDefault(a => a.id == action.id);
if (removeActionFromList != null)
currentList.Remove(removeActionFromList);
currentList.Add(action);
AllActions = currentList;
return await webservice.SaveAction(action);
}
There are 3 ways I can think of that would allow you to do this.
The ActionService could send out some sort of notification when data changes. One easy way to do this is to use the MvvmCross Messenger plugin. This is the way the CollectABull service works in CollectionService.cs in the N+1 days of mvvmcross videos (for more info watch N=13 in http://mvvmcross.wordpress.com)
This is the approach I generally use. It has low overhead, uses WeakReferences (so doesn't leak memory), it is easily extensible (any object can listen for changes), and it encourages loose coupling of the ViewModel and Model objects
You could implement some kind of Refresh API on the list ViewModel and could call this from appropriate View events (e.g. ViewDidAppear, OnNavigatedTo and OnResume).
I don't generally use this approach for Refreshing known data, but I have used it for enabling/disabling resource intensive objects - e.g. timers
For certain shape of model data (and especially how often it changes), then I can imagine scenarios where this approach might be more efficient than the messenger approach.
You could extend the use of INotifyPropertyChanged and INotifyCollectionChanged back into your model layer.
I've done this a few times and it's worked well for me.
If you do choose this approach, be careful to ensure that all Views do subscribe to change events using WeakReference subscriptions such as those used in MvvmCross binding - see WeakSubscription. If you didn't do this, then it could be possible for the Model to cause Views to persist in memory even after the UI itself has removed them.
Update - solved
The final solution differs a bit from Brandon's suggestion but his answer brought me on the right track.
class State
{
public int Offset { get; set; }
public HashSet<string> UniqueImageUrls = new HashSet<string>();
}
public IObservable<TPicture> GetPictures(ref object _state)
{
var localState = (State) _state ?? new State();
_state = localState;
return Observable.Defer(()=>
{
return Observable.Defer(() => Observable.Return(GetPage(localState.Offset)))
.SubscribeOn(TaskPoolScheduler.Default)
.Do(x=> localState.Offset += 20)
.Repeat()
.TakeWhile(x=> x.Count > 0)
.SelectMany(x=> x)
.Where(x=> !localState.UniqueImageUrls.Contains(x.ImageUrl))
.Do(x=> localState.UniqueImageUrls.Add(x.ImageUrl));
});
}
IList<TPicture> GetPage(int offset)
{
...
return result;
}
Original Question
I'm currently struggling with the following problem. The PictureProvider implementation shown below is working with an offset variable used for paging results of a backend service providing the actual data. What I would like to implement is an elegant solution making the current offset available to the consumer of the observable to allow for resuming the observable sequence at a later time at the correct offset. Resuming is already accounted for by the intialState argument to GetPictures().
Recommendations for improving the code in a more RX like fashion would be welcome as well. I'm actually not so sure if the Task.Run() stuff is appropriate here.
public class PictureProvider :
IPictureProvider<Picture>
{
#region IPictureProvider implementation
public IObservable<Picture> GetPictures(object initialState)
{
return Observable.Create<Picture>((IObserver<Picture> observer) =>
{
var state = new ProducerState(initialState);
ProducePictures(observer, state);
return state;
});
}
#endregion
void ProducePictures(IObserver<Picture> observer, ProducerState state)
{
Task.Run(() =>
{
try
{
while(!state.Terminate.WaitOne(0))
{
var page = GetPage(state.Offset);
if(page.Count == 0)
{
observer.OnCompleted();
break;
}
else
{
foreach(var picture in page)
observer.OnNext(picture);
state.Offset += page.Count;
}
}
}
catch (Exception ex)
{
observer.OnError(ex);
}
state.TerminateAck.Set();
});
}
IList<Picture> GetPage(int offset)
{
var result = new List<Picture>();
... boring web service call here
return result;
}
public class ProducerState :
IDisposable
{
public ProducerState(object initialState)
{
Terminate = new ManualResetEvent(false);
TerminateAck = new ManualResetEvent(false);
if(initialState != null)
Offset = (int) initialState;
}
public ManualResetEvent Terminate { get; private set; }
public ManualResetEvent TerminateAck { get; private set; }
public int Offset { get; set; }
#region IDisposable implementation
public void Dispose()
{
Terminate.Set();
TerminateAck.WaitOne();
Terminate.Dispose();
TerminateAck.Dispose();
}
#endregion
}
}
I suggest refactoring your interface to yield the state as part of the data. Now the client has what they need to resubscribe where they left off.
Also, once you start using Rx, you should find that using synchronization primitives like ManualResetEvent are rarely necessary. If you refactor your code so that retrieving each page is its own Task, then you can eliminate all of that synchronization code.
Also, if you are calling a "boring web service" in GetPage, then just make it async. This gets rid of the need to call Task.Run among other benefits.
Here is a refactored version, using .NET 4.5 async/await syntax. It could also be done without async/await. I also added a GetPageAsync method that uses Observable.Run just in case you really cannot convert your webservice call to be asynchronous
/// <summary>A set of pictures</summary>
public struct PictureSet
{
public int Offset { get; private set; }
public IList<Picture> Pictures { get; private set; }
/// <summary>Clients will use this property if they want to pick up where they left off</summary>
public int NextOffset { get { return Offset + Pictures.Count; } }
public PictureSet(int offset, IList<Picture> pictures)
:this() { Offset = offset; Pictures = pictures; }
}
public class PictureProvider : IPictureProvider<PictureSet>
{
public IObservable<PictureSet> GetPictures(int offset = 0)
{
// use Defer() so we can capture a copy of offset
// for each observer that subscribes (so multiple
// observers do not update each other's offset
return Observable.Defer<PictureSet>(() =>
{
var localOffset = offset;
// Use Defer so we re-execute GetPageAsync()
// each time through the loop.
// Update localOffset after each GetPageAsync()
// completes so that the next call to GetPageAsync()
// uses the next offset
return Observable.Defer(() => GetPageAsync(localOffset))
.Select(pictures =>
{
var s = new PictureSet(localOffset, pictures);
localOffset += pictures.Count;
})
.Repeat()
.TakeWhile(pictureSet => pictureSet.Pictures.Count > 0);
});
}
private async Task<IList<Picture>> GetPageAsync(int offset)
{
var data = await BoringWebServiceCallAsync(offset);
result = data.Pictures.ToList();
}
// this version uses Observable.Run() (which just uses Task.Run under the hood)
// in case you cannot convert your
// web service call to be asynchronous
private IObservable<IList<Picture>> GetPageAsync(int offset)
{
return Observable.Run(() =>
{
var result = new List<Picture>();
... boring web service call here
return result;
});
}
}
Clients just need to add a SelectMany call to get their IObservable<Picture>. They can choose to store the pictureSet.NextOffset if they wish.
pictureProvider
.GetPictures()
.SelectMany(pictureSet => pictureSet.Pictures)
.Subscribe(picture => whatever);
Instead of thinking about how to save the subscription state, I would think about how to replay the state of the inputs (i.e. I'd try to create a serializable ReplaySubject that, on resume, would just resubscribe and catch back up to the current state).
I am doing a program like messenger that has all the contacts in a listbox with the relative states of the contacts.
Cyclic I get a xml with the contacts were updated over time, then updates the states within a class of binding called "Contacts".
The class Contacts has a filter to display only certain contacts by their state, "online, away, busy,.. " but not offline, for example ....
Some code:
public class Contacts : ObservableCollection<ContactData>
{
private ContactData.States _state = ContactData.States.Online | ContactData.States.Busy;
public ContactData.States Filter { get { return _state; } set { _state = value; } }
public IEnumerable<ContactData> FilteredItems
{
get { return Items.Where(o => o.State == _state || (_state & o.State) == o.State).ToArray(); }
}
public Contacts()
{
XDocument doc = XDocument.Load("http://localhost/contact/xml/contactlist.php");
foreach (ContactData data in ContactData.ParseXML(doc)) Add(data);
}
}
Update part:
void StatusUpdater(object sender, EventArgs e)
{
ContactData[] contacts = ((Contacts)contactList.Resources["Contacts"]).ToArray<ContactData>();
XDocument doc = XDocument.Load("http://localhost/contact/xml/status.php");
foreach (XElement node in doc.Descendants("update"))
{
var item = contacts.Where(i => i.UserID.ToString() == node.Element("uid").Value);
ContactData[] its = item.ToArray();
if (its.Length > 0) its[0].Data["state"] = node.Element("state").Value;
}
contactList.ListBox.ItemsSource = ((Contacts)contactList.Resources["Contacts"]).FilteredItems;
}
My problem is that when ItemsSource reassigns the value of the listbox, the program lag for a few seconds, until it has finished updating contacts UI (currently 250 simulated).
How can I avoid this annoying problem?
Edit:
I tried with Thread and after with BackgroundWorker but nothing is changed...
When i call Dispatcher.Invoke lag happen.
Class ContactData
public class ContactData : INotifyPropertyChanged
{
public enum States { Offline = 1, Online = 2, Away = 4, Busy = 8 }
public event PropertyChangedEventHandler PropertyChanged;
public int UserID
{
get { return int.Parse(Data["uid"]); }
set { Data["uid"] = value.ToString(); NotifyPropertyChanged("UserID"); }
}
public States State
{
get { return (States)Enum.Parse(typeof(States), Data["state"]); }
//set { Data["state"] = value.ToString(); NotifyPropertyChanged("State"); }
//correct way to update, i forgot to notify changes of "ColorState" and "BrushState"
set
{
Data["state"] = value.ToString();
NotifyPropertyChanged("State");
NotifyPropertyChanged("ColorState");
NotifyPropertyChanged("BrushState");
}
}
public Dictionary<string, string> Data { get; set; }
public void Set(string name, string value)
{
if (Data.Keys.Contains(name)) Data[name] = value;
else Data.Add(name, value);
NotifyPropertyChanged("Data");
}
public Color ColorState { get { return UserStateToColorState(State); } }
public Brush BrushState { get { return new SolidColorBrush(ColorState); } }
public string FullName { get { return Data["name"] + ' ' + Data["surname"]; } }
public ContactData() {}
public override string ToString()
{
try { return FullName; }
catch (Exception e) { Console.WriteLine(e.Message); return base.ToString(); }
}
Color UserStateToColorState(States state)
{
switch (state)
{
case States.Online: return Colors.LightGreen;
case States.Away: return Colors.Orange;
case States.Busy: return Colors.Red;
case States.Offline: default: return Colors.Gray;
}
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public static ContactData[] ParseXML(XDocument xmlDocument)
{
var result = from entry in xmlDocument.Descendants("contact")
select new ContactData { Data = entry.Elements().ToDictionary(e => e.Name.ToString(), e => e.Value) };
return result.ToArray<ContactData>();
}
}
I developed a similar software: a huge contact list with data (presence and other stuff) updating quite frequently.
The solution I used is different: instead of updating the whole itemssource everytime, that is quite expensive, implement a ViewModel class for each contact. The ViewModel class should implement INotifiyPropertyChanged.
At this point when you parse the XML, you update the ContactViewModel properties and this will trigger the correct NotifyPropertyChanged events that will update the correct piece of UI.
It might be expensive if you update a lot of properties for a lot of contacts at the same time, for that you can implement some kind of caching like:
contactViewModel.BeginUpdate()
contactViewModel.Presence = Presence.Available;
..... other updates
contactViewModel.EndUpdate(); // at this point trigger PropertyCHanged events.
Another point:
keep a separate ObservableCollection bound to the ListBox and never change the itemssource property: you risk losing the current selection, scrollposition, etc.
dynamically add/remove elements from the collection bound to the listbox.
Buon divertimento e in bocca al lupo :-)
Move the downloading and parsing of the contact status information to another thread.
The line where you assigning the ItemsSource I would put in another thread, but remember about invoking or you're gonna have irritating errors.
hi what is the easiest way to implement asynch operations on WPF and MVVM, lets say if user if user hits enter when on a field i want to launch a command and then return back while a thread will do some search operations and then come back and update the properties so notification can update the bindings.
thanks!
Rob Eisenberg showed a really clean implementation of running async operations in MVVM during his MIX10 talk. He has posted the source code on his blog.
The basic idea is that you implement the command as returning an IEnumerable and use the yield keyword to return the results. Here is a snippet of code from his talk, which does a search as a background task:
public IEnumerable<IResult> ExecuteSearch()
{
var search = new SearchGames
{
SearchText = SearchText
}.AsResult();
yield return Show.Busy();
yield return search;
var resultCount = search.Response.Count();
if (resultCount == 0)
SearchResults = _noResults.WithTitle(SearchText);
else if (resultCount == 1 && search.Response.First().Title == SearchText)
{
var getGame = new GetGame
{
Id = search.Response.First().Id
}.AsResult();
yield return getGame;
yield return Show.Screen<ExploreGameViewModel>()
.Configured(x => x.WithGame(getGame.Response));
}
else SearchResults = _results.With(search.Response);
yield return Show.NotBusy();
}
Hope that helps.
How about a BackgroundWorker instance to call your command on the VM ?
Update:
Scratch the above suggestion.. There's an online video on MVVM by Jason Dolinger.. I recommend you take a look at that. It's a cleaner way where the view is thin/ does not hold any threading code.
To summarize:
the VM ctor caches the Dispatcher.CurrentDispatcher object (main thread).
when updating the backing store (results), use
_dispatcher.BeginInvoke( () => _results.AddRange( entries) ) so that the UI is updated correctly.
In Shawn Wildermuth's MSDN Article he did something like this:
check out the article here:
http://msdn.microsoft.com/en-us/magazine/dd458800.aspx
and his more recent blog post here:
http://wildermuth.com/2009/12/15/Architecting_Silverlight_4_with_RIA_Services_MEF_and_MVVM_-_Part_1
public interface IGameCatalog
{
void GetGames();
void GetGamesByGenre(string genre);
void SaveChanges();
event EventHandler<GameLoadingEventArgs> GameLoadingComplete;
event EventHandler<GameCatalogErrorEventArgs> GameLoadingError;
event EventHandler GameSavingComplete;
event EventHandler<GameCatalogErrorEventArgs> GameSavingError;
}
with an implementation like this:
public class GameCatalog : IGameCatalog
{
Uri theServiceRoot;
GamesEntities theEntities;
const int MAX_RESULTS = 50;
public GameCatalog() : this(new Uri("/Games.svc", UriKind.Relative))
{
}
public GameCatalog(Uri serviceRoot)
{
theServiceRoot = serviceRoot;
}
public event EventHandler<GameLoadingEventArgs> GameLoadingComplete;
public event EventHandler<GameCatalogErrorEventArgs> GameLoadingError;
public event EventHandler GameSavingComplete;
public event EventHandler<GameCatalogErrorEventArgs> GameSavingError;
public void GetGames()
{
// Get all the games ordered by release date
var qry = (from g in Entities.Games
orderby g.ReleaseDate descending
select g).Take(MAX_RESULTS) as DataServiceQuery<Game>;
ExecuteGameQuery(qry);
}
public void GetGamesByGenre(string genre)
{
// Get all the games ordered by release date
var qry = (from g in Entities.Games
where g.Genre.ToLower() == genre.ToLower()
orderby g.ReleaseDate
select g).Take(MAX_RESULTS) as DataServiceQuery<Game>;
ExecuteGameQuery(qry);
}
public void SaveChanges()
{
// Save Not Yet Implemented
throw new NotImplementedException();
}
// Call the query asynchronously and add the results to the collection
void ExecuteGameQuery(DataServiceQuery<Game> qry)
{
// Execute the query
qry.BeginExecute(new AsyncCallback(a =>
{
try
{
IEnumerable<Game> results = qry.EndExecute(a);
if (GameLoadingComplete != null)
{
GameLoadingComplete(this, new GameLoadingEventArgs(results));
}
}
catch (Exception ex)
{
if (GameLoadingError != null)
{
GameLoadingError(this, new GameCatalogErrorEventArgs(ex));
}
}
}), null);
}
GamesEntities Entities
{
get
{
if (theEntities == null)
{
theEntities = new GamesEntities(theServiceRoot);
}
return theEntities;
}
}
}