从代码后面调用命令

Posted

技术标签:

【中文标题】从代码后面调用命令【英文标题】:Call Command from Code Behind 【发布时间】:2012-04-24 23:59:05 【问题描述】:

所以我一直在四处寻找,但无法确切知道如何做到这一点。我正在使用 MVVM 创建用户控件,并希望在“加载”事件上运行命令。我意识到这需要一些代码,但我不太清楚需要什么。该命令位于 ViewModel 中,它被设置为视图的数据上下文,但我不确定如何路由它,因此我可以从加载事件后面的代码中调用它。基本上我想要的是这样的......

private void UserControl_Loaded(object sender, RoutedEventArgs e)

    //Call command from viewmodel

环顾四周,我似乎无法在任何地方找到它的语法。我是否需要先在 xaml 中绑定命令才能引用它?我注意到用户控件中的命令绑定选项不会让您像在按钮之类的东西中那样绑定命令...

<UserControl.CommandBindings>
    <CommandBinding Command="Binding MyCommand" /> <!-- Throws compile error -->
</UserControl.CommandBindings>

我确信有一种简单的方法可以做到这一点,但我终其一生都无法弄清楚。

【问题讨论】:

【参考方案1】:

好吧,如果 DataContext 已经设置,你可以转换它并调用命令:

var viewModel = (MyViewModel)DataContext;
if (viewModel.MyCommand.CanExecute(null))
    viewModel.MyCommand.Execute(null);

(根据需要更改参数)

【讨论】:

是的,这正是我所需要的,我知道有一个简单的方法。谢谢! 您应该在尝试访问 MyCommand 之前检查 viewModel 是否为空。我知道我的解决方案没有,但你的解决方案是公认的答案,所以它可能应该在那里。 @Alain:我的前言中有一个注释,我认为人们可以引入一个检查,如果在他们的情况下不能保证。 当 ViewModel 中的MyCommand 定义为某个MyMethod 时,为什么不直接执行viewModel.MyMethod()?除了CanExecute,当然也可以直接调用。 @Gerard:如果类接口允许直接访问该方法,并且该方法与您可以执行的命令执行相同的操作。但是您可能应该记住,命令可能在语义上与方法不同,并且您的代码不再是“在代码中调用命令”的表示,因为对命令实现的更改将不再反映。【参考方案2】:

前言:在不了解您的要求的情况下,在加载时从代码隐藏执行命令似乎是一种代码味道。必须有更好的方法,MVVM 方面。

但是,如果您真的需要在后面的代码中执行此操作,这样的方法可能会起作用(注意:我目前无法对此进行测试):

private void UserControl_Loaded(object sender, RoutedEventArgs e)     

    // Get the viewmodel from the DataContext
    MyViewModel vm = this.DataContext as MyViewModel;

    //Call command from viewmodel     
    if ((vm != null) && (vm.MyCommand.CanExecute(null)))
        vm.MyCommand.Execute(null);
 

再次尝试寻找更好的方法...

【讨论】:

在搜索该主题时,确实没有任何简单的方法,我看到的普遍共识是一个小代码背后从未杀死任何人。 @KDiTraglia - 是的,但一般来说,“背后的小代码”通常是指仅影响视图本身的操作(例如,当它获得焦点时选择文本框中的所有文本) .在这里,您是在 View 的代码隐藏中直接与 ViewModel 交互,这违反了 MVVM 原则。 @WonkotheSane 我不同意。视图已经通过绑定到它的命令和属性来了解 ViewModel,因此从代码隐藏中引用 ViewModel 就像从 XAML 中一样。 ViewModel 不应该知道 View,这肯定会违反 MVVM 原则。 @ShahinDohan - 这可能在过去 8 年中有所发展 :) 但我认为基本原则仍然存在。如果您需要在代码隐藏中进行交互以进行确认对话框之类的事情,那很好。但是,如果您在代码中与 ViewModel 交互以执行业务逻辑,那仍然违反了这个想法。至少这是我的看法。【参考方案3】:

我想分享一个更紧凑的解决方案。因为我经常在 ViewModel 中执行命令,所以我厌倦了编写相同的 if 语句。所以我为ICommand接口写了一个扩展。

using System.Windows.Input;

namespace SharedViewModels.Helpers

    public static class ICommandHelper
    
        public static bool CheckBeginExecute(this ICommand command)
        
            return CheckBeginExecuteCommand(command);
        

        public static bool CheckBeginExecuteCommand(ICommand command)
        
            var canExecute = false;
            lock (command)
            
                canExecute = command.CanExecute(null);
                if (canExecute)
                
                    command.Execute(null);
                
            

            return canExecute;
        
    

这就是你在代码中执行命令的方式:

((MyViewModel)DataContext).MyCommand.CheckBeginExecute();

我希望这会稍微加快您的开发速度。 :)

附:不要忘记也包括 ICommandHelper 的命名空间。 (在我的例子中是 SharedViewModels.Helpers)

【讨论】:

为什么不直接通过呢?例如:WizardViewModel.OkCommand.Execute(true);【参考方案4】:

试试这个:

private void UserControl_Loaded(object sender, RoutedEventArgs e)

    //Optional - first test if the DataContext is not a MyViewModel
    if( !this.DataContext is MyViewModel) return;
    //Optional - check the CanExecute
    if( !((MyViewModel) this.DataContext).MyCommand.CanExecute(null) ) return;
    //Execute the command
    ((MyViewModel) this.DataContext).MyCommand.Execute(null)

【讨论】:

你不应该在没有确保可以先执行的情况下调用命令。 如果你只是使用 CanExecute 来判断用户是否可以执行(即绑定到按钮的启用状态),那没有什么问题。 通过一个按钮,控件确保命令不会被粗心执行,如果您手动执行,则需要自己处理。【参考方案5】:

要在代码后面调用命令,可以使用此代码行

例如: 调用 Button 命令

Button.Command?.Execute(Button.CommandParameter);

【讨论】:

【参考方案6】:

您也可能已将代码嵌入到任何 MessaginCenter.Subscribe 并使用 MessagingCenter 模型。 如果您只想从后面的代码中执行某些操作,而不是单击具有 Command 属性的视图按钮,那么它对我来说非常有效。

我希望它对某人有所帮助。

【讨论】:

以上是关于从代码后面调用命令的主要内容,如果未能解决你的问题,请参考以下文章

从后面的代码调用动态创建的控件

从后面的 vb.net 代码调用 javascript

从文件后面的 ascx 代码调用 Aspx javascript 函数

在 Asp.net VB 中从后面的代码调用 OnClientClick

如何从 .aspx 页面 javascript 调用 C# 方法后面的 Web 窗体代码

vbscript 从vb.net后面的代码调用javascript函数