【问题标题】:Re-evaluating CanExecute on property change在属性更改时重新评估 CanExecute
【发布时间】:2012-10-23 18:53:54
【问题描述】:

我正在尝试在自定义控件(类似于按钮)上实现 ICommandSource。目前的实现主要是显示在 ICommandSource 的 msdn 页面上以及显示在 ButtonBase 源代码中。

CanExecute 在控件加载时触发,但在任何属性发生更改时不会触发。传递给常规按钮的相同命令可以正常工作。当应该更改的属性发生更改时,CanExecute 触发并启用按钮。该命令是一个委托命令。

我已经尝试过 CommandManager.InvalidateRequerySuggested();但这并没有奏效。

有什么想法吗?

这是自定义控件中的实现:

private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    CollapsibleSplitButton csb = (CollapsibleSplitButton)d;
    csb.OnCommandChanged((ICommand)e.OldValue, (ICommand)e.NewValue);
}

private void OnCommandChanged(ICommand oldCommand, ICommand newCommand)
{
    if (oldCommand != null) UnhookCommand(oldCommand);
    if (newCommand != null) HookCommand(newCommand);
}

private void UnhookCommand(ICommand command)
{
    command.CanExecuteChanged -= OnCanExecuteChanged;
    UpdateCanExecute();
}

private void HookCommand(ICommand command)
{
    command.CanExecuteChanged += OnCanExecuteChanged;
    UpdateCanExecute();
}
private void OnCanExecuteChanged(object sender, EventArgs e)
{
    UpdateCanExecute();
}

private void UpdateCanExecute()
{
    if (Command != null)
        CanExecute = Command.CanExecute(CommandParameter);
    else
        CanExecute = true;
}

protected override bool IsEnabledCore
{
    get { return base.IsEnabledCore && CanExecute; }
}

我设置命令的地方:

...
    MyCommand = new DelegatingCommand(DoStuff, CanDoStuff);
...

private bool CanDoStuff()
{
    return (DueDate == null);
}

private void DoStuff() {//do stuff}

【问题讨论】:

  • 它是一个 INotifyChangedProperty

标签: c# wpf icommand


【解决方案1】:

使用delegate 命令,只要您认为应该在 UI 上更新CanExecuteChanged,就必须明确提出。尝试使用这个版本的命令 RelayCommand -

public class RelayCommand<T> : ICommand
{
   #region Fields

   readonly Action<T> _execute = null;
   readonly Predicate<T> _canExecute = null;

   #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of <see cref="DelegateCommand{T}"/>.
    /// </summary>
    /// <param name="execute">Delegate to execute when Execute is called on the command.
    ///This can be null to just hook up a CanExecute delegate.</param>
    /// <remarks><seealso cref="CanExecute"/> will always return true.</remarks>
    public RelayCommand(Action<T> execute)
            : this(execute, null)
    {
    }

    /// <summary>
    /// Creates a new command.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    /// <param name="canExecute">The execution status logic.</param>
    public RelayCommand(Action<T> execute, Predicate<T> canExecute)
    {
       if (execute == null)
          throw new ArgumentNullException("execute");

       _execute = execute;
       _canExecute = canExecute;
    }

    #endregion

    #region ICommand Members

    ///<summary>
    ///Defines the method that determines whether the command can execute in its current 
    ///state.
    ///</summary>
    ///<param name="parameter">Data used by the command.  If the command does not require 
    /// data to be passed, this object can be set to null.</param>
    ///<returns>
    ///true if this command can be executed; otherwise, false.
    ///</returns>
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute((T)parameter);
    }

    ///<summary>
    ///Occurs when changes occur that affect whether or not the command should execute.
    ///</summary>
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    ///<summary>
    ///Defines the method to be called when the command is invoked.
    ///</summary>
    ///<param name="parameter">Data used by the command. If the command does not require 
    ///data to be passed, this object can be set to <see langword="null" />.</param>
    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }

    #endregion
}

并在您的班级中注册Delegate 命令-

public ICommand TestCommand { get; private set; }
TestCommand = new RelayCommand<object>(CommandMethod, CanExecuteCommand);

编辑

尝试将CommandManager.InvalidateRequerySuggested(); 放入您的CanExecute -

private void OnCanExecuteChanged(object sender, EventArgs e)
{
    CommandManager.InvalidateRequerySuggested();
    UpdateCanExecute();
}

【讨论】:

  • 我的 DelegateCommand 的代码大致相同。只是这里和那里的一些差异。但是不,这也不起作用。
  • 不。还是不行。常规 Button 仍然可以正常工作,但是 Custom 控件不能。当我修改需要更改的属性时,自定义控件上的 OnCanExecuteChanged 不会触发。在自定义控件上 OnCanExecuteChanged 仅在加载/重新加载该视图模型时触发。
【解决方案2】:

设法通过将回调包装在 EventHandler 中来解决问题。

private EventHandler currentHandler;

private void UnhookCommand(ICommand command)
{
    if (currentHandler != null)
        command.CanExecuteChanged -= currentHandler;
    UpdateCanExecute();
}

private void HookCommand(ICommand command)
{
    if (currentHandler == null) return;

    command.CanExecuteChanged += currentHandler;
    UpdateCanExecute();
}

【讨论】:

    猜你喜欢
    • 2017-04-07
    • 2023-03-31
    • 1970-01-01
    • 2016-03-02
    • 1970-01-01
    • 2013-01-14
    • 1970-01-01
    • 2014-12-29
    • 2011-05-06
    相关资源
    最近更新 更多