Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/blackberry/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 命令绑定到视图中带有确认逻辑的ViewModel_C#_Wpf_Mvvm_Mvvm Light_Icommand - Fatal编程技术网

C# 命令绑定到视图中带有确认逻辑的ViewModel

C# 命令绑定到视图中带有确认逻辑的ViewModel,c#,wpf,mvvm,mvvm-light,icommand,C#,Wpf,Mvvm,Mvvm Light,Icommand,正在寻找最优雅的解决方案,将按钮命令绑定到ViewModel ICommand属性,同时允许在视图中进行确认 我想做的是: 仅允许用户在需要时单击按钮 单击按钮时,请确认 如果确认,请在ViewModel中工作,否则取消 不要破坏MVVM体系结构 通过显示ViewModel中的消息框,可以满足确认要求。然而,我不认为这是一条路要走。它不会破坏MVVM吗?如果CanExecute取决于UI(代码隐藏)和ViewModel的状态,该怎么办?另外,当从ViewModel中弹出messagebox时,可

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

我想做的是:

  • 仅允许用户在需要时单击按钮
  • 单击按钮时,请确认
  • 如果确认,请在ViewModel中工作,否则取消
  • 不要破坏MVVM体系结构
  • 通过显示ViewModel中的消息框,可以满足确认要求。然而,我不认为这是一条路要走。它不会破坏MVVM吗?如果CanExecute取决于UI(代码隐藏)和ViewModel的状态,该怎么办?另外,当从ViewModel中弹出messagebox时,可测试性如何

    我尝试的另一件事是绑定OnClick(到视图)和Command(到视图模型)。尽管事件总是在命令执行之前执行,但似乎没有办法取消命令的执行。此外,执行顺序似乎是一个未记录的特性,因此您不应该依赖它。除此之外,它仍然不允许CanExecute考虑视图逻辑

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

    视图(XAML)
    
    
    视图(代码隐藏)
    公共部分类主窗口:窗口
    {
    私有只读MainViewModel _viewModel=new MainViewModel();
    公共主窗口()
    {
    初始化组件();
    }
    public MainViewModel ViewModel{get{返回此。\u ViewModel;}}
    public ICommand SaveCommand{get{返回新的RelayCommand(this.Save,this.CanSave);}
    私有bool CanSave(int templateId)
    {
    返回此值。_viewModel.SaveCommand.CanExecute(null);
    }
    私有void保存(int templateId)
    {
    var messageBoxResult=MessageBox.Show(“是否要覆盖?”,“覆盖?”,MessageBoxButton.ok取消);
    if(messageBoxResult==messageBoxResult.Cancel)
    返回;
    //调用方法隐藏主网格并将SelectTemplateUserControl设置为visible。。
    }
    私有void SelectTemplate\u OnTemplateSelected(对象发送方,int-templateId)
    {
    这是._viewModel.SaveCommand.Execute(templateId);
    }
    }
    
    视图模型
    public类MainViewModel:ViewModelBase
    {
    public ICommand SaveCommand{get{返回新的RelayCommand(this.Save,this.CanSave);}
    私有bool CanSave(int templateId)
    {
    //可以保存逻辑,返回布尔值
    }
    私有void保存(int templateId)
    {
    //保存逻辑。。。。
    }
    }
    
    我认为它很好地遵循了MVVM模式,它还实现了单一责任。但这是最好的方法吗?还有其他的可能性吗

    通过显示ViewModel中的消息框,可以满足确认要求。然而,我不认为这是一条路要走。它不会破坏MVVM吗

    在使用与视图相关的依赖项(如“MessageBox”)时,保留MVVM样式的一种方法是将它们封装并注入视图模型。因此,您可以通过在构造函数中请求
    IDialogService
    来表示依赖关系:

    public class MainViewModel : ViewModelBase
    {
        private readonly IDialogService _dialog;
    
        public MainViewModel(IDialogService dialog)
        {
            _dialog = dialog;
        }
    }
    
    然后从视图中传入实现:

    private readonly MainViewModel _viewModel = new MainViewModel(new DialogService());
    
    该接口封装了您需要的任何功能,因此可能需要“警报”、“确认”等

    并使用
    MessageBox
    或任何其他方法实现它(并为单元测试切换虚拟实现):

    这样,您可以将所有确认逻辑从视图移动到视图模型,其中“Save”方法如下所示:

    private void Save()
    {
        if (!_dialog.Confirm("Do you want to overwrite?", "Overwrite?"))
            return;
    
        this.SaveCommand.Execute(null);
    }
    
    如果CanExecute取决于UI(代码隐藏)和ViewModel的状态,该怎么办


    如果您关心测试,那么
    CanExecute
    所依赖的任何东西都不应该出现在代码背后——您应该将类似的东西移到视图模型中。

    也许该链接可以帮助您找到很好的解决方案,谢谢。但有一种情况,我认为仍然需要在不影响可测试性的情况下从视图中添加功能。用户已在窗口2中打开文档。当文档未处于“完成状态”时,“应用模板”按钮应亮起,因此ApplyTemplateCommand.CanExecute来自ViewModel 3。一个消息框弹出“覆盖…?”4。如果用户回答是,DocumentUserControl将折叠,SelectTemplateUserControl将显示在窗口5中。当用户选择模板时,视图中会触发ApplyTemplateCommand.Execute(templateId)。我只能想到我的解决方案在这里应用,将对MessageBox的直接调用替换为IDialogService。在这种情况下,还有其他方法适合您的解决方案吗?@RamonBertrand那么您是说在这种情况下,“ApplyEmplateCommand”需要出现在视图中吗?如果没有更多的细节,我想我无法理解你的论点。我建议更新你的问题,突出重点(或者,如果这个新场景与当前问题有足够大的不同,你可以自由地提出一个新问题)。更新代码,以便更清楚地表达我的问题。没有在我的示例中添加DialogService,因为该部分已经回答。
    public class MainViewModel : ViewModelBase
    {
        private readonly IDialogService _dialog;
    
        public MainViewModel(IDialogService dialog)
        {
            _dialog = dialog;
        }
    }
    
    private readonly MainViewModel _viewModel = new MainViewModel(new DialogService());
    
    public interface IDialogService
    {
        bool Confirm(string message, string caption = "Confirm");
    }
    
    public class DialogService : IDialogService
    {
        public bool Confirm(string message, string caption)
        {
            return MessageBox.Show(message, caption, MessageBoxButton.OKCancel) == MessageBoxResult.OK;
        }
    }
    
    private void Save()
    {
        if (!_dialog.Confirm("Do you want to overwrite?", "Overwrite?"))
            return;
    
        this.SaveCommand.Execute(null);
    }