从后台代码调用命令


73

因此,我一直在搜索,无法确切地知道如何执行此操作。我正在使用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>

我敢肯定有一个简单的方法可以做到这一点,但是我无法终生解决。

Answers:


143

好吧,如果已经设置了DataContext,则可以将其强制转换并调用以下命令:

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

(根据需要更改参数)


1
是的,这正是我所需要的,我知道有一种简单的方法。谢谢!
凯文·迪特里亚

在尝试访问MyCommand之前,应检查viewModel是否为null。我知道我的解决方案不是,但是您的解决方案是可以接受的答案,因此可能应该在其中。
阿兰(Alain)2012年

2
@Alain:在我的序言中有一条注释,我认为人们可以引入支票,如果不能保证的话。
HB

MyCommand在ViewModel中定义某个对象时MyMethod,为什么不执行viewModel.MyMethod()呢?除了CanExecute,当然也可以直接调用。
杰拉德2014年

@杰拉德:如果类接口允许直接访问该方法,并且该方法执行与命令相同的操作,则可以执行此操作。但是,您可能应该记住,命令在语义上可能与方法不同,并且您的代码不再表示“在代码中调用命令”,因为对命令实现的更改将不再反映出来。
HB

7

前言:在不了解您的要求的情况下,在加载时从代码后面执行命令似乎有点代码味道。必须有一种更好的方法,即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);
} 

再次-尝试找到更好的方法...


1
在搜索该主题时,确实没有任何简便的方法,而且我所看到的普遍共识是,从未杀死任何人的背后是一些代码。
凯文·迪特里亚

1
@KDiTraglia-是的,但总的来说,“后面的小代码”通常是指仅影响视图本身的动作(例如,在获得焦点时选择文本框中的所有文本)。在这里,您正在直接从View的后台代码与ViewModel进行交互,这违反了MVVM原理。
Wonko the Sane,2012年

@WonkotheSane我不同意。该视图已经通过绑定到其命令和属性来了解ViewModel,因此从代码隐藏中引用ViewModel与从XAML进行引用一样好。虽然ViewModel不应该了解View,但肯定会违反MVVM原理。
Shahin Dohan

@ShahinDohan-在过去的8年中这可能有所发展:),但我认为基本原理仍然存在。如果您需要在代码后方交互以进行诸如确认对话框之类的操作,那很好。但是,如果要在后面的代码中与ViewModel交互以进行业务逻辑,那实际上仍然违反了这个想法。至少那是我的看法。
桑科旺科(Wonko the Sane),

2

我想分享一个更紧凑的解决方案。因为我经常在ViewModels中执行命令,所以我厌倦了编写相同的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();

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

PS不要忘记也包含ICommandHelper的名称空间。(在我的情况下是SharedViewModels.Helpers)


为什么不通过真实?例如:WizardViewModel.OkCommand.Execute(true);
AZ_

1

尝试这个:

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)
}

2
在不确保可以首先执行命令之前,不应调用命令。
HB

如果仅使用CanExecute来确定用户是否可以执行它(即绑定到按钮的启用状态),则没有任何问题。
阿兰(Alain)2012年

3
使用按钮,控件可确保该命令永远不会被不小心执行,如果您手动执行该命令,则需要您自己照顾。
HB

0

嗨,您可以使用此代码行在代码背后调用命令

例如:调用按钮命令

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

-1

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

希望对您有所帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.