WPF ICommand 적용해보기

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를 통해 버튼의 활성화 상태를 비즈니스 로직에 따라 선언적으로 제어할 수 있다는 점이 강력하는 것 같다.

Author avatar

웨이호프

WordPress creator and blogger.

View all posts