ICommand CanExecuteChanged始终为空



我有一个基本的MVVM WPF应用程序与视图,由一个文本框和提交按钮组成。这两个控件都正确地绑定到ViewModel中的属性和命令。问题是,CanSubmit不会被触发,因为CanExecuteChanged eventandler(在DelegateCommand中)总是为空。基本上,问题是如何正确地通知命令运行CanExecute检查当textox更新。

public DelegateCommand  SubmitCommand => new DelegateCommand(Submit, CanSubmit);
private string _company;
public string Company
{
get => _company;
set
{
SetProperty(ref _company, value);
SubmitCommand.RaiseCanExecuteChanged();
}
}

我的委托命令

public class DelegateCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public DelegateCommand(Action<object> execute) : this(execute, null) { }
public virtual bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
if(CanExecuteChanged != null) <------ Always null
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}

我认为问题在于初始化get only属性的c#新特性的一个非常细微的差别。使用语法,您可以:

public DelegateCommand  SubmitCommand => new DelegateCommand(Submit, CanSubmit);

返回新的命令实例每次调用SumitCommand getter时。您在XAML中绑定的SubmitCommand实例与Companysetter:SubmitCommand.RaiseCanExecuteChanged();中的实例不同setter的SubmitCommand实例当然没有CanExecuteChaned的处理程序,因为它对UI是未知的。并且后面没有处理程序。

如果你把它改成:

public DelegateCommand  SubmitCommand { get; } = new DelegateCommand(Submit, CanSubmit);

我想这个问题会解决的。因为你只创建一次命令,按钮canexecutechange处理程序将绑定到公司setter调用raisecanexecutechange的同一个SubmitCommand实例。

我会补充@lidqy的答案。
他是完全正确的,问题在于每次访问属性时都创建一个新命令。
命令的实例必须始终相同。

通常,Submit和CanSubmit都是实例方法。
但是字段(属性)初始化器只能访问静态成员。

有两种典型的初始化命令的方法:
  1. ViewModel构造函数中的初始化:
public DelegateCommand SubmitCommand { get; }
public ViewModel()
{
SubmitCommand = new DelegateCommand(Submit, CanSubmit);
}
  1. 第一次调用属性时的初始化:
private DelegateCommand _submitCommand;
public DelegateCommand SubmitCommand => _submitCommand
?? (_submitCommand = new DelegateCommand(Submit, CanSubmit));

额外建议。
如果您有一个WPF解决方案(不是UWP),那么您应该订阅CommandManager.RequerySuggested命令。
在这种情况下,在GUI中发生更改时,将自动调用命令验证。
在属性setter中这样做的需求将会消失。

public class DelegateCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
private readonly EventHandler requerySuggested;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
requerySuggested = (s, e) => RaiseCanExecuteChanged();
CommandManager.RequerySuggested += requerySuggested;
}
public DelegateCommand(Action<object> execute) : this(execute, null) { }
public virtual bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
private string _company;
public string Company { get => _company; set => SetProperty(ref _company, value); }

还请记住,对于绑定到GUI中的WPF元素的命令,CanExecuteChanged事件应该始终在应用程序的主线程上调用。
这也适用于在属性设置器中调用它,因为属性不仅可以从GUI中更改。
参见这些实现:BaseInpc, RelayCommand和RelayCommand类。

相关内容

  • 没有找到相关文章

最新更新