如何将ViewModel中的命令绑定到Behavior中的命令?

时间:2018-11-20 16:48:20

标签: wpf mvvm prism-7

WPF项目+ Prism 7 +(纯MVVM模式)

简单,我有TextBox,当按下某些按钮时需要清除(不违反MVVM模式)

<Button Command="{Binding ClearCommand}"/>
<TextBox Text="{Binding File}">
    <i:Interaction.Behaviors>
        <local:ClearTextBehavior ClearTextCommand="{Binding ClearCommand, Mode=OneWayToSource}" />
    </i:Interaction.Behaviors>
</TextBox>

ViewModel

public class ViewModel {
    public ICommand ClearCommand { get; set; }
}

行为

public class ClearTextBehavior : Behavior<TextBox>
{
    public ICommand ClearTextCommand
    {
        get { return (ICommand)GetValue(ClearTextCommandProperty); }
        set
        {
            SetValue(ClearTextCommandProperty, value);
            RaisePropertyChanged(); 
        }
    }

    public static readonly DependencyProperty ClearTextCommandProperty =
        DependencyProperty.Register(nameof(ClearTextCommand), typeof(ICommand), typeof(ClearTextBehavior));

    public ClearTextBehavior()
    {
        ClearTextCommand = new DelegateCommand(ClearTextCommandExecuted);
    }

    private void ClearTextCommandExecuted()
    {
        this.AssociatedObject.Clear();
    }
}

问题是,尽管我确保已在行为类中将其初始化,但ViewModel中的命令始终为null(未绑定到Behavior中的命令)。

注意:请不要建议将File属性设置为空字符串,因为这只是一个示例,在我的实际情况下,我需要选择所有Text,因此我确实需要访问{ {1}}的行为

2 个答案:

答案 0 :(得分:1)

如果我正确理解了您的问题,您想知道为什么ICommand中的ViewModel未设置为DelegateCommand中定义的Behaviour

问题是ICommandDelegateCommand没有直接连接。我认为您可能会误解了Binding的工作原理以及使用它们会发生什么。

首先,ICommand来自Class,因此是引用类型。

第二,对ICommand的引用保存在DependencyProperty ClearTextCommandProperty中。

第三,通过在Binding中使用XAML,这种情况以C#代码的形式发生:

Binding binding = new Binding();
binding.Path = new PropertyPath("ClearTextCommand");
binding.Source = ClearCommand; 
BindingOperations.SetBinding(TextBox.ClearTextCommandProperty, binding);

现在重要的是:我不知道到底是哪个赋值最先出现,但是这两行都将覆盖Value中的ClearTextCommandProperty引用!

//either here
SetBinding(TextBox.ClearTextCommandProperty, binding);

//or here 
ClearTextCommand = new DelegateCommand(ClearTextCommandExecuted);
//Which could be written as
SetValue(ClearTextCommandProperty, new DelegateCommand(ClearTextCommandExecuted));

在任何时候都没有这样的作业:

ViewModel.ClearCommand = SomeICommand;

因此它是Null,就像@Andy所说的


已编辑以匹配所有文本

另外,我建议您放弃这种复杂的东西,并像这样利用Interactivity软件包的全部潜能:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

<Button>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <utils:SelectAllText TargetName="TextToSelect"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

<TextBox x:Name="TextToSelect" Text="{Binding File}"/>

还有SelectAllText

public class SelectAllText : TargetedTriggerAction<TextBox>
{
    protected override void Invoke(object parameter)
    {
        if (Target == null) return;

        Target.SelectAll();
        Target.Focus();
    }
}

答案 1 :(得分:0)

如果您在此处查看此示例: https://social.technet.microsoft.com/wiki/contents/articles/31915.wpf-mvvm-step-by-step-1.aspx 您会注意到,我那里有一个ICommand,它已设置为运行方法。

如果它只是一个具有Get和Set的ICommand,那么它将为NULL。有一个属性,但在设置为某项之前为空。

这是实现ICommand的笨拙方式,但不依赖任何外部库或任何东西。

如果您看一下该系列的第二篇文章,它使用了mvvmlight和relaycommand,因此创建命令相当麻烦。

https://social.technet.microsoft.com/wiki/contents/articles/32164.wpf-mvvm-step-by-step-2.aspx

public RelayCommand AddListCommand { get; set; }

public MainWindowViewModel()
{
    AddListCommand = new RelayCommand(AddList);
}
private void AddList()
{
    stringList.Add(myString));
}

如果您查看该代码,AddListCommand最初为空。 在构造函数中将其设置为新的RelayCommand,这意味着它不为null。

这很简单,但是命令的代码与属性位于不同的位置,因此通常使用更优雅的方法。如此处所示:https://msdn.microsoft.com/en-gb/magazine/dn237302.aspx


说了这么多。 选择所有文本是视图中要做的事情,而不是视图模型。 您实际上不应该将一部分UI从视图传递到ViewModel。

您应该绑定一个在视图模型中设置并按照行为执行的布尔,而不是命令。