12. Command - CanExecute

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.


댓글을 사용할 수 없습니다.