none
在MVVM中实现异步操作 RRS feed

  • 问题

  • 用WPF已经一段时间了,现在新的项目里想用到MVVM,看了一些资料,遇到一些问题,希望大家帮忙看看。

    MVVM很好地解决了数据和UI的耦合,但是在一些常见场景中,MVVM似乎很难解决:异步操作

    操作大量数据的时候,一般用到异步+UI进度条来优化UE,但是MVVM中把数据操作部分移到了ViewModel中,与UI解耦了,于是ViewModel中操作大量数据时,无法让UI知晓,影响了UE。

     

    不知道各位有没有什么解决方案在MVVM下实现异步操作+进度条的?

    2010年11月16日 9:18

答案

  • 你好,

    常见的MVVM模式是通过WPF的数据邦定来同步数据的,在这种情况下,如果我们可以保证被邦定的在VM里面的属性和UI能够同步就可以实现你的要求。至于是什么线程或者什么方法来改变这个VM里的属性值,是不会影响到UI的数据邦定同步的。举一个例子,你要实现在MVVM里面通过多线程来更新UI进度条,下面是一些代码段:

      #region Properties
    
      double _ProgressValue;
      public double ProgressValue
      {
       get { return _ProgressValue; }
       set
       {
        _ProgressValue = value;
        OnPropertyChanged("ProgressValue");
       }
      }
    
      bool _IsStarted;
      public bool IsStarted
      {
       get { return _IsStarted; }
       set
       {
        _IsStarted = value;
        OnPropertyChanged("IsStarted");
       }
      }
    
      #endregion
    
      #region Commands
    
      public DelegateCommand StartCommand
      {
       get
       {
        return new DelegateCommand(
         new Action(
          () =>
          {
           BackgroundWorker worker = new BackgroundWorker();
           worker.DoWork += (o, e) =>
           {
            IsStarted = true;
            for (int i = 0; i < 100; i++)
            {
             worker.ReportProgress(i);
             Thread.Sleep(100);
            }
           };
           worker.ProgressChanged += (o, e) => { ProgressValue = e.ProgressPercentage; };
           worker.RunWorkerCompleted += (o, e) => { IsStarted = false; };
           worker.WorkerReportsProgress = true;
           worker.RunWorkerAsync();
          }), () => { return true; }
         );
       }
      }
    
      #endregion
    
    

    完整的例子你可以从我的Skydrive.com上下载:http://cid-51b2fdd068799d15.office.live.com/self.aspx/.Public/Samples%5E_2010/20101117%5E_MultiThreadsInMVVM.zip

    致,

    Bob Bao


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Are you looking for a typical code sample? Please download All in One Code Framework !
    ---------------------------------
    如果有回复帮助并解决了您的问题,请点击 “标记为答案”,如果没有没有帮助,请随时点击“取消答复标记”
    你在为寻找代码示例而苦恼吗?赶快来下载 All in One Code Framework 微软一站式代码框架 吧!
    • 已标记为答案 懵盛盛 2010年11月22日 1:16
    2010年11月17日 11:03
    版主

全部回复

  • 你好,

    常见的MVVM模式是通过WPF的数据邦定来同步数据的,在这种情况下,如果我们可以保证被邦定的在VM里面的属性和UI能够同步就可以实现你的要求。至于是什么线程或者什么方法来改变这个VM里的属性值,是不会影响到UI的数据邦定同步的。举一个例子,你要实现在MVVM里面通过多线程来更新UI进度条,下面是一些代码段:

      #region Properties
    
      double _ProgressValue;
      public double ProgressValue
      {
       get { return _ProgressValue; }
       set
       {
        _ProgressValue = value;
        OnPropertyChanged("ProgressValue");
       }
      }
    
      bool _IsStarted;
      public bool IsStarted
      {
       get { return _IsStarted; }
       set
       {
        _IsStarted = value;
        OnPropertyChanged("IsStarted");
       }
      }
    
      #endregion
    
      #region Commands
    
      public DelegateCommand StartCommand
      {
       get
       {
        return new DelegateCommand(
         new Action(
          () =>
          {
           BackgroundWorker worker = new BackgroundWorker();
           worker.DoWork += (o, e) =>
           {
            IsStarted = true;
            for (int i = 0; i < 100; i++)
            {
             worker.ReportProgress(i);
             Thread.Sleep(100);
            }
           };
           worker.ProgressChanged += (o, e) => { ProgressValue = e.ProgressPercentage; };
           worker.RunWorkerCompleted += (o, e) => { IsStarted = false; };
           worker.WorkerReportsProgress = true;
           worker.RunWorkerAsync();
          }), () => { return true; }
         );
       }
      }
    
      #endregion
    
    

    完整的例子你可以从我的Skydrive.com上下载:http://cid-51b2fdd068799d15.office.live.com/self.aspx/.Public/Samples%5E_2010/20101117%5E_MultiThreadsInMVVM.zip

    致,

    Bob Bao


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Are you looking for a typical code sample? Please download All in One Code Framework !
    ---------------------------------
    如果有回复帮助并解决了您的问题,请点击 “标记为答案”,如果没有没有帮助,请随时点击“取消答复标记”
    你在为寻找代码示例而苦恼吗?赶快来下载 All in One Code Framework 微软一站式代码框架 吧!
    • 已标记为答案 懵盛盛 2010年11月22日 1:16
    2010年11月17日 11:03
    版主
  • 非常感谢Bob Bao的回复!在学习MVVM的初期,UI和后台数据的解耦的确有很多不太了解的地方,看了你的例子后,顿悟了不少!

    之后还有很多关于MVVM的问题,希望Bob Bao能继续帮助,谢谢!

    2010年11月22日 1:17
  • Bob Bao  你得代码我吓不到 ,你能给我发一个嘛? 我得邮箱 mendeliangyang@163.com 或者 微软得  mendeliangyang@hotemail.com
    2012年3月29日 9:45
  • 谢谢. 我收到了.灰常感谢.Bob Bao 对我一如既往得技术支持.
    2012年3月31日 3:08
  • 我在最近的一个项目里遇到了麻烦(初学者啦)我是用mvvm从数据库里获得数据然后加载数据时前台UI不使用加载条而是使用动画来表示加载过程,我遇到的问题就是运用上述方法吧加载数据的功能放到DoWork里面前台UI还是会造成假死

    public static void LoadMainPanelResources(string _funNum)
            {
                //_resContext = new ResourcesContext();
                EntityQuery<UserMgr_ScreenReport_Item_Name> query = _resContext.GetUserMgr_ScreenReport_Item_NameQuery();
                LoadOperation<UserMgr_ScreenReport_Item_Name> lo = _resContext.Load(_resContext.GetUserMgr_ScreenReport_Item_NameQuery());
                switch (_funNum)
                {
                    case "1":
                        lo.Completed += delegate
                        {
                            foreach (UserMgr_ScreenReport_Item_Name p in lo.Entities)
                            {
                                if (p.ScreenReport_Name.Contains("Menu") || p.ScreenReport_Name.Contains("Label"))
                                {
                                    MainPanelStrings.Add(p.Show_ScreenReport_Item_Name_1);
                                }
                            }
                        };
                        break;
                    case "2":
                        lo.Completed += delegate
                        {
                            foreach (UserMgr_ScreenReport_Item_Name p in lo.Entities)
                            {
                                if (p.ScreenReport_Name.Contains("Menu") || p.ScreenReport_Name.Contains("Label"))
                                {
                                    MainPanelStrings.Add(p.Show_ScreenReport_Item_Name_2);
                                }
                            }
                        };
                        break;
                    case "3":
                        lo.Completed += delegate
                        {
                            foreach (UserMgr_ScreenReport_Item_Name p in lo.Entities)
                            {
                                if (p.ScreenReport_Name.Contains("Menu") || p.ScreenReport_Name.Contains("Label"))
                                {
                                    MainPanelStrings.Add(p.Show_ScreenReport_Item_Name_3);
                                }
                            }
                        };
                        break;
                    case "4":
                        lo.Completed += delegate
                        {
                            foreach (UserMgr_ScreenReport_Item_Name p in lo.Entities)
                            {
                                if (p.ScreenReport_Name.Contains("Menu") || p.ScreenReport_Name.Contains("Label"))
                                {
                                    MainPanelStrings.Add(p.Show_ScreenReport_Item_Name_4);
                                }
                            }
                        };
                        break;
                    case "5":
                        lo.Completed += delegate
                        {
                            foreach (UserMgr_ScreenReport_Item_Name p in lo.Entities)
                            {
                                if (p.ScreenReport_Name.Contains("Menu") || p.ScreenReport_Name.Contains("Label"))
                                {
                                    MainPanelStrings.Add(p.Show_ScreenReport_Item_Name_5);
                                }
                            }
                        };
                        break;
                    case "C":
                        lo.Completed += delegate
                        {
                            foreach (UserMgr_ScreenReport_Item_Name p in lo.Entities)
                            {
                                if (p.ScreenReport_Name.Contains("Menu") || p.ScreenReport_Name.Contains("Label"))
                                {
                                    MainPanelStrings.Add(p.Show_ScreenReport_Item_Name_C);
                                }
                            }
                        };
                        break;
                    default:
                        break;
                }
            }

    以上是我加载数据的代码 希望你能告诉我哪里出了问题...太纠结了

    2013年12月18日 16:17