这是我的第一个WPF-MVVM
应用程序,这是我的结构:
app.xaml
的一个项目打开应用程序并覆盖OnStartup
以解析MainWindow。 (由于参考,我这样做了); 我有以下问题:我在MainWindowView
,我点击按钮显示另一个视图。我应该如何从我的MainWindowViewModel
打开另一个视图,而我的View Project
引用了ViewModel Project
,我无法用ViewModel Project
引用View Project
{1}}?
顺便说一句,我正在Unity
使用dependency injection
。
那么,你能帮助我吗?
答案 0 :(得分:9)
有几种方法。
您可以定义ViewModels项目中定义的对话框/导航/窗口服务界面。您需要决定ViewModel如何表达他们想要打开的窗口。我通常使用IDialogViewModel接口,我的一些ViewModel实现,并将ViewModel的实例传递给服务,但你可以使用枚举,字符串,无论你想要什么,所以你的实现可以映射到真正的窗口,这将是打开。
例如:
public interface IDialogService
{
bool? ShowDialog(object dialogViewModel);
}
想要打开新Windows的ViewModel将接收该服务的实例,并使用它来表达打开Window的意图。在Views项目中,您将定义一个实现服务接口的类型,并打开Window后面的真实逻辑。
以下示例:
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中创建和注入对话框服务。在该示例中,App将创建一个新的对话服务实例,将MainWindow传递给它。
类似的方法是使用某种形式的消息传递模式(link1 link2)。 此外,如果你想要一些简单的东西,你也可以让你的ViewModel在他们想要打开Windows并让视图订阅它们时引发事件。
修改强>
我在我的应用程序中使用的完整解决方案通常有点复杂,但基本上就是这个想法。我有一个基本的DialogWindow,它需要一个实现IDialogViewModel接口的ViewModel作为DataContext。此接口抽象出您在对话框中期望的一些功能,例如接受/取消命令以及关闭事件,因此您也可以从ViewModel关闭窗口。 DialogWindow基本上包含一个ContentPresenter,它将Content属性绑定到DataContext,并在更改DataContext时挂钩close事件(以及其他一些事情)。
每个“对话框”包含一个IDialogViewModel和一个关联的View(UserControl)。为了映射它们,我只是在App的资源中声明隐式DataTemplates。在我展示的代码中,唯一的区别是没有方法MapWindow,窗口实例总是一个DialogWindow。
我使用了一个额外的技巧来重用对话框之间的布局元素。方法是将它们包含在DialogWindow中(接受/取消按钮等)。我喜欢保持DialogWindow干净(所以我可以将它用于“非对话框”对话框)。我使用公共接口元素声明ContentControl的模板,当我声明View-ViewModel映射模板时,我使用ContentControl包装View并应用了我的“对话框模板”。然后,您可以根据需要为DialogWindow提供尽可能多的“主模板”(例如,像“向导之类”)。
答案 1 :(得分:3)
如果我理解正确,应用程序启动时通过Unity解决了MainWindowView,这解决了它对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>
This article提供了与之前在制作中使用的类似的解决方案。
答案 2 :(得分:0)
要从MainWindowView
打开新窗口,您需要将Frame
组件或整个窗口的引用传递给MainWindowViewModel
对象(您可以在将命令绑定到转换按钮或其他内容时执行此操作,将它们作为对象传递,在那里你可以导航到新页面,但是,如果在转换时你需要在ViewModel
中做任何特殊的事情,你可以使用经典的ButtonClick
事件或w / e MainWindowView.cs
中将为您执行导航,这对于基本过渡是可以的。
P.S。我不确定为什么你为ViewModels / Views / Models使用不同的项目。