views:

479

answers:

3

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!

+3  A: 

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.
Gishu
does the binding will reflect the change with out a workaround? have you tried this approach succesfully, thank Gishu
Oscar Cabrero
@Oscar: see update... or better yet watch that video.
Gishu
Would have been nice to link to the page with the video:http://blog.lab49.com/archives/2650
OwenP
@OwenP : thanks for the link.
Gishu
+4  A: 

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.

Doug
@Doug - Thank you for the link. Been trying to wrap my head around the awesomeness for a while now.. :)
Gishu
A: 

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;
    }
  }
}
silverfighter