MVVM:在调用命令

时间:2016-10-17 07:50:14

标签: wpf events mvvm command

我有什么:

  • 使用MVVM模式
  • 用XAML编写的视图
  • ViewModel中的命令MyCommand,它从视图中的多个位置调用
  • 对视图进行操作的方法DoSthInView,在codebehind
  • 中定义

我的目标:

无论何时执行命令,我都要调用DoSthInView,无论哪个控件执行命令。

问题:

由于在MVVM中ViewModel不知道View,因此我无法从ViewModel调用DoSthInView。那么如何调用此代码呢?

自己的想法:

为了不那么抽象,这是我的用例:我们有一个Button和一个TextBox。该命令获取当前位于TextBox中的文本,并将其写入模型数据中的某处。完成此写作后,我想要显示绿色复选标记的动画并逐渐淡出(这是DoSthInView),以便用户获得数据已更新的直观确认。

有两种运行命令的方法:

  1. 点击按钮
  2. 按"输入"而TextBox则专注于
  3. 对于Button我知道一种方法来调用DoSthInView

    <Button Content="run command" Command="{Binding MyCommand}" Click={Binding DoSthInView}" />
    

    对于TextBox,我有一个KeyBinding来接受Enter键:

    <TextBox>
        <TextBox.InputBindings>
            <KeyBinding Command="{Binding MyCommand}" Key="Enter" />
        </TextBox.InputBindings>
    </TextBox>
    

    但是InputBindings似乎不支持事件,只支持命令。所以我在这里不知道如何拨打DoSthInView

    但即使我找到了一种从输入绑定(类似于Button)调用DoSthInView的方法,它也不会感觉正确。我正在寻找一种说法&#34;只要执行MyCommand,就运行DoSthInView&#34;因此,MyCommand的每个调用者都不必单独关心它,但只有一个地方可以处理它。也许这可以在根FrameworkElement中完成?

4 个答案:

答案 0 :(得分:1)

你要求的是可能的。您需要实施RelayCommand。 您还可以看到我的其他SO answer,其中有一个示例。

实施RelayCommand后,您可以执行以下操作:

在ViewModel中:

public ICommand MyCommand { get; set; }
public MyViewModel()
{
    MyCommand = new RelayCommand(MyCommand_Execute);
}

private void MyCommand_Execute(object sender)
{
    var myView = sender as MyView;

    myView?.DoSthInView();
}

在视图中

<TextBox>
    <TextBox.InputBindings>
        <KeyBinding Command="{Binding Path=MyCommand}" CommandParameter="{Binding}" Key="Enter"/>
    </TextBox.InputBindings>
 </TextBox>

虽然不建议混合使用view和viewModel,但可能会出现无法实现的情况。有时它可能是要求。但是不建议这样做。

答案 1 :(得分:0)

虽然我仍然对原始问题的答案感兴趣(在执行命令后调用codebehind-code),但Kirenenko的建议帮助我解决了关于动画的实际问题。这个答案不再符合原始问题,因为没有代码隐藏代码(动画只用XAML编写,不留任何代码隐藏代码来执行)。我仍然把它放在这里,因为它对我来说是部分有用的。

在ViewModel中,我有这个:

...
private bool _triggerBool;
public bool TriggerBool
{
    get { return _triggerBool; }
    set
    {
        if (_triggerBool != value)
        {
            _triggerBool = value;
            NotifyPropertyChanged(nameof(TriggerBool));
        }
    }
}
...
public DelegateCommand MyCommand; // consists of MyCommandExecute and MyCommandCanExecute
...
public void MyCommandExecute()
{
    ... // actual command code
    TriggerBool = true;
    TriggerBool = false;
}
...

这是用XAML编写并由DataTrigger调用的动画:

<Image Source="myGreenCheckmark.png" Opacity="0">
    <Image.Style>
        <Style TargetType="Image">
            <Style.Triggers>
                <DataTrigger Binding="{Binding TriggerBool}" Value="True">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation Storyboard.TargetProperty="Opacity"
                                                 From="1" To="0" Duration="0.0:0:0.750"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Image.Style>

TriggerBool设置为true然后再设置false看起来真的很愚蠢,但有效......

答案 2 :(得分:0)

根据Leonid Malyshev的提示,这是一个非常干净的解决方案,使用一个事件(关于View和ViewModel分离的“干净”):

ViewModel代码:

public class MyViewModel
    {
    ...
    public event Action MyCommandExecuted;
    public DelegateCommand MyCommand; // consists of MyCommandExecute, MyCommandCanExecute
    ...
    private void MyCommandExecute()
    {
        ... // actual command code
        MyCommandExecuted.Invoke();
    }
    ...
}

查看Codebehind:

public partial class MyView : Window
{
    public MyView(MyViewModel vm)
    {
        InitializeComponent();
        DataConext = vm;
        vm.MyCommandExecuted += DoSthInView();
    }
    ...
    private void DoSthInView()
    {
        ...
    }
    ...
}

答案 3 :(得分:-2)

通常你有更好的MVVM方法来解决这些问题,但由于我不知道你的情况,我会假设没有别的办法。 因此,您需要在视图中使用依赖项属性。布尔值很棒,你可以在更改的事件上处理它并在其中运行DoSthInView。在视图模型中,您可以设置此属性的值,并在更改后调用它。

如果你需要,我可以给你演示。还要记住,这是一个诽谤MVVM的事件驱动编码。尝试使用绑定并尽可能将DoSthInView移动到ViewModel。