WPF中ICommand使用指南
在 WPF 中,ICommand 是实现 命令模式 的核心接口,用于解耦 UI 操作(如按钮点击)和业务逻辑。以下是关键知识点的总结:
1. ICommand 接口定义
public interface ICommand
{
event EventHandler CanExecuteChanged;
bool CanExecute(object parameter);
void Execute(object parameter);
}
-
CanExecute:判断命令是否可执行(返回
false时,绑定控件自动禁用) -
Execute:执行命令的实际逻辑
-
CanExecuteChanged:通知命令状态变化的事件
2. 常用实现方式
(1) 自定义 RelayCommand(最常用)
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute?.Invoke(parameter) ?? true;
public void Execute(object parameter) => _execute(parameter);
// 通过 CommandManager 自动刷新状态
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
(2) 使用 CommunityToolkit.Mvvm 的 RelayCommand
// 安装 NuGet 包:CommunityToolkit.Mvvm
using CommunityToolkit.Mvvm.Input;
public class MyViewModel
{
public ICommand ClickCommand { get; }
public MyViewModel()
{
ClickCommand = new RelayCommand(ExecuteClick, CanClick);
}
private void ExecuteClick() => Console.WriteLine("Clicked!");
private bool CanClick() => true;
}
3. 在 ViewModel 中声明命令
public class MainViewModel : INotifyPropertyChanged
{
private string _inputText;
public string InputText
{
get => _inputText;
set => SetField(ref _inputText, value);
}
public ICommand SubmitCommand { get; }
public MainViewModel()
{
SubmitCommand = new RelayCommand(
execute: _ => Submit(),
canExecute: _ => !string.IsNullOrWhiteSpace(InputText)
);
}
private void Submit() => MessageBox.Show($"Submitted: {InputText}");
}
4. XAML 中绑定命令
<Button Content="提交"
Command="{Binding SubmitCommand}"
CommandParameter="可选参数"/>
<!-- 绑定带参数的命令 -->
<Button Command="{Binding OpenFileCommand}"
CommandParameter="{Binding SelectedItem}"/>
5. 自动刷新命令状态
-
方法1:依赖
CommandManager(推荐)
如上文RelayCommand实现,通过CommandManager.RequerySuggested自动触发状态刷新(例如文本框输入时)。 -
方法2:手动触发
调用CommandManager.InvalidateRequerySuggested()强制刷新所有命令:public string InputText { get => _inputText; set { _inputText = value; OnPropertyChanged(); CommandManager.InvalidateRequerySuggested(); // 手动刷新 } }
6. 处理异步操作
使用 async/await 的异步命令示例:
public class AsyncRelayCommand : ICommand
{
private readonly Func<object, Task> _execute;
private bool _isExecuting;
public AsyncRelayCommand(Func<object, Task> execute) => _execute = execute;
public bool CanExecute(object parameter) => !_isExecuting;
public async void Execute(object parameter)
{
if (_isExecuting) return;
_isExecuting = true;
OnCanExecuteChanged();
try { await _execute(parameter); }
finally
{
_isExecuting = false;
OnCanExecuteChanged();
}
}
private void OnCanExecuteChanged() =>
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
7. 内置命令类型
-
RoutedCommand:支持路由事件(冒泡/隧道),常用于菜单快捷键
-
RoutedUICommand:带文本描述的
RoutedCommand
// 示例:绑定 ApplicationCommands 内置命令
<Button Command="ApplicationCommands.Copy"/>
8. 最佳实践
-
ViewModel 中实现命令:保持 UI 与逻辑分离
-
避免命令阻塞 UI:耗时操作使用异步命令
-
参数传递:通过
CommandParameter传递上下文数据 -
禁用状态可视化:依赖
CanExecute自动禁用按钮
完整示例
ViewModel:
public class MainViewModel : INotifyPropertyChanged
{
public ICommand ClearTextCommand { get; }
private string _text;
public string Text
{
get => _text;
set => SetField(ref _text, value);
}
public MainViewModel()
{
ClearTextCommand = new RelayCommand(
_ => Text = "",
_ => !string.IsNullOrEmpty(Text)
);
}
// INotifyPropertyChanged 实现略...
}
XAML:
<StackPanel>
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="清除"
Command="{Binding ClearTextCommand}"
Margin="5"/>
</StackPanel>
通过以上实现,当文本框为空时,按钮自动禁用,点击按钮清空文本框内容。

浙公网安备 33010602011771号