在C#和MVVM中如何实现异步操作?

9

你好,如何在WPF和MVVM中实现异步操作是最简单的方法呢?比方说,如果用户在输入框内按下回车键,我想启动一个命令,并在后台执行某些搜索操作。然后等到线程完成后返回并更新属性,这样通知就可以更新绑定了。

谢谢!

3个回答

8

Rob Eisenberg在他的MIX10演讲中展示了一种非常干净的MVVM异步操作实现方式。他已经在他的博客上发布了源代码。

基本思想是将命令实现为返回IEnumerable并使用yield关键字返回结果。这里是他演讲中的一段代码片段,它作为后台任务执行搜索:

    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();
    }

希望这有所帮助。

@Doug - 感谢您提供的链接。我已经试图理解这个令人惊叹的东西一段时间了.. :) - Gishu

3

你可以考虑使用BackgroundWorker实例在虚拟机上调用命令。

更新: 忽略上面的建议... Jason Dolinger有一段关于MVVM的在线视频.. 我建议你去看一下。这是一种更简洁的方式,其中视图很薄/不包含任何线程代码。

总结:

  • VM构造函数缓存了Dispatcher.CurrentDispatcher对象(主线程)。
  • 当更新后备存储器(结果)时,请使用_dispatcher.BeginInvoke( () => _results.AddRange( entries) )以便正确更新UI。

绑定是否会在没有变通方法的情况下反映出更改?您是否成功尝试过这种方法?谢谢Gishu。 - Oscar Cabrero
3
很希望你能够提供视频页面的链接: http://blog.lab49.com/archives/2650 - OwenP

0
在Shawn Wildermuth的MSDN文章中,他做了类似于这样的事情: 在此处查看文章: http://msdn.microsoft.com/en-us/magazine/dd458800.aspx 以及他更近期的博客文章: 在此处查看文章: 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;
}

使用如下实现:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接