WPF에서 명령 처리와 매개변수 전달 방법

MVVM 패턴에서는 명령을 구현하는 여러 가지 방식이 존재하며, 각각의 특성과 사용 목적에 따라 선택할 수 있다.

1. 커스텀 명령 클래스: DelegateCommand


다음은 간단한 ICommand 인터페이스를 직접 구현한 예제이다. 이 방식은 유연성과 제어력을 제공하지만, 추가적인 라이브러리 없이도 사용 가능하다.

public class CustomCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Func<object, bool> _canExecute;

    public event EventHandler CanExecuteChanged;

    public CustomCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter) => _canExecute?.Invoke(parameter) != false;

    public void Execute(object parameter) => _execute(parameter);

    public void RaiseCanExecuteChange() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

뷰모델에서는 다음과 같이 활용할 수 있다:

public class MainViewModel
{
    public ICommand SaveAction { get; }
    public ICommand RemoveItem { get; }

    public MainViewModel()
    {
        SaveAction = new CustomCommand(OnSave, CanSave);
        RemoveItem = new CustomCommand<Customer>(OnRemove, CanRemove);
    }

    private void OnSave(object param) { /* 저장 로직 */ }
    private bool CanSave(object param) => true;

    private void OnRemove(Customer item) { /* 삭제 로직 */ }
    private bool CanRemove(Customer item) => item != null;
}

2. CommunityToolkit.Mvvm 라이브러리 사용


최신 프로젝트에서는 CommunityToolkit.Mvvm을 활용해 간결하고 안정적인 명령 정의가 가능하다.

using CommunityToolkit.Mvvm.Input;

public partial class MainViewModel
{
    [RelayCommand]
    private void SaveData() => Console.WriteLine("저장됨");

    [RelayCommand]
    private void RemoveCustomer(Customer customer)
    {
        // 고객 삭제 로직
    }

    [RelayCommand]
    private async Task FetchDataAsync()
    {
        await Task.Delay(500);
        // 비동기 데이터 로드
    }
}

이 방식은 컴파일 타임 코드 생성을 기반으로 하며, 성능과 유지보수성 측면에서 우수하다.

3. Prism 프레임워크 통합


대규모 애플리케이션에서 사용되는 Prism.Commands는 강력한 기능을 제공한다.

using Prism.Commands;

public class MainViewModel
{
    public DelegateCommand SaveCommand { get; }
    public DelegateCommand<Customer> RemoveCommand { get; }

    public MainViewModel()
    {
        SaveCommand = new DelegateCommand(Save, CanSave);
        RemoveCommand = new DelegateCommand<Customer>(Remove, CanRemove);
    }

    private void Save() { /* 저장 처리 */ }
    private bool CanSave() => true;

    private void Remove(Customer item) { /* 삭제 처리 */ }
    private bool CanRemove(Customer item) => item != null;
}

비교 및 선택 기준

방식 장점 단점 추천 대상
커스텀 DelegateCommand 완전한 제어, 의존성 없음 반복 코드, 유지보수 어려움 단순 프로젝트 또는 교육용
CommunityToolkit.Mvvm 간결한 문법, 비동기 지원, 속성 변경 알림 추가 라이브러리 필요 신규 개발 프로젝트
PrismDelegateCommand 확장성, 전체 프레임워크 통합 무거운 라이브러리, 복잡성 대규모 워크플로우 기반 앱

매개변수 전달 예시 (XAML 바인딩)

명령에 파라미터를 전달하는 방법은 다음과 같다.

<!-- 고정 값 전달 -->
<Button Content="초기화"
        Command="{Binding ResetCommand}"
        CommandParameter="default" />

<!-- 현재 항목 바인딩 (ListView/ListBox 내부) -->
<ListBox ItemsSource="{Binding Items}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Title}" />
                <Button Content="삭제"
                        Command="{Binding DataContext.RemoveCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
                        CommandParameter="{Binding}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

이처럼, 명령과 데이터 사이의 연결을 효과적으로 관리할 수 있으며, 특히 컬렉션 내 요소별 작업 시 매우 유용하다.

태그: WPF MVVM ICommand CommunityToolkit.Mvvm Prism

8월 1일 03:00에 게시됨