【问题标题】:Standard Approach of Launching Dialogs/Child Windows from a WPF Application using MVVM使用 MVVM 从 WPF 应用程序启动对话框/子窗口的标准方法
【发布时间】:2013-06-22 22:27:07
【问题描述】:

所有,我想知道使用 MVVM 模式从 WPF 启动 [子] 对话框/窗口的公认最佳方法/行业标准。我遇到过以下文章:

A. CodeProject - Showing Dialogs When Using the MVVM Pattern

这种方法看起来不错,但对我来说太过分了。这是某种程度的代码复制,我不相信这是正确的方法。

B. WPF MVVM and Showing Dialogs

这简要介绍了三个选项,其中包含各种链接,这些链接在解释方法方面都相当/非常差或与主题无关。

有人可以解释行业标准使用 MVVM 从 WPF 应用程序启动对话框的方法/方法,最好是一些指向进一步阅读材料的链接?如果你能自己提供一个例子,我当然会非常感激!

感谢您的宝贵时间。

【问题讨论】:

  • 没有行业标准,也不可能有。最普遍接受的解决方案是使用 Dialog 服务。

标签: c# wpf mvvm dialog


【解决方案1】:

创建对话服务对我来说效果很好,并且在您的两个链接中也有建议。

后来我在 an MVVM presentation by Gill Cleeren 的开发日看到了相同的解决方案。检查工作代码示例的链接(虽然是为 Metro 编写的)

对话服务唯一让我有点恼火的是它在某种程度上依赖于 UI 技术(富客户端)。

可以在 WPF XAML 绑定到的相同 ViewModel 和 Model 代码之上构建一个简单的请求-响应 Web 前端视图。直到 ViewModel 开始通过对话服务弹出对话。我不知道如何为 Web 视图实现对话服务。在那里实现对话框需要向视图推送更多逻辑。

【讨论】:

    【解决方案2】:

    使用接口实现对话框的目的是使代码可测试。在这种情况下,“A”被广泛使用,但仍然很难说“标准”。如果你没有对你的 ViewModel 进行测试,或者你可以测试你的 ViewModel 避免触摸对话框,例如使用 Extract-Override,你绝对可以不按照说明进行操作。

    【讨论】:

      【解决方案3】:

      首先,我不知道任何使用 MVVM 显示对话框的“行业标准”方式,因为没有这样的东西。
      其次,欢迎使用 MVVM,您刚刚谈到了 MVVM 没有标准的领域。
      说实话,MVVM 有很多痛点,这就是为什么有大量 MVVM 框架存在的原因,仅举几个 MVVM Light、PRISM、Caliburn.Micro、Cinch、Catel、WAF、Baboon、shell i停下来,或者你想要更多。
      现在回答你的问题,在处理了大多数框架之后,我注意到一个共性,它们都使用 DI/IoC 容器,然后为你提供一个接口,比如 IDialogManager 和他们自己的实现,然后他们问你在您的视图模型中接受此接口并使用它来显示对话框。所以总结一下,我会使用依赖注入,有一个显示对话框的接口,然后提供和实现它,并将它注册到 di 容器,然后从我的视图模型或视图中使用它。
      编辑:所以你选择了PRISM (在我看来)在显示对话框方面是它们中最难的。撇开这一点不谈,还有一个困难的方法是使用Interaction Requests(查看文章中间),或者您可以使用这个Answer 作为更快的方法。

      【讨论】:

      • 我还没有就实施做出决定。我想朝着你建议的方向前进并下载了 Prism。但是,查看源代码我看不到您引用的模块。您能否提供对我需要检查的 Prism 类的明确引用?
      【解决方案4】:

      最近,我实现了自己的 WPF 导航服务,它使用 Caliburn.Micro 的 WindowManager(但您可以用其他东西替换它)。

      示例(如何使用):

      _navigationService.GetWindow<ClientDetailsViewModel>()
                  .WithParam(vm => vm.IsEditing, true)
                  .WithParam(vm => vm.Client, SelectedClient)
                  .DoIfSuccess(() => RefreshSelectedClient())
                  .ShowWindowModal();
      

      实施:

      namespace ClientApplication.Utilities
      {
          public class NavigationService : INavigationService
          {
              SimpleContainer _container;
              IWindowManager _windowManager;
      
              public NavigationService(SimpleContainer container, IWindowManager windowManager)
              {
                  _container = container;
                  _windowManager = windowManager;
              }
      
              public INavigationService<TViewModel> GetWindow<TViewModel>()
              {
                  return new NavigationService<TViewModel>(_windowManager, (TViewModel)_container.GetInstance(typeof(TViewModel), null));
              }
          }
      
      
      
      
          public class NavigationService<TVM> : INavigationService<TVM>
          {
              IWindowManager _windowManager;
              TVM _viewModel;
              System.Action _action;
      
              public NavigationService(IWindowManager windowManager, TVM viewModel)
              {
                  _windowManager = windowManager;
                  _viewModel = viewModel;
              }
      
              public INavigationService<TVM> WithParam<TProperty>(Expression<Func<TVM, TProperty>> property, TProperty value)
              {
                  var prop = (PropertyInfo)((MemberExpression)property.Body).Member;
                  prop.SetValue(_viewModel, value, null);
      
                  return this;
              }
      
              public INavigationService<TVM> DoBeforeShow(Action<TVM> action)
              {
                  action(_viewModel);
                  return this;
              }
      
              public INavigationService<TVM> DoIfSuccess(System.Action action)
              {
                  _action = action;
                  return this;
              }
      
              public void ShowWindow(IDictionary<string, object> settings = null)
              {
                  _windowManager.ShowWindow(_viewModel, null, settings);
              }
      
              public bool ShowWindowModal(IDictionary<string, object> settings = null)
              {
                  bool result = _windowManager.ShowDialog(_viewModel, null, settings) ?? false;
                  if (result && _action != null)
                      _action();
      
                  return result;
              }
          }
      }
      

      接口:

      namespace Common
      {
          public interface INavigationService<TVM>
          {
              INavigationService<TVM> WithParam<TProperty>(Expression<Func<TVM, TProperty>> property, TProperty value);
      
              INavigationService<TVM> DoIfSuccess(System.Action action);
      
              INavigationService<TVM> DoBeforeShow(Action<TVM> action);
      
              void ShowWindow(IDictionary<string, object> settings = null);
      
              bool ShowWindowModal(IDictionary<string, object> settings = null);
          }
      
      
      
          public interface INavigationService
          {
              INavigationService<TViewModel> GetWindow<TViewModel>();
          }
      }
      

      【讨论】:

        【解决方案5】:

        最新版本的 Prism (download here) 包含一个名为“Stock Trader”的 MVVM 应用程序的所谓“参考实现”。我的理由是,如果 Prism 团队将其称为“参考实现”,那么从他们的角度来看,这是最“标准”的(如果 MVVM 中的任何东西都是标准的),并且是继续推进的合乎逻辑的选择。

        源代码包含一个用于引发模式对话框的基础设施库,它非常好。所以我采用了该库并成功部署了它(我将这样的应用程序上传到 Codeplex)。

        我需要将代码调整为1 将父图标添加到标题栏,因为库没有提供它; [2] 将一些有意义的文本添加到标题栏,因为库将其留空,并且 [3] 添加一个委托以在对话框关闭时调用。它被抽象到 VM 可以通过将两个字符串(即 Unity 注册名称)传递给中介来引发对话。 Codeplex 上提供了这些更改。

        因此,在所有其他“标准”中,“参考实施”应作为可行的选择最少参与。对您的问题的更直接的回答是,如果您的视图模型足够隔离并且完全通过 POCO 接口工作,那么在理论中,这应该没关系,因为切换到另一个“标准”应该是一个微不足道的练习。

        【讨论】:

        • 你能在 CodePlex 上引用你的应用吗?非常感谢您抽出宝贵时间。
        • tyburnphotobrowser.codeplex.com 与我的答案相关的代码位于 Infrastructure\Behaviour 目录以及主应用程序目录的 Resource Dictionary 中。请记住将目标设置为 4.5
        • 一件事,加里。在 WinForms(即使对于大型应用程序)中,我很少需要将解决方案拆分为多个项目。在您的解决方案中,您将组件拆分为大量单独的项目,为什么?我不是在批评,而是真的很想在这里了解最佳实践。再次感谢。
        • 如果组件太多无法适应您的风格,您可以将它们组合起来。然而,在 Codeplex 上,人们通常喜欢解决一个非常具体的问题(比如打开一个对话框 :)),并希望深入到相关的程序集,而不需要巨石重构。另外,我是隔离的忠实拥护者,因为它使事情易于测试,并且易于“重用”。当 VS 加载项目以支持设计质量时,我很高兴受到性能的影响。请阅读@jerryjvl 答案:stackoverflow.com/questions/1192004/…
        【解决方案6】:

        我只是使用一个对话服务,见here

        在您的视图模型中,您只需要做:

        var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);
        
        ... do anything with the dialog result...
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-01-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多