使用ICommand设置按钮的IsEnabled属性

时间:2018-01-12 16:39:38

标签: c# wpf icommand

我有命令

public class RelayActionCommand : ICommand
{
    /// <summary>
    /// The Action Delegate representing a method with input parameter 
    /// </summary>
    public Action<object> ExecuteAction { get; set; }
    /// <summary>
    /// The Delegate, used to represent the method which defines criteria for the execution 
    /// </summary>
    public Predicate<object> CanExecuteAction { get; set; }

    public bool CanExecute(object parameter)
    {
        if (CanExecuteAction != null)
        {
            return CanExecuteAction(parameter);
        }
        return true;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        if (ExecuteAction != null)
        {
            ExecuteAction(parameter);
        }
    }
}

要使用它,

public RelayActionCommand SearchPersonCommnad { get; set; }

    DataAccess objds;
    public PersonViewModel()
    {
        Persons = new ObservableCollection<PersonInfo>();
        objds = new DataAccess();


        Persons = new ObservableCollection<PersonInfo>(objds.GetPersonData());

        var defaultView = CollectionViewSource.GetDefaultView(Persons);

        //based upon the data entered in the TextBox
        SearchPersonCommnad = new RelayActionCommand()
        {
            CanExecuteAction = n=> !String.IsNullOrEmpty(Name),
             ExecuteAction = n => defaultView.Filter = name => ((PersonInfo)name).FirstName.StartsWith(Name) 
                 || ((PersonInfo)name).LastName.StartsWith(Name) 
                 || ((PersonInfo)name).City==Name
        };

开始时,该按钮被禁用。但在运行时间,它会因不同情况而发生变化。我的问题是如何设置按钮的IsEnabled属性?这意味着,当ExecuteAction我必须正确设置属性时。

更新

我使用ICommand而不是DelegateCommand。

1 个答案:

答案 0 :(得分:-1)

您可以使用CanExecute方法,但实际上最好避免这种情况,并将按钮的启用状态绑定到视图模型的单独布尔属性。大多数其他解决方案会产生意想不到的影响,或者不是最理想的为什么呢?

CanExecute是一种方法。这意味着需要轮询它以更改按钮状态。您可以强制使用该命令的控件在状态更改时重新轮询,但如果您只在视图模型上使用属性,则代码更清晰,更直接。这是因为作为一种方法,您不能使用INotifyPropertyChanged来通知更改,而使用属性则可以。

使用CanExecute的危险在于,在方法返回false之后,但在按钮的启用发生变化之前,用户将设法点击按钮。

修改:代码可以执行您想要的操作:

public class ViewModel : INotifyPropertyChanged
{
    private int someValue;
    private bool isEnabled;

    public ViewModel()
    {
        MyCommand = new RelayActionCommand(Click);
    }

    private void Click(object obj)
    {
        //Do something.
    }

    /// <summary>
    /// Bind this to the IsEnabled property of the button, and 
    /// also the background using a convertor or see ButtonBackground.
    /// </summary>
    public bool IsEnabled => SomeValue < 5;

    /// <summary>
    /// Option 2 - use this property to bind to the background of the button.
    /// </summary>
    public Brush ButtonBackground => IsEnabled ? Brushes.SeaShell : Brushes.AntiqueWhite;

    public int SomeValue
    {
        get { return someValue; }
        set
        {
            if (value == someValue) return;
            someValue = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(IsEnabled));
            OnPropertyChanged(nameof(ButtonBackground));
        }
    }

    /// <summary>
    /// Bind this to the command of the button.
    /// </summary>
    public RelayActionCommand MyCommand { get; }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged
        ([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

中继命令固定了一点以避免使用CanExecute:

public class RelayActionCommand : ICommand
{
    public RelayActionCommand(Action<object> executeAction)
    {
        ExecuteAction = executeAction;
    }

    /// <summary>
    /// The Action Delegate representing a method with input parameter 
    /// </summary>
    public Action<object> ExecuteAction { get; }
    /// <summary>

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        ExecuteAction?.Invoke(parameter);
    }

    //Deliberately empty.
    public event EventHandler CanExecuteChanged
    {
        add { }
        remove { }
    }
}

编辑2:使用DelegateCommand执行您想要的代码

注意,这不使用InvalidateRequerySuggested - 主要是因为它在任何CanExecute更改时刷新所有按钮,这是一个糟糕的解决方案。正如您所看到的,这不像直接将代码直接放在视图模型中那么简单直接,但无论如何,我想你的船都会漂浮。

public sealed class ViewModel : INotifyPropertyChanged
{
    private int calls;

    public ViewModel()
    {
        SafeOnceCommand = new RelayCommand(DoItOnce, HasDoneIt);    
    }

    private bool HasDoneIt()
    {
        return Calls == 0;
    }

    private void DoItOnce()
    {
        if (Calls > 0) throw new InvalidOperationException();
        Calls++;
    }

    public int Calls
    {
        get { return calls; }
        set
        {
            if (value == calls) return;
            calls = value;
            OnPropertyChanged();
            SafeOnceCommand.RaiseCanExecuteChanged();
        }
    }

    public RelayCommand SafeOnceCommand { get; }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public sealed class RelayCommand : ICommand
{
    private readonly Action execute;
    private readonly Func<bool> canExecute;
    private readonly List<EventHandler> invocationList = new List<EventHandler>();

    public RelayCommand(Action execute, Func<bool> canExecute)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return canExecute();
    }

    public void Execute(object parameter)
    {
        execute();
    }

    /// <summary>
    /// Method to raise CanExecuteChanged event
    /// </summary>
    public void RaiseCanExecuteChanged()
    {
        foreach (var elem in invocationList)
        {
            elem(null, EventArgs.Empty);
        }
    }

    public event EventHandler CanExecuteChanged
    {
        add { invocationList.Add(value); }
        remove { invocationList.Remove(value); }
    }
}