11

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

A. CodeProject - 使用 MVVM 模式时显示对话框

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

B. WPF MVVM 和显示对话框

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

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

谢谢你的时间。

4

6 回答 6

10

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

于 2013-06-25T23:57:13.593 回答
2

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

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

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

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

于 2013-06-26T01:02:17.800 回答
2

最近,我实现了自己的 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>();
    }
}
于 2013-06-26T00:37:56.047 回答
2

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

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

var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);

... do anything with the dialog result...
于 2013-06-26T05:49:45.857 回答
1

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

后来我在Gill Cleeren 的 MVVM 演示中看到了相同的解决方案。检查工作代码示例的链接(尽管是为 Metro 编写的)

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

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

于 2013-06-25T23:36:07.937 回答
0

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

于 2013-06-25T23:38:58.340 回答