使用View中的确认逻辑命令绑定到ViewModel

时间:2014-06-06 22:36:54

标签: c# wpf mvvm mvvm-light icommand

寻找将按钮命令绑定到ViewModel ICommand属性的最优雅解决方案,同时允许在View中进行确认。

我想做什么:

  1. 仅允许用户在
  2. 时单击按钮
  3. 单击按钮时,请询问确认
  4. 如果确认,请在ViewModel中工作,否则取消
  5. 不要破坏MVVM架构
  6. 可以通过显示ViewModel中的消息框来满足确认要求。但是,我不认为这是要走的路。它没有打破MVVM吗?如果CanExecute取决于UI(代码隐藏)和ViewModel的状态怎么办?另外,从ViewModel弹出消息框时的可测试性呢?

    我尝试的另一件事是绑定OnClick(到View)和C​​ommand(到ViewModel)。虽然事件始终在Command之前执行,但似乎无法取消执行该命令。此外,执行顺序似乎是一个未记录的功能,所以你不应该依赖它。除此之外,它仍然不允许CanExecute考虑View逻辑。

    接下来,我提出了以下解决方案:

    查看(XAML)

    <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模式,它也实现了单一责任。但这是最好的方式吗?还有其他可能性吗?

1 个答案:

答案 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所依赖的任何内容都不应该在代码隐藏中 - 您应该将类​​似的内容移动到视图模型中。