WPF의 MVVM 패턴에서 UI의 이벤트를 뷰모델의 로직과 연결하기 위해 ICommand 인터페이스를 사용한다.
이를 통해 뷰의 비하인드 코드를 최소화하고 코드의 재사용성과 테스트 가능성을 높일 수 있다.
RelayCommand 구현
WPF는 기본적으로 ICommand를 쉽게 구현할 수 있는 클래스를 제공하지 않으므로, 재사용 가능한 RelayCommand 클래스를 정의하여 사용한다.
using System;
using System.Windows.Input;
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 == null || _canExecute(parameter);
public void Execute(object parameter) => _execute(parameter);
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
ViewModel에서의 명령 정의
뷰모델에서 명령을 프로퍼티로 선언하고 초기화한다.
public class MainViewModel : INotifyPropertyChanged
{
private string _input;
public string Input
{
get => _input;
set { _input = value; OnPropertyChanged(); }
}
public ICommand SubmitCommand { get; }
public MainViewModel()
{
// 실행 로직과 실행 가능 조건을 설정
SubmitCommand = new RelayCommand(
execute: param => SubmitData(),
canExecute: param => !string.IsNullOrWhiteSpace(Input)
);
}
private void SubmitData()
{
System.Windows.MessageBox.Show($"Submitted: {Input}");
}
}
XAML 바인딩
버튼의 Command 속성에 뷰모델의 명령 프로퍼티를 바인딩한다.
<StackPanel>
<TextBox Text="{Binding Input, UpdateSourceTrigger=PropertyChanged}" />
<!-- Input이 비어있으면 버튼이 자동으로 비활성화됨 -->
<Button Content="제출" Command="{Binding SubmitCommand}" />
</StackPanel>
P.S
ICommand를 활용하면 UI의 행위를 데이터처럼 취급하여 바인딩할 수 있다.
특히 CanExecute를 통해 버튼의 활성화 상태를 비즈니스 로직에 따라 선언적으로 제어할 수 있다는 점이 강력하는 것 같다.