【问题标题】:Using ApplicationCommands in WPF PRISM在 WPF PRISM 中使用 ApplicationCommands
【发布时间】:2018-07-08 10:32:31
【问题描述】:

在研究了几个Q&A on stackoverflow, some tutorials 当然还有official documentation之后,我尝试使用987654331@ 在我的 WPF Prism MVVM 应用程序中。

我目前的做法

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

  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" />
    

我的第一个问题此时是:Executed()CanExecute() 方法是否已经绑定到 Button 的 Command-DependencyProperty?既然不行,那我是不是忘了什么或者做错了什么?

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

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

【问题讨论】:

    标签: c# .net wpf mvvm prism


    【解决方案1】:

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

    您行为的CommandBindings 属性是ObservableCollection&lt;CommandBinding&gt;,但您将其绑定到视图模型中的CommandBindingCollection。将视图模型的属性更改为 ObservableCollection&lt;CommandBinding&gt;

    您链接到的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.NewValue 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 开发人员试图使自动命令重新查询尽可能无缝,因此它在大多数情况下“正常工作”。

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

    需要明确的是,作为CanExecuteRoutedEventHandler,您的CanSave 方法应返回void,并在事件参数上将CanExecute 设置为true

    【讨论】:

    • 非常感谢您的宝贵建议,先生!我会试试这个。
    猜你喜欢
    • 2019-05-14
    • 2014-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-31
    • 2019-09-20
    相关资源
    最近更新 更多