C# WPF从ViewModel打开一个新视图
这是我的第一个C# WPF从ViewModel打开一个新视图,c#,wpf,xaml,mvvm,C#,Wpf,Xaml,Mvvm,这是我的第一个WPF-MVVM应用程序,这是我的结构: 使用myapp.xaml打开应用程序并覆盖启动时的以解析主窗口的一个项目。(我这样做是因为参考) 我的观点是一个项目 一个用于我的ViewModels的项目 我的模型有一个项目 我有以下问题:我在MainWindowView上,点击一个按钮显示另一个视图。如果我的视图项目引用了视图模型项目,而我不能引用视图项目,我该如何从我的主窗口视图模型打开另一个视图呢 顺便说一下,我正在使用Unity进行依赖项注入 所以,您能帮我吗?要从MainWin
WPF-MVVM
应用程序,这是我的结构:
app.xaml
打开应用程序并覆盖启动时的以解析主窗口的一个项目。(我这样做是因为参考)李>
我的观点是一个项目李>
一个用于我的ViewModels的项目李>
我的模型有一个项目
我有以下问题:我在MainWindowView
上,点击一个按钮显示另一个视图。如果我的视图项目
引用了视图模型项目
,而我不能引用视图项目
,我该如何从我的主窗口视图模型
打开另一个视图呢
顺便说一下,我正在使用Unity
进行依赖项注入
所以,您能帮我吗?要从MainWindowView
打开新窗口,您需要将框架组件或整个窗口的引用传递到MainWindowViewModel
对象(您可以在将命令绑定到转换按钮或其他东西时执行此操作,将它们作为对象传递),但是,您可以在那里导航到新页面,如果转换时在ViewModel
中没有什么特别的事情需要做,您可以使用经典的按钮单击事件或MainWindowView.cs
中的w/e来为您导航,这对于基本转换来说是可以的
另外,我不知道为什么您对视图模型/视图/模型使用不同的项目。有几种方法
您可以定义在ViewModels项目中定义的对话框/导航/窗口服务界面。您需要决定ViewModels将如何表示要打开的窗口。我通常使用IDialogViewModel接口(我的一些ViewModels实现了该接口),并将ViewModel的一个实例传递给服务,但您可以使用枚举、字符串,无论您想要什么,这样您的实现就可以映射到将要打开的实际窗口
例如:
public interface IDialogService
{
bool? ShowDialog(object dialogViewModel);
}
想要打开新窗口的ViewModels将收到该服务的一个实例,并使用它来表示打开窗口的意图。在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;
}
}
公共类DialogService:IDialogService
{
私有堆栈windowStack=新堆栈();
公共对话服务(窗口根目录)
{
此.windowStack.Push(根);
}
public bool?ShowDialog(对象对话框ViewModel)
{
窗口对话框=映射窗口(dialogViewModel);
dialog.DataContext=dialogViewModel;
dialog.Owner=this.windowStack.Peek();
此.windowStack.Push(对话框);
布尔结果;
尝试
{
结果=dialog.ShowDialog();
}
最后
{
this.windowStack.Pop();
}
返回结果;
}
}
您的主项目将负责在需要对话服务的ViewModels中创建和注入对话服务。在本例中,应用程序将创建一个新的对话服务实例,并将主窗口传递给它
类似的方法是使用某种形式的消息传递模式()。
此外,如果您想要一些简单的东西,还可以让ViewModels在打开窗口时引发事件,并让视图订阅这些事件
编辑
我在应用程序中使用的完整解决方案通常有点复杂,但基本上就是这样。我有一个基本的DialogWindow,它需要一个ViewModel,作为DataContext实现IDialogViewModel接口。此界面抽象了您在对话框中期望的一些功能,如接受/取消命令以及关闭的事件,以便您也可以从ViewModel关闭窗口。DialogWindow基本上由ContentPresenter组成,ContentPresenter的内容属性绑定到DataContext,并在DataContext发生更改(以及其他一些事情)时钩住close事件
每个“对话框”包含一个IDialogViewModel和一个关联的视图(UserControl)。为了映射它们,我只在应用程序的资源中声明隐式数据模板。在我展示的代码中,唯一的区别是没有方法MapWindow,窗口实例始终是DialogWindow
我使用另外一个技巧在对话框之间重用布局元素。方法是将它们包括在对话框窗口中(接受/取消按钮等)。我喜欢保持对话框窗口干净(这样我就可以使用它来创建“非对话框”对话框)。我使用公共接口元素为ContentControl声明一个模板,当我声明一个视图ViewModel映射模板时,我使用一个应用了“对话框模板”的ContentControl包装视图。然后,您可以为您的对话框窗口设置任意数量的“主模板”(例如,像“向导式”模板)。直接方法
如果我理解正确,当应用程序启动时,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>