想象一下,你想在你喜欢的WPF MVVM窗口上找到Save & Close
和Cancel & Close
按钮吗?
你会怎么做? MVVM规定您将按钮绑定到ICommand
并且控制反转指示您的View
可能知道您的ViewModel
但不是相反。
在网上闲逛我发现了一个ViewModel
关闭事件的解决方案,View
订阅了这样的事件:
private void OnLoaded(Object sender
, RoutedEventArgs e)
{
IFilterViewModel viewModel = (IFilterViewModel)DataContext;
viewModel.Closing += OnViewModelClosing;
}
private void OnViewModelClosing(Object sender
, EventArgs<Result> e)
{
IFilterViewModel viewModel = (IFilterViewModel)DataContext;
viewModel.Closing -= OnViewModelClosing;
DialogResult = (e.Value == Result.OK) ? true : false;
Close();
}
但是这与我目前为止设计得很好的MVVM混合在一起。
另一个问题是在显示主窗口时显示许可问题消息框。我可以像上面那样使用Window
。Loaded
事件,但这也打破了MVVM,不是吗?
在这些情况下,是否有一种干净的方式或应该是实用的而不是迂腐的?
答案 0 :(得分:7)
首先,创建一个仅包含Close
方法的接口:
interface IClosable
{
void Close();
}
接下来,让您的窗口实现IClosable
:
class MyWindow : Window, IClosable
{
public MyWindow()
{
InitializeComponent();
}
}
然后让视图将自身作为IClosable
作为命令参数传递给视图模型:
<Button Command="{Binding CloseCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
最后,该命令调用Close
:
CloseCommand = new DelegateCommand<IClosable>( view => view.Close() );
我们现在有什么?
, IClosable
答案 1 :(得分:1)
使用代码背后没有任何错误或正确,这主要是基于意见的,取决于您的偏好。
这个example展示了如何在没有代码的情况下使用MVVM设计模式关闭窗口。
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/>
<!-- the CommandParameter should bind to your window, either by name or relative or what way you choose, this will allow you to hold the window object and call window.Close() -->
基本上你将窗口作为参数传递给命令。 IMO你的viewmodel不应该知道控件,所以这个版本不是那么好。我会将Func<object>
/ some接口传递给viewmodel,以便使用依赖注入来关闭窗口。
答案 2 :(得分:0)
看看一些工具包,例如MVVMLight具有EventToCommand,它允许您将命令绑定到事件。我通常会尽力限制View中的逻辑,因为它很难测试它。
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight"
...
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<command:EventToCommand Command="{Binding YourCommandInVM}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
答案 3 :(得分:0)
有时我会使用解决方法。
假设你有一个view
“MainWindow”和一个viewmodel
“MainWindowVM”。
public class MainWindowVM
{
private MainWindow mainWindow;
public delegate void EventWithoudArg();
public event EventWithoudArg Closed;
public MainWindowVM()
{
mainWindow = new MainWindow();
mainWindow.Closed += MainWindow_Closed;
mainWindow.DataContext = this;
mainWindow.Loaded += MainWindow_Loaded;
mainWindow.Closing += MainWindow_Closing;
mainWindow.Show();
}
private void MainWindow_Loaded()
{
//your code
}
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
//your code
}
private void MainWindow_Closed()
{
Closed?.Invoke();
}
}
这里我将我的视图存储在私有变量中,以便您可以在需要时访问它。它打破了一点MVVM
在我的viewmodel中,我创建了一个新的view
并显示它
在这里,我还捕捉了view
的结束事件并将其传递给自己的事件
您还可以为.Loaded
的{{1}}和.Closing
事件添加方法。
在App.xaml.cs中,您只需创建一个新的view
对象。
viewmodel
我创建一个新的public partial class App : Application
{
public App()
{
MainWindowVM mainWindowVM = new MainWindowVM();
mainWindowVM.Closed += Mwvm_Close;
}
private void Mwvm_Close()
{
this.Shutdown();
}
}
对象并捕获它自己的close-event并将其绑定到App的shutdown方法。
答案 4 :(得分:0)
您的描述表明视图模型是某种文档视图。如果这是正确的,那么我将留下Save, Close, etc.
由文档容器处理,例如应用程序或主窗口,因为这些命令位于文档上方,与复制/粘贴在应用程序级别上的方式相同。实际上ApplicationCommands
已经预定义了Save和Close的命令,这些命令表明了框架作者的某种方法。