[英]MVVM: Is code-behind evil or just pragmatic?
想象一下,你想要在你的花式WPF MVVM窗口上Save & Close
和Cancel & 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.