【问题标题】:How to have a blendable project using MVVM-Light and WCF RIA Services如何使用 MVVM-Light 和 WCF RIA 服务创建可混合项目
【发布时间】:2010-07-13 10:09:07
【问题描述】:

我想使用 MVVM 模式构建我的业务应用程序。我选择 MVVM-Light 是因为它符合我的需求。 在我看到的关于 MVVM-Light 的每个示例中,没有人使用 WCF RIA。经典的 MIX10 示例在同一项目中使用服务,而 WCF RIA 在 Web 项目中创建服务。 问题是:构建 WCF Ria 创建的整个 DomainContex 的接口看起来非常困难(这对我来说肯定很难!)但是没有接口我怎么能构建一个假的 DomainContex 用于 Blend 和测试中? 我错过了什么吗? 谢谢。

【问题讨论】:

    标签: wcf mvvm-light


    【解决方案1】:

    实际上,我使用的解决方案是可混合的,并且使混合过程更加简单。

    以下是我如何使用此框架完成此任务的示例:

     public class FolderViewModel : VMBase
    {
        private string _subject = string.Empty;
        private string _folderName = string.Empty;
        private string _service = string.Empty;
        private string _dept = string.Empty;
        private string _efolderid = string.Empty;
        private string _timingName = string.Empty;
        private WorkplanBase _planBase = null;
        private IEnumerable<PSCustomList> _timingOptions = null;
        private decimal _totalvalue = 0;
    
    
        public FolderViewModel()
        {
            registerForMessages();
    
            if (IsInDesignMode)
            {
                // Code runs in Blend --> create design time data.
                EFOLDERID = "0123456790123456790123456791";
                Subject = "9999-00 - This is a test nVision Subject";
                Service = "AUDCOMP";
                Department = "AUDIT";
                FolderName = "NVSN003000";
    
    
                List<PSCustomList> listItems = new List<PSCustomList>();
                listItems.Add(new PSCustomList()
                {
                    ID = "1234",
                    ParameterValue = "Busy Season"
                });
                listItems.Add(new PSCustomList()
                {
                    ID = "1134",
                    ParameterValue = "Another Season"
                });
    
                _timingOptions = listItems.ToArray();
                _totalvalue = 12000;
    
                PlanBase = new WorkplanBase()
                {
                    ClientFee = 15000,
                    Timing = "1234"
                };
            }
        }
    }
    

    然后,所有示例数据都在您的 ViewModelLocator 类中绑定的实际视图模型的构造函数中定义。 VMBase 负责在混合时不尝试实例化 DataContext。

    【讨论】:

      【解决方案2】:

      我发现对我很有效的是以下内容(我从 MVVM light 和 RIA 业务模板中提取了部分内容)。

      我构建了一个新的 ViewModelBase 类,继承自 MVVM Light 的 ViewModelBase 类,我在其中实现了一个 DomainContext、可能挂起的操作列表、当前操作、IsBusy 属性、一个 SaveCommand 和一个受保护的方法来记录 ViewModels 创建的任何操作从这个类继承。

      这是一个例子:

      public class VMBase : ViewModelBase
      {
          protected DomainContext _context;
          protected IList<OperationBase> operations = new List<OperationBase>();
          protected OperationBase currentOperation;
      
          public VMBase()
          {
              if (IsInDesignMode == false)
              {
                  _context = new BudgetContext();
                  _context.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_context_PropertyChanged);
              }
              SaveChangesCommand = new RelayCommand(
                      () =>
                      {
                          currentOperation = _context.SubmitChanges((so) =>
                          {
                              DispatcherHelper.CheckBeginInvokeOnUI(() =>
                              {
                                  OnSaveComplete();
                                  SaveChangesCommand.RaiseCanExecuteChanged();
                                  CancelChangesCommand.RaiseCanExecuteChanged();
                              });
                          },
                              null);
                          logCurrentOperation();
                      },
                      () =>
                      {
                          return _context != null && _context.HasChanges;
                      });
              CancelChangesCommand = new RelayCommand(
                      () =>
                      {
                          _context.RejectChanges();
                          SaveChangesCommand.RaiseCanExecuteChanged();
                          CancelChangesCommand.RaiseCanExecuteChanged();
                      },
                      () =>
                      {
                          return _context != null && _context.HasChanges;
                      });
          }
      
          /// <summary>
          /// This is called after Save is Completed
          /// </summary>
          protected virtual void OnSaveComplete() { }
      
          void _context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
          {
              if (e.PropertyName == "HasChanges")
              {
                  DispatcherHelper.CheckBeginInvokeOnUI(() =>
                  {
                      SaveChangesCommand.RaiseCanExecuteChanged();
                      CancelChangesCommand.RaiseCanExecuteChanged();
                  });
              }
          }
      
          /// <summary>
          /// Bind to Busy Indicator to show when async server
          /// call is being made to submit or load data via the
          /// DomainContext/
          /// </summary>
          public bool IsWorking
          {
              get
              {
                  if (currentOperation != null)
                      return !currentOperation.IsComplete;
                  else
                  {
                      return operations.Any(o => o.IsComplete == false);
                  }
              }
          }
      
          /// <summary>
          /// Call this command to save all changes
          /// </summary>
          public RelayCommand SaveChangesCommand { get; private set; }
          /// <summary>
          /// Revert all changes not submitted
          /// </summary>
          public RelayCommand CancelChangesCommand { get; private set; }
      
          /// <summary>
          /// Occurs after each operation is completed, which was registered with logCurrentOperation()
          /// </summary>
          /// <param name="sender"></param>
          /// <param name="e"></param>
          protected void currentOperation_Completed(object sender, EventArgs e)
          {
              currentOperation = null;
              operations.Remove((OperationBase)sender);
              DispatcherHelper.CheckBeginInvokeOnUI(() =>
              {
                  RaisePropertyChanged("IsWorking");
              });
          }
      
          /// <summary>
          /// Logs and notifies IsBusy of the Current Operation
          /// </summary>
          protected void logCurrentOperation()
          {
              currentOperation.Completed += new EventHandler(currentOperation_Completed);
              operations.Add(currentOperation);
              RaisePropertyChanged("IsWorking");
          }
      
          /// <summary>
          /// Just make sure any async calls are done
          /// </summary>
          public override void Cleanup()
          {
              // Clean own resources if needed
              foreach (OperationBase op in operations)
              {
                  if (op.IsComplete == false)
                  {
                      if (op.CanCancel)
                          op.Cancel();
                  }
              }
      
              base.Cleanup();
          }
      }
      

      然后,在您的实际视图模型中,您可以专注于视图模型的属性和命令 - 所有域上下文已经为您真正连接好了。只需使用 _context 属性 - 例如:

      void loadFolderInfo()
          {
              if (_context != null)
              {
                  EntityQuery<eFolder> query = _context.GetEFoldersByFolderQuery(_efolderid);
                  currentOperation =
                      _context.Load<eFolder>(query,
                      new Action<LoadOperation<eFolder>>((lo) =>
                          {
                              if (lo.HasError)
                              {
                                  Messenger.Default.Send<DialogMessage>(
                                      new DialogMessage(lo.Error.Message, (mbr) =>
                                          {}));
                              }
                              DispatcherHelper.CheckBeginInvokeOnUI(
                                  () =>
                                  {
                                      eFolder myFolder = lo.Entities.First();
                                      Subject = myFolder.eSubject;
                                      FolderName = myFolder.eFolderName;
      
      
                                  });
      
                          }), null);
                  logCurrentOperation();
              }
          }
      

      一个属性可能看起来像这样:

      public string EFOLDERID
          {
              get
              {
                  return _efolderid;
              }
      
              set
              {
                  if (_efolderid == value)
                  {
                      return;
                  }
      
                  var oldValue = _efolderid;
                  _efolderid = value;
      
                  // Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
                  RaisePropertyChanged("EFOLDERID", oldValue, value, true);
                  loadFolderInfo();
              }
          }
      

      关键部分是 VMBase 类处理 DomainContext 的所有连接和管理。为了实现这一点,在您的 ViewModel 实现中,确保将任何 _context.BaseOperationCall(..) 的返回值分配给 currentOperation,然后立即调用 logCurrentOperation。在那之后,它就放手了。然后,您可以将 BusyIndi​​cator 绑定到您的 IsWorking 属性,并且您有一个简化的 RIA 实现。

      希望这可以帮助您入门。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多