在分离的项目中,从VM显示新窗口的MVVM模式

5
我的问题涉及数千个主题,如果我忽略了我的问题的答案,我很抱歉,但是就我所看到的,似乎没有人真正能够回答我的问题。例如:在MVVM WPF中打开新窗口
答案可以解决单个WPF项目(包括模型、视图模型和视图)的问题,但是由于我正在学习如何正确实现MVVM(多次阅读后最佳实践是为模型、视图模型创建单独的类库(dlls)和一个独立的GUI项目),这种方法在我看来似乎行不通,因为如果我想要创建像IWindowService这样的界面(在之前的URL和这里描述),我就无法访问Window或Control类,因为那样我应该引用GUI项目,而整个模式的目标也被破坏了。
所以我的问题是如何显示一个新窗口(带有新的视图模型),例如从MainViewModel,同时尊重松耦合的MVVM原则和分离的项目。
更详细的示例:
我有以下结构:
MODEL(dll项目)      Profile VIEWMODEL(dll项目)      MainViewModel      AddProfileViewModel
VIEW(WPF)(exe 项目)      MainWindow      AddProfileWindow
我打开MainWindow并想按AddProfile按钮,然后AddProfileWindow需要显示并附加AddProfileViewModel。

1
您可以使用Messenger发送一些OpenWindow消息,并在View项目中订阅它。 https://dev59.com/xGQn5IYBdhLWcg3wPlCj - Taras Shcherban
我看了一下Messenger,但似乎有点过度设计。不过对于 viewmodels 之间的通信似乎很有用,所以 +1。 - JC97
2个回答

10
  • 在模型项目中定义IWindowService接口。
  • 从视图模型项目引用模型项目。
  • 在WPF应用程序(视图)项目中实现IWindowService

按钮应该绑定到使用IWindowService打开窗口的ICommand。像这样:

public class MainWindowViewModel
{
    private readonly IWindowService _windowService;

    public MainWindowViewModel(IWindowService windowService)
    {
        _windowService = windowService;
        AddProfile = new DelegateCommand(() =>
        {
            _windowService.OpenProfileWindow(new AddProfileViewModel());
        });
    }

    public ICommand AddProfile { get; }
}

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainWindowViewModel(new WindowService());
    }
}

public class WindowService : IWindowService
{
    public void OpenProfileWindow(AddProfileViewModel vm)
    {
        AddProfileWindow win = new AddProfileWindow();
        win.DataContext = vm;
        win.Show();
    }
}

我知道这个答案很旧,但你最终不是需要在模型中引用视图模型吗?因为你的接口需要一个对AddProfileViewModel的引用作为OpenProfileWindow参数?或者我们只需将视图模型作为object传递? - Matt
或者在视图模型项目中定义'IWindowService'会更好吗? - Matt

0

我曾经为此苦苦挣扎,后来发现@mm8发布的答案非常有帮助,还有其他一些帖子也很有用。我不太喜欢为每个视图创建一个类(或在类中创建方法),因此我创建了自己的变体。这是我在尝试使用MVVM时的第一个项目,所以我分享这个内容,以便得到更有经验的人的反馈。

public class WindowDialogService : IWindowDialogService
    {
        private static readonly Dictionary<Type, Type> viewModelPairs =
            new Dictionary<Type, Type>
            {
                [typeof(DetailsViewModel)] = typeof(DetailsView)
            };

        public void ShowWindowDialog(IViewModelBase viewModel)
        {
            if (!viewModelPairs.TryGetValue(viewModel.GetType(), out Type viewType))
            {
                throw new ArgumentException("View Model not mapped", nameof(viewModel));
            }

            if (viewType.GetConstructor(Type.EmptyTypes).Invoke(new object[] { }) is Window view)
            {
                view.DataContext = viewModel;
                view.Owner = Application.Current.MainWindow;
                view.ShowDialog();
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接