【问题标题】:How should I handle this common UI scenario in MVVM?我应该如何处理 MVVM 中的这种常见 UI 场景?
【发布时间】:2011-04-01 17:12:40
【问题描述】:

场景:

  1. 父窗口加载、填充数据(网格、标签等)
  2. 用户单击按钮,在父窗口的 ViewModel 中触发绑定命令,启动编辑对话框
  3. 用户在编辑对话框中进行更改并单击“接受”,将一些信息保存到数据库中
  4. 父窗口的 ViewModel 识别出某些事情发生了(注意到 dialog.DialogResult == true),它应该刷新它的数据和任何相关的绑定

还要考虑到步​​骤 3 可能由于某种原因失败(复杂的业务逻辑规则可能会失败,用户需要调整一些东西,数据库更新可能会抛出异常,僵尸入侵等),所以编辑对话框可能有保持开放。

ViewModel 不应该(据我了解)对其关联的 View 了解太多/任何信息,因此 ViewModel 很难与 View 进行通信:

“嘿,保存操作成功完成!继续关闭吧!”

或者:

“哇!有一个问题需要解决,然后才能关闭……等一下。”

问题:

如何以尽可能少的xaml.cs 代码(最好没有代码)优雅地 完成这样的事情?

【问题讨论】:

    标签: wpf user-interface mvvm


    【解决方案1】:

    我推出了自己的窗口加载器,我的回答中对此进行了描述:

    Managing multiple WPF views in an application

    【讨论】:

      【解决方案2】:

      好问题。
      MVVM 模式规定视图应该数据绑定到 ViewModel.Properties。 View 可以通过调用 ViewModel.Commands (View => VM) 来触发操作。
      因此,ViewModel 将某些内容传回给 View 的唯一方法是通过属性(即 PropertyChangedNotifications)。 View 必须绑定到 VM 上的某些属性,并在发生更改时决定是否自行关闭。

      【讨论】:

      • 我在多个项目中采用的解决方案是将窗口本身作为命令参数传递给可能会关闭窗口的 ViewModel 命令。虽然并不完美,但到目前为止,它使用最少的代码(并且没有 xaml.cs 代码)。
      【解决方案3】:

      通常在执行此类操作时,我会将对话窗口的打开交给一个服务,该服务可以稍后被模拟以进行测试。比如:

      public interface IDialogProvider
      {
            bool OpenDialog(IViewModel viewModel)
      }
      

      然后可以将传入的viewmodel绑定为模态窗口的DataContext,并通过数据模板使用正确的视图。

      当命令被触发打开子视图时,父视图模型可以使用该服务。孩子可以进行所需的所有处理,并且可以从孩子那里获得任何所需的信息以得出真实的结果:

      public void ExecuteEdit()
      {
          ChildViewModel childViewModel = GetViewModelForSelectedItem();
          if (_dialogProvider.OpenDialog(childViewModel)
          {
              //child view model saved, trigger rebinding etc...
          }
      }
      

      如果您有这些对话框使用的视图模型的特定基类,要处理错误情况,您可以在后面添加一些代码来处理窗口的关闭。我会在视图模型中使用与视图无关的事件,并在触发此事件时关闭窗口。在视图中:

      public DialogWindowView()
      {
          InitializeComponent();
          DataContexctChanged += HandleDataContextChanged;
      }
      
      private void HandleDataContextChanged(object sender, EventArgs e)
      { 
          IDialogViewModel viewModel = DataContext as IDialogViewModel;
      
          if (viewModel != null)
          {
               viewModel.ActionSuccessful += HandleActionSuccessful
          }
      }
      
      private void HandleActionSuccessful(object sender, EventArgs e)
      {
           DialogResult = DialogResult.OK;
           Close();
      } 
      

      我对这个代码作为视图完全没有问题,因为视图模型不负责处理事物的外观,包括打开或关闭窗口。如果您完全反对隐藏代码的想法,或者您不想将视图耦合到视图模型类型,则可以将责任转移到 DialogProvider:

      public class DialogProvider : IDialogProvider
      {
        public bool OpenDialog(IDialogViewModel viewModel)
        {
             Window dialog = new DialogWindow();
             dialog.DataContext = viewModel;
             bool success = false;
             viewModel.ActionSuccessful = (o, e) => 
               {
                    dialog.Close();
                    success = true;
               }
      
            dialog.ShowDialog();
      
            return success;
        }
      }
      

      【讨论】:

      • 我会把它放在对话框提供程序中。这是一个经典的行为,可能你所有的模态对话框都会使用......
      【解决方案4】:

      您的 EditDialog 可以具有对话框可见性绑定到的 IsVisible 属性。如果保存失败,则将 IsVisible 属性设置为 true,否则将其设置为 false。由于它是绑定的,它应该在保存成功时自动隐藏 Popup,如果保存失败则保持打开状态。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-14
        • 1970-01-01
        • 2012-12-16
        • 2021-04-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-20
        • 1970-01-01
        相关资源
        最近更新 更多