在WPF PRISM中使用ApplicationCommands

时间:2018-01-29 16:49:20

标签: c# .net wpf mvvm prism

在研究了几个Q & A o n s tackoverflow,s ome t utorials后,当然还有o fficial d ocumentation,我尝试在我的WPF Prism MVVM应用程序中使用ApplicationCommands

我目前的做法

在尝试了不同的解决方案之后我发现了,我最终得到了以下星座:

  1. 我正在使用this answer中提到的AttachCommandBindingsBehavior类,它将在视图中使用,如下所示:

    <UserControl>
        <i:Interaction.Behaviors>
            <localBehaviors:AttachCommandBindingsBehavior CommandBindings="{Binding CommandBindings}"/>
        </i:Interaction.Behaviors>
    </UserControl>
    
  2. MyViewModel包含CommandBindingCollection属性,该属性将填充在构造函数中:

    public CommandBindingCollection CommandBindings { get; } = new CommandBindingCollection();
    
    public MyViewModel()
    {
        this.CommandBindings.AddRange(new[]
        {
            new CommandBinding(ApplicationCommands.Save, this.Save, this.CanSave),
            new CommandBinding(ApplicationCommands.Open, this.Open)
        });
    }
    
  3. UserControl MyView包含两个按钮:

    <Button Command="ApplicationCommands.Open" Content="Open" />
    <Button Command="ApplicationCommands.Save" Content="Save" />
    
  4. 此时的第一个问题是:Executed()CanExecute()方法是否已绑定到Button的Command-DependencyProperty?既然它不起作用,我忘了或做错了什么?

    我的第二个问题是:如何触发按钮绑定的命令的CanExecute?当用户成功执行MyViewModel.CanSave()方法时,实际用例:MyViewModel.Open()返回true。通常,我会拨打DelegateCommand.RaiseCanExecuteChanged(),但是调用ApplicationCommands.Save.RaiseCanExecuteChanged()不会执行MyViewModel.CanSave()

    随时询问更多信息。我将非常感谢你的回答。谢谢!

1 个答案:

答案 0 :(得分:1)

  

既然它不起作用,我忘记了什么或做错了什么?

您行为的CommandBindings属性为ObservableCollection<CommandBinding>,但您可以将其绑定到视图模型中的CommandBindingCollection。将您的视图模型的属性更改为ObservableCollection<CommandBinding>

您链接的AttachCommandBindingsBehavior也存在一些问题。我不确定为什么答案被接受了,因为它确实很破碎。不过,下面的调整版本应该可行。

public class AttachCommandBindingsBehavior : Behavior<FrameworkElement>
{
    public ObservableCollection<CommandBinding> CommandBindings
    {
        get => (ObservableCollection<CommandBinding>)GetValue(CommandBindingsProperty);
        set => SetValue(CommandBindingsProperty, value);
    }

    public static readonly DependencyProperty CommandBindingsProperty =
        DependencyProperty.Register(
            "CommandBindings",
            typeof(ObservableCollection<CommandBinding>),
            typeof(AttachCommandBindingsBehavior),
            new PropertyMetadata(null, OnCommandBindingsChanged));

    private static void OnCommandBindingsChanged(
        DependencyObject sender,
        DependencyPropertyChangedEventArgs e)
    {
        var b = sender as AttachCommandBindingsBehavior;
        if (b == null)
            return;

        var oldBindings = e.OldValue as ObservableCollection<CommandBinding>;
        if (oldBindings != null)
            oldBindings.CollectionChanged -= b.OnCommandBindingsCollectionChanged;

        var newBindings = e.OldValue as ObservableCollection<CommandBinding>;
        if (newBindings != null)
            newBindings.CollectionChanged += b.OnCommandBindingsCollectionChanged;

        b.UpdateCommandBindings();
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        UpdateCommandBindings();
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.CommandBindings.Clear();
    }

    private void UpdateCommandBindings()
    {
        if (AssociatedObject == null)
            return;

        AssociatedObject.CommandBindings.Clear();

        if (CommandBindings != null)
            AssociatedObject.CommandBindings.AddRange(CommandBindings);

        CommandManager.InvalidateRequerySuggested();
    }

    private void OnCommandBindingsCollectionChanged(
        object sender,
        NotifyCollectionChangedEventArgs e)
    {
        UpdateCommandBindings();
    }
}
  

如何触发按钮绑定的命令的CanExecute?

您可以建议 WPF的路由命令系统通过调用CommandManager.InvalidateRequerySuggested()重新评估所有命令。实际的重新评估将以Background调度程序优先级异步发生。这是明确的方法,但您应该知道,某些操作已经发生隐式,例如焦点更改和鼠标/键盘按钮事件。 WPF开发人员试图尽可能无缝地进行自动命令重新查询,因此它只是工作&#34;大多数时候。

  

实际用例:MyViewModel.CanSave()返回true [...]

为了清楚起见,作为CanExecuteRoutedEventHandler,您的CanSave方法应返回void并在事件参数上将CanExecute设置为true