WPF命令无法触发求助:Command绑定异常情况排查
问题现象
已查阅大量相关案例,但仍未定位问题根源。测试按钮出现如下反常行为:
- 当Command绑定使用
{Binding Path=DataContext.TestButtonClickCommand}时,按钮处于启用状态,但命令属性的getter断点完全不触发; - 当移除
Path=DataContext,仅使用{Binding TestButtonClickCommand}时,按钮变为禁用状态,但getter断点会正常触发。
相关代码
RelayCommand实现
public class RelayCommand : ICommand { #region ... vars private readonly Action<object> _execute; private readonly Predicate<object> _canExecute; #endregion #region ... constructor public RelayCommand(Action<object> execute) : this(execute, null) { } public RelayCommand(Action<object> execute, Predicate<object> canExecute) { if (execute == null) throw new ArgumentNullException("Invalid action"); this._execute = execute; this._canExecute = canExecute; } #endregion #region ... properties public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } #endregion #region ... methods public bool CanExecute(object parameter) { return this._canExecute == null ? true : false; } public void Execute(object parameter) { this._execute(parameter); } #endregion }
ViewModel代码
private RelayCommand _testButtonClickCommand; public ICommand TestButtonClickCommand { get { if (_testButtonClickCommand == null) _testButtonClickCommand = new RelayCommand(param => TestButtonClick(), param => CanTestButtonClick()); return _testButtonClickCommand; } } public bool CanTestButtonClick() { return true; } public void TestButtonClick() { }
XAML代码
<StackPanel x:Name="buttonList" Grid.Row="4" Orientation="Horizontal" Margin="5"> <Button Content="Test" Command="{Binding Path=DataContext.TestButtonClickCommand}"/> </StackPanel>
问题根源分析
RelayCommand的CanExecute方法逻辑错误
当前代码中CanExecute方法完全忽略了传入的_canExecute委托:return this._canExecute == null ? true : false;这段代码的逻辑是:未传入
_canExecute则返回true,否则返回false。而你创建命令时传入了CanTestButtonClick作为_canExecute,因此CanExecute会固定返回false,直接导致按钮绑定命令后处于禁用状态。绑定路径错误导致命令未正确加载
当使用{Binding Path=DataContext.TestButtonClickCommand}时,实际是尝试绑定Button自身DataContext的DataContext属性下的TestButtonClickCommand。如果Button的DataContext已经是ViewModel(通过父元素继承),而ViewModel并没有DataContext属性,此时绑定会失败,Command值为null。WPF中Button默认IsEnabled为true,因此按钮显示启用,但命令的getter根本不会被调用。
解决方案
1. 修复RelayCommand的CanExecute逻辑
将CanExecute方法修改为正确调用传入的委托:
public bool CanExecute(object parameter) { // 未传入CanExecute委托则默认返回true,否则调用委托判断 return _canExecute == null || _canExecute(parameter); }
2. 修正命令绑定路径
如果StackPanel的DataContext已设置为ViewModel,Button会自动继承该上下文,直接使用如下绑定即可:
<Button Content="Test" Command="{Binding TestButtonClickCommand}"/>
若Button的DataContext被单独设置过,可通过RelativeSource定位父元素的DataContext:
<Button Content="Test" Command="{Binding Path=DataContext.TestButtonClickCommand, RelativeSource={RelativeSource AncestorType=StackPanel}}"/>
内容的提问来源于stack exchange,提问作者Taucher

