relaycommand 构造时 must 传非 null canexecute 委托或设默认值,否则调用 canexecute(null) 抛 nullreferenceexception;canexecutechanged 必须绑定 commandmanager.requerysuggested 才能自动刷新按钮状态;xaml 中 command 绑定失败多因 datacontext 断开,需显式设置;依赖属性变更后应调用 commandmanager.invalidaterequerysuggested() 触发重查。

RelayCommand 构造时必须传非 null 的 CanExecute 委托或设默认值
传 null 给 RelayCommand 的 canExecute 参数,又没在 CanExecute 方法里判空,运行时调用 CanExecute(null) 就会抛 NullReferenceException。这不是 XAML 绑定报错,而是按钮内部触发检查时崩掉,很难定位。
安全写法是构造时就给兜底逻辑:
new RelayCommand(
param => Save(param),
param => param is Customer && !string.IsNullOrEmpty((param as Customer).Name)
);
或者更稳妥地显式提供默认委托:
-
canExecute参数不传null,统一用_ => true或具体判断逻辑 - 若业务上真需要“无条件可执行”,就写
_ => true,别留空 - 参数类型统一用
object,避免 XAML 里传CommandParameter时类型不匹配
CanExecuteChanged 不挂 CommandManager.RequerySuggested 就不会自动刷新按钮状态
这是 WPF 命令系统里最隐蔽的坑:按钮启停不随数据变化更新,不是 ViewModel 没通知,而是 CanExecuteChanged 事件根本没被监听。WPF 自身不主动轮询,它依赖 CommandManager.RequerySuggested 这个全局事件来批量重查所有命令的 CanExecute 结果。
所以自定义 RelayCommand 时,CanExecuteChanged 的 add/remove 必须绑定到它:
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
- 漏写这行,改完
SelectedItem或IsEditing,按钮还是灰的 - 不要试图手动调用
RaiseCanExecuteChanged()—— 那只是触发事件,没人听 - 如果用了 Community Toolkit 的
RelayCommand,它已内置该逻辑;手写版必须自己加
XAML 中 Command 绑定失败,90% 是 DataContext 断了
WPF 不会跨命名空间、不沿视觉树向上找 ViewModel。你写了 {Binding SaveCommand},但 Button 的 DataContext 是 null 或是父控件的 DataContext(比如某个 UserControl 未显式设置),绑定就静默失败——没警告、没异常、按钮永远不可点。
验证方法:在 XAML 里临时加一句 Text="{Binding}" 看输出,或调试时鼠标悬停绑定表达式看是否解析出对象。
- 窗口级:在
MainWindow构造函数里写this.DataContext = new MainViewModel(); - 用户控件级:别依赖继承,显式设
UserControl.DataContext = new MyViewModel(); - 弹窗场景:新窗口的
DataContext不继承父窗口,必须重新赋值 -
Command属性只认ICommand类型,绑Save(方法名)或string会完全失效,且无提示
需要手动通知 CanExecute 变化时,用 CommandManager.InvalidateRequerySuggested()
当 CanExecute 依赖的属性变更(比如 SelectedItem 改了),但 WPF 没自动触发重查——常见于非 INotifyPropertyChanged 属性、或属性变更没走 SetProperty 流程时,就得人工“敲钟”。
不要在属性 setter 里反复调 RaiseCanExecuteChanged()(除非你用的是 Community Toolkit 的 IRelayCommand 并调它的 NotifyCanExecuteChanged()),标准做法是触发全局重查:
private Customer _selectedItem;
public Customer SelectedItem
{
get => _selectedItem;
set
{
_selectedItem = value;
OnPropertyChanged();
CommandManager.InvalidateRequerySuggested(); // ← 关键一行
}
}
- 这个调用开销极小,WPF 内部是延迟合并的
- 只在真正影响命令可用性的属性变更后调,别滥用
- 如果用了 MVVM Toolkit,优先用
IRelayCommand.NotifyCanExecuteChanged(),它更精准











