1.  View (1단계)

더보기
        Title="ICommand Example" 
        Height="100"
        Width="300">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="3*"/>
            <RowDefinition Height="2*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <TextBox />

        <Button Grid.Column="1"
                Grid.RowSpan="2"
                Content="Submit"/>

        <TextBlock Grid.Row="1"
                   TextAlignment="Center"
                   FontSize="14"/>
    </Grid>

 

 

 

 

 

2. ViewModel (1단계)

더보기
public class MainViewModel
{
    // <summary>
    // 사용자로부터 입력받은 텍스트를 저장하는 프로퍼티입니다.
    // </summary>
    private string _inputText;
    public string InputText
    {
        get => _inputText;
        set
        {
            if (_inputText != value)
            {
                _inputText = value;
            }
        }
    }

    // <summary>
    // 버튼 실행 결과를 사용자에게 보여줄 상태 메시지를 저장하는 프로퍼티입니다.
    // </summary>
    private string _statusMessage;
    public string StatusMessage
    {
        get => _statusMessage;
        set
        {
            _statusMessage = value;
        }
    }
    
    // <summary>
    // 버튼을 누르고, Command가 실행될 때 호출되는 메서드입니다.
    // 입력된 텍스트를 StatusMessage(상태 메시지)로 설정합니다.
    // </summary>
    private void BtnClick(object parameter)
    {
        StatusMessage = $"입력하신 텍스트: \"{InputText}\"";
        //MessageBox.Show(StatusMessage);
    }
}

 

 

 

 

 

3. Command

더보기
/// RelayCommand 클래스는 ICommand 인터페이스를 구현하여
/// MVVM 패턴에서 ViewModel에서 명령을 정의할 수 있도록 돕습니다.
public class RelayCommand : ICommand
{

    //  ViewModel에 구현된 실행 로직을 저장할 Action 델리게이트
    private readonly Action<object> _execute;

    // 명령 실행 가능 여부를 판단하는 Predicate 델리게이트
    private readonly Predicate<object> _canExecute;


    /// RelayCommand 생성자
    /// <param name="execute">명령이 실행될 때 호출될 메서드</param>
    /// <param name="canExecute">명령 실행 가능 여부를 판단할 메서드 (null이면 항상 실행 가능)</param>
    public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
    {
        // execute가 null인 경우 ArgumentNullException을 발생, 필수 매개변수가 null로 전달되는 것을 방지
        // nameof(execute)는 예외 메시지에 매개변수 이름을 명확히 표시해 줍니다.
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    /// <summary>
    /// CanExecuteChanged 이벤트는 명령 실행 가능 상태가 변경될 때 발생합니다.
    /// WPF의 CommandManager가 자동으로 이 이벤트를 구독하여 UI 갱신을 유도합니다.
    /// </summary>
    public event EventHandler CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    /// <summary>
    /// 명령을 실행할 수 있는지를 판단합니다.
    /// </summary>
    /// <param name="parameter">명령 실행 시 전달되는 매개변수</param>
    /// <returns>실행 가능 여부</returns>
    public bool CanExecute(object parameter)
    {
        // _canExecute가 null이면 true 반환 (항상 실행 가능), 아니면 평가
        return _canExecute == null || _canExecute(parameter);
    }

    /// <summary>
    /// 명령을 실행합니다.
    /// </summary>
    /// <param name="parameter">명령 실행 시 전달되는 매개변수</param>
    public void Execute(object parameter)
    {
        _execute(parameter);
    }
}

 

 

 

 

 

 

4. ViewModel (2단계 - Command 적용)

더보기
public class MainViewModel : INotifyPropertyChanged
{
    private string _inputText;
    public string InputText
    {
        get => _inputText;
        set
        {
            if (_inputText != value)
            {
                _inputText = value;
                OnPropertyChanged();

                // CanExecute 변경 알림
                // CommandManager.InvalidateRequerySuggested()는 WPF에게 "모든 Command"의 CanExecute 상태를 다시 평가하라고 알립니다.
                // 이 메서드는 TextBox의 입력값이 바뀌면, 버튼을 활성화 또는 비활성화할 수 있도록 유도합니다.
                // 단점: 전체 명령을 다시 평가하므로 빈번하게 호출 시 성능에 영향을 줄 수 있습니다.
                CommandManager.InvalidateRequerySuggested();
            }
        }
    }

    private string _statusMessage;
    public string StatusMessage
    {
        get => _statusMessage;
        set
        {
            _statusMessage = value;
            OnPropertyChanged();
        }
    }

    public ICommand BtnClickCommand { get; }

    public MainViewModel()
    {
        // Execute: 버튼 클릭 시 실행될 메서드
        // CanExecute: InputText에 \"Hello\"가 입력될 때만 버튼 활성화
        BtnClickCommand = new RelayCommand(BtnClick, CanSubmit);
        InputText = string.Empty;
        StatusMessage = "텍스트를 입력하세요.";
    }

    private void BtnClick(object parameter)
    {
        StatusMessage = $"입력하신 텍스트: \"{InputText}\"";
        //MessageBox.Show(StatusMessage);
    }

    private bool CanSubmit(object parameter)
    {
        // InputText가 정확히 \"Hello\"일 때만 실행 가능
        return InputText == "Hello";
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

 

 

 

 

 

5. View (2단계)

더보기
xmlns:vm="clr-namespace:프로젝트명.ViewModel"
<Window.DataContext>
    <vm:MainViewModel/>
</Window.DataContext>

 

 

 

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="3*"/>
        <RowDefinition Height="2*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="2*"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <TextBox Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}"/>

    <Button Grid.Column="1"
            Grid.RowSpan="2"
            Content="Submit"
            Command="{Binding BtnClickCommand}"/>

    <TextBlock Grid.Row="1"
               Text="{Binding StatusMessage}"
               TextAlignment="Center"
               FontSize="14"/>
</Grid>

 

 

 

 

 

6. 소스코드

 

7. 이론

더보기

1. ICommand 동작 순서

ICommand는 WPF 컨트롤의 Command 속성에 Binding 하기 위해 사용합니다.


위순서도는 ICommand의 동작방식입니다.

CanExecuteChanged이벤트가발생하면

>>CanExecute를통해 컨트롤이 사용가능한지 체크하고, IsEnable 속성을 업데이트합니다.

IsEnable이적용되는컨트롤은ButtonBase,MenuItem,Hyperlink등이있습니다.

사용자명령이 발생하면 IsEnable이 true로 활성화된 컨트롤은 Execute에 정의된 동작이 실행됩니다.

 

 

 

 

2.