寻找将按钮命令绑定到ViewModel ICommand属性的最优雅解决方案,同时允许在View中进行确认。
我想做什么:
可以通过显示ViewModel中的消息框来满足确认要求。但是,我不认为这是要走的路。它没有打破MVVM吗?如果CanExecute取决于UI(代码隐藏)和ViewModel的状态怎么办?另外,从ViewModel弹出消息框时的可测试性呢?
我尝试的另一件事是绑定OnClick(到View)和Command(到ViewModel)。虽然事件始终在Command之前执行,但似乎无法取消执行该命令。此外,执行顺序似乎是一个未记录的功能,所以你不应该依赖它。除此之外,它仍然不允许CanExecute考虑View逻辑。
接下来,我提出了以下解决方案:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Button Content="Do Work" Command="{Binding Path=ViewModel.SaveCommand}"/>
</Grid>
<SelectTemplateUserControl Visibility="Collapsed" OnTemplateSelected="SelectTemplate_OnTemplateSelected"/>
</Window>
public partial class MainWindow : Window
{
private readonly MainViewModel _viewModel = new MainViewModel();
public MainWindow()
{
InitializeComponent();
}
public MainViewModel ViewModel { get { return this._viewModel; } }
public ICommand SaveCommand { get { return new RelayCommand<int>(this.Save, this.CanSave);} }
private bool CanSave(int templateId)
{
return this._viewModel.SaveCommand.CanExecute(null);
}
private void Save(int templateId)
{
var messageBoxResult = MessageBox.Show("Do you want to overwrite?", "Overwrite?", MessageBoxButton.OKCancel);
if (messageBoxResult == MessageBoxResult.Cancel)
return;
// Call method to hide main Grid and set SelectTemplateUserControl to visible..
}
private void SelectTemplate_OnTemplateSelected(object sender, int templateId)
{
this._viewModel.SaveCommand.Execute(templateId);
}
}
public class MainViewModel : ViewModelBase
{
public ICommand SaveCommand { get { return new RelayCommand<int>(this.Save, this.CanSave); } }
private bool CanSave(int templateId)
{
// Can Save Logic, returning a bool
}
private void Save(int templateId)
{
// Save Logic....
}
}
我认为它很好地遵循MVVM模式,它也实现了单一责任。但这是最好的方式吗?还有其他可能性吗?
答案 0 :(得分:5)
可以通过显示ViewModel中的消息框来满足确认要求。但是,我不认为这是要走的路。它没有打破MVVM吗?
在使用与视频相关的依赖关系(如&#34; MessageBox&#34;)时保留MVVM样式的一种方法是将它们封装并注入到视图模型中。因此,您可以通过在构造函数中请求IDialogService
来表达依赖关系:
public class MainViewModel : ViewModelBase
{
private readonly IDialogService _dialog;
public MainViewModel(IDialogService dialog)
{
_dialog = dialog;
}
}
然后从视图中传递实现:
private readonly MainViewModel _viewModel = new MainViewModel(new DialogService());
界面封装了您需要的任何功能,因此可能&#34;警报&#34;,&#34;确认&#34;等
public interface IDialogService
{
bool Confirm(string message, string caption = "Confirm");
}
使用MessageBox
或任何其他方法实现它(并为单元测试切换虚拟实现):
public class DialogService : IDialogService
{
public bool Confirm(string message, string caption)
{
return MessageBox.Show(message, caption, MessageBoxButton.OKCancel) == MessageBoxResult.OK;
}
}
通过这种方式,您可以将所有确认逻辑从视图移动到视图模型,其中&#34;保存&#34;方法看起来像这样:
private void Save()
{
if (!_dialog.Confirm("Do you want to overwrite?", "Overwrite?"))
return;
this.SaveCommand.Execute(null);
}
如果CanExecute取决于UI(代码隐藏)和ViewModel的状态会怎样?
如果您担心测试,那么CanExecute
所依赖的任何内容都不应该在代码隐藏中 - 您应该将类似的内容移动到视图模型中。