MVVM:代码隐藏的邪恶还是务实的?

时间:2016-10-26 08:39:36

标签: c# wpf mvvm data-binding code-behind

想象一下,你想在你喜欢的WPF MVVM窗口上找到Save & CloseCancel & 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混合在一起。

另一个问题是在显示主窗口时显示许可问题消息框。我可以像上面那样使用WindowLoaded事件,但这也打破了MVVM,不是吗?

在这些情况下,是否有一种干净的方式或应该是实用的而不是迂腐的?

5 个答案:

答案 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的命令,这些命令表明了框架作者的某种方法。