【问题标题】:how do you implement Async Operations in C# and MVVM?你如何在 C# 和 MVVM 中实现异步操作?
【发布时间】:2011-02-01 16:12:21
【问题描述】:

嗨,在 WPF 和 MVVM 上实现异步操作的最简单方法是什么,假设用户在我想启动命令的字段上按 Enter 键,然后返回,同时线程将执行一些搜索操作,然后返回并更新属性,以便通知可以更新绑定。

谢谢!

【问题讨论】:

    标签: c# wpf mvvm asynchronous binding


    【解决方案1】:

    Rob Eisenberg 在他的MIX10 talk 中展示了在 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 - 谢谢你的链接。一段时间以来,我一直试图将我的头包裹在令人敬畏的事物上。.. :)
    【解决方案2】:

    BackgroundWorker 实例在 VM 上调用您的命令怎么样?

    更新: 从上面的建议开始.. Jason Dolinger 有一个关于 MVVM 的在线视频.. 我建议你看看那个。这是一种更简洁的方式,视图很薄/不包含任何线程代码。

    总结一下:

    • VM ctor 缓存 Dispatcher.CurrentDispatcher 对象(主线程)。
    • 更新后备存储(结果)时,使用 _dispatcher.BeginInvoke( () =&gt; _results.AddRange( entries) ) 以便正确更新 UI。

    【讨论】:

    • 绑定是否会在没有解决方法的情况下反映更改?你试过这个方法成功了吗,谢谢 Gishu
    • 如果能链接到包含视频的页面就好了:blog.lab49.com/archives/2650
    【解决方案3】:

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

    【讨论】:

      最近更新 更多