簡體   English   中英

MVVM:代碼隱藏的邪惡還是務實的?

[英]MVVM: Is code-behind evil or just pragmatic?

想象一下,你想要在你的花式WPF MVVM窗口上Save & CloseCancel & Close按鈕?

你會怎么做? MVVM規定您將按鈕綁定到ICommand並且控制反轉指示您的View可能知道您的ViewModel而不是相反。

在網上尋找我發現了一個解決方案,它有一個View訂閱的ViewModel關閉事件,如下所示:

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,不是嗎?

在這些情況下,是否有一種干凈的方式或者應該是實用的而不是迂腐的?

首先,創建一個僅包含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
  • 視圖模型對視圖一無所知,它只是獲取一個可以關閉的任意對象
  • 該命令可以很容易地進行單元測試

使用代碼背后沒有任何錯誤或正確,這主要是基於意見,取決於您的偏好。

示例顯示如何使用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,以便使用依賴注入來關閉窗口。

看看一些工具包,例如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>

有時我會使用解決方法。

假設你有一個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的結束事件並將其傳遞給自己的事件。
您還可以向view.Loaded.Closing事件添加方法。

在App.xaml.cs中,您只需創建一個新的viewmodel對象。

public partial class App : Application
{
    public App()
    {
        MainWindowVM mainWindowVM = new MainWindowVM();
        mainWindowVM.Closed += Mwvm_Close;
    }

    private void Mwvm_Close()
    {
        this.Shutdown();
    }
}

我創建一個新的viewmodel對象並捕獲它自己的close-event並將其綁定到App的shutdown方法。

您的描述表明視圖模型是某種文檔視圖。 如果這是正確的,那么我將保留Save, Close, etc.由文檔容器(例如應用程序或主窗口)處理,因為這些命令位於文檔上方,與復制/粘貼在應用程序上的方式相同水平。 事實上, ApplicationCommands具有Save和Close的預定義命令,這些命令表示框架作者的某種方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM