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.