9

这是我的第一个WPF-MVVM应用程序,这是我的结构:

  1. 与我app.xaml一起打开应用程序并覆盖OnStartup以解析 MainWindow 的一个项目。(由于参考资料,我这样做了);
  2. 我的观点的一个项目;
  3. 我的 ViewModel 的一个项目;
  4. 我的模型的一个项目。

我有以下问题:我在上面MainWindowView,我点击一个按钮来显示另一个视图。我应该怎么做才能从我的另一个视图中打开这个另一个视图,MainWindowViewModel而我的View Project有引用ViewModel Project,而我不能引用ViewModel ProjectView Project

顺便说一句,我正在Unity使用dependency injection.

那么,你能帮帮我吗?

4

3 回答 3

11

有几种方法。

您可以定义在 ViewModels 项目中定义的对话框/导航/窗口服务接口。您将需要决定 ViewModel 将如何表达他们想要打开的窗口。我通常使用我的一些 ViewModel 实现的 IDialogViewModel 接口,并将 ViewModel 的实例传递给服务,但您可以使用枚举、字符串,任何您想要的,因此您的实现可以映射到将是的真实窗口打开。

例如:

public interface IDialogService
{
    bool? ShowDialog(object dialogViewModel); 
}

想要打开新窗口的 ViewModel 将接收该服务的实例并使用它来表达打开窗口的意图。在您的 Views 项目中,您将定义一个实现您的服务接口的类型,以及打开窗口背后的真实逻辑。

按照示例:

public class DialogService : IDialogService
{
    private Stack<Window> windowStack = new Stack<Window>();


    public DialogService(Window root)
    {
        this.windowStack.Push(root);
    }

    public bool? ShowDialog(object dialogViewModel)
    {
        Window dialog = MapWindow(dialogViewModel); 
        dialog.DataContext = dialogViewModel;
        dialog.Owner = this.windowStack.Peek();

        this.windowStack.Push(dialog);

        bool? result;

        try
        {
            result = dialog.ShowDialog();
        }
        finally
        {
            this.windowStack.Pop();
        }

        return result;
    }

}

您的主项目将负责在需要它的 ViewModel 中创建和注入对话服务。在示例中,应用程序将创建一个新的对话服务实例,并将 MainWindow 传递给它。

一种类似的方法是使用某种形式的消息传递模式(link1 link2)。此外,如果您想要一些简单的东西,您还可以让您的 ViewModel 在他们想要打开 Windows 时引发事件并让 View 订阅它们。

编辑

我在我的应用程序中使用的完整解决方案通常有点复杂,但基本思路就是这样。我有一个基本的 DialogWindow,它需要一个将 IDialogViewModel 接口实现为 DataContext 的 ViewModel。该接口抽象了一些您期望在对话框中实现的功能,例如接受/取消命令以及关闭事件,因此您也可以从 ViewModel 关闭窗口。DialogWindow 基本上包含在 ContentPresenter 中,其中 Content 属性绑定到 DataContext 并在 DataContext 更改(以及其他一些事情)时挂钩 close 事件。

每个“对话框”都包含一个 IDialogViewModel 和一个关联的视图 (UserControl)。为了映射它们,我只需在 App 的资源中声明隐式 DataTemplates。在我展示的代码中,唯一的区别是不会有 MapWindow 方法,窗口实例始终是 DialogWindow。

我使用了一个额外的技巧来重用对话框之间的布局元素。方法是将它们包含在 DialogWindow 中(接受/取消按钮等)。我喜欢保持 DialogWindow 干净(所以我可以使用它来“非对话”对话框)。我使用通用界面元素为 ContentControl 声明了一个模板,当我声明一个 View-ViewModel 映射模板时,我用我的“对话框模板”应用了 ContentControl 来包装 View。然后,您可以根据需要为您的 DialogWindow 拥有尽可能多的“主模板”(例如,像“向导一样”的模板)。

于 2012-12-02T20:59:44.433 回答
3

直截了当的方法

如果我对您的理解正确,则 MainWindowView 在应用程序启动时通过 Unity 解决,这解决了它对 MainWindowViewModel 的依赖关系?

如果这是您正在使用的流程,我建议您继续使用相同的路径,并让 MainWindowView 通过按钮的简单单击处理程序来处理新视图的打开。然后,在这个处理程序中,您可以解析新视图,这将解析该视图的视图模型,然后您也将返回 MVVM 区域以获取新视图。

该解决方案直截了当,并且对于大多数较小的应用程序都可以正常工作。

更复杂的应用程序的更重方法

如果您不想要那种视图优先的流程,我建议您引入某种控制器/演示器来协调视图和视图模型。演示者负责决定是否/何时实际打开/关闭视图等。

不过,这是一个相当繁重的抽象,它更适合更复杂的应用程序,因此请确保您实际上从中获得了足够的好处,以证明增加的抽象/复杂性是合理的。

以下是此方法的代码示例:

public partial class App
{
    protected override void OnStartup(StartupEventArgs e)
    {
        var container = new UnityContainer();

        container.RegisterType<IMainView, MainWindow>();
        container.RegisterType<ISecondView, SecondWindow>();
        container.RegisterType<IMainPresenter, MainPresenter>();
        container.RegisterType<ISecondPresenter, SecondPresenter>();

        var presenter = container.Resolve<IMainPresenter>();

        presenter.ShowView();
    }
}

public interface IMainPresenter
{
    void ShowView();
    void OpenSecondView();
}

public interface ISecondPresenter
{
    void ShowView();
}

public interface ISecondView
{
    void Show();
    SecondViewModel ViewModel { get; set; }
}

public interface IMainView
{
    void Show();
    MainViewModel ViewModel { get; set; }
}

public class MainPresenter : IMainPresenter
{
    private readonly IMainView _mainView;
    private readonly ISecondPresenter _secondPresenter;

    public MainPresenter(IMainView mainView, ISecondPresenter secondPresenter)
    {
        _mainView = mainView;
        _secondPresenter = secondPresenter;
    }

    public void ShowView()
    {
        // Could be resolved through Unity just as well
        _mainView.ViewModel = new MainViewModel(this);

        _mainView.Show();
    }

    public void OpenSecondView()
    {
        _secondPresenter.ShowView();
    }
}

public class SecondPresenter : ISecondPresenter
{
    private readonly ISecondView _secondView;

    public SecondPresenter(ISecondView secondView)
    {
        _secondView = secondView;
    }

    public void ShowView()
    {
        // Could be resolved through Unity just as well
        _secondView.ViewModel = new SecondViewModel();
        _secondView.Show();
    }
}

public class MainViewModel
{
    public MainViewModel(MainPresenter mainPresenter)
    {
        OpenSecondViewCommand = new DelegateCommand(mainPresenter.OpenSecondView);
    }

    public DelegateCommand OpenSecondViewCommand { get; set; }
}

public class SecondViewModel
{
}

<!-- MainWindow.xaml -->
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Command="{Binding OpenSecondViewCommand}" Content="Open second view" />
    </Grid>
</Window>

<!-- SecondWindow.xaml -->
<Window x:Class="WpfApplication1.SecondWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SecondWindow" Height="350" Width="525">
    <Grid>
        <TextBlock>Second view</TextBlock>
    </Grid>
</Window>

本文提供了一个与我之前在生产中使用过的解决方案类似的解决方案。

于 2012-12-02T20:59:49.410 回答
0

要从中打开新窗口MainWindowView,您需要将Frame组件或整个窗口的引用传递给MainWindowViewModel对象(您可以在将命令绑定到转换按钮或其他东西时执行此操作,将它们作为对象传递),您可以导航到新页面,但是,如果有转换时您不需要做任何特别的事情ViewModel,您可以使用经典ButtonClick事件或 w/e 来MainWindowView.cs为您导航,这对于基本转换来说是可以的。

PS 我不确定你为什么对 ViewModels/Views/Models 使用不同的项目。

于 2012-12-02T16:59:36.920 回答