C#

WPF MVVM Command ํŒจํ„ด ์™„์ „ ์ •๋ณต - RelayCommand ๊ตฌํ˜„

samie 2025. 4. 4. 10:15
WPF MVVM Command ํŒจํ„ด ์™„์ „ ์ •๋ณต - RelayCommand ๊ตฌํ˜„

๐Ÿš€ WPF MVVM Command ํŒจํ„ด ์™„์ „ ์ •๋ณต

WPF์—์„œ MVVM ํŒจํ„ด์„ ์‚ฌ์šฉํ•  ๋•Œ ๋ฒ„ํŠผ ํด๋ฆญ ๋“ฑ์˜ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฐœ๋…์ด Command ํŒจํ„ด์ž…๋‹ˆ๋‹ค.
์ด ๊ธ€์—์„œ๋Š” ICommand ์ธํ„ฐํŽ˜์ด์Šค์™€ RelayCommand ๊ตฌํ˜„์„ ์‹ค์ œ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์ƒ์„ธํžˆ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

1. ์™œ Command ํŒจํ„ด์ด ํ•„์š”ํ•œ๊ฐ€?

MVVM์—์„œ๋Š” View๊ฐ€ ViewModel์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
Command๋Š” View์™€ ViewModel ๊ฐ„์˜ ๊ฐ„์ ‘์ ์ธ ์ƒํ˜ธ์ž‘์šฉ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

  • ๐ŸŸข ์ฝ”๋“œ ๋น„ํ•˜์ธ๋“œ(XAML.cs)๋ฅผ ์ค„์ด๊ณ  ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ ํ–ฅ์ƒ
  • ๐ŸŸข Button, MenuItem, ListBox ๋“ฑ ๋‹ค์–‘ํ•œ ์ปจํŠธ๋กค์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ๐ŸŸข MVVM ๊ตฌ์กฐ๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ

2. ICommand ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์กฐ

WPF์˜ ๋ชจ๋“  ์ปค๋งจ๋“œ๋Š” ICommand๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


public interface ICommand
{
    event EventHandler CanExecuteChanged;

    bool CanExecute(object parameter);
    void Execute(object parameter);
}
- `CanExecute()`๋Š” ์ปค๋งจ๋“œ ์‹คํ–‰ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ (๋ฒ„ํŠผ ํ™œ์„ฑํ™” ์กฐ๊ฑด ๋“ฑ) - `Execute()`๋Š” ์ปค๋งจ๋“œ๊ฐ€ ์‹คํ–‰๋  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ

3. RelayCommand ๊ตฌํ˜„

๋ฐ˜๋ณต์ ์ธ ICommand ๊ตฌํ˜„์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ๋ณดํ†ต RelayCommand ๋˜๋Š” DelegateCommand๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

โœ”๏ธ ๊ธฐ๋ณธ 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)
    {
        this.execute = execute ?? throw new ArgumentNullException(nameof(execute));
        this.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;
    }
}

4. ์‹ค์Šต ์˜ˆ์ œ: ๋ฒ„ํŠผ ํด๋ฆญ์œผ๋กœ ๋ฉ”์‹œ์ง€ ๋ณ€๊ฒฝ

โœ”๏ธ MainWindow.xaml

<Window x:Class="CommandSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:CommandSample"
        Title="Command ์˜ˆ์ œ" Height="200" Width="400">

    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>

    <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
        <TextBlock Text="{Binding Message}" FontSize="20" Margin="0,0,0,10"/>
        <Button Content="๋ณ€๊ฒฝํ•˜๊ธฐ" Command="{Binding ChangeMessageCommand}" Width="120"/>
    </StackPanel>
</Window>

โœ”๏ธ MainViewModel.cs


using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;

namespace CommandSample
{
    public class MainViewModel : INotifyPropertyChanged
    {
        private string message = "์ดˆ๊ธฐ ๋ฉ”์‹œ์ง€";
        public string Message
        {
            get => message;
            set { message = value; OnPropertyChanged(); }
        }

        public ICommand ChangeMessageCommand { get; }

        public MainViewModel()
        {
            ChangeMessageCommand = new RelayCommand(_ =>
            {
                Message = "Command๋กœ ๋ณ€๊ฒฝ๋จ!";
            });
        }

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

5. CanExecute ์กฐ๊ฑด ์‚ฌ์šฉ ์˜ˆ

์‚ฌ์šฉ์ž๊ฐ€ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•  ๋•Œ๋งŒ ๋ฒ„ํŠผ์„ ํ™œ์„ฑํ™”ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋Š” CanExecute๋ฅผ ์„ค์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.


ChangeMessageCommand = new RelayCommand(
    execute: _ => Message = "์กฐ๊ฑด ๋งŒ์กฑ!",
    canExecute: _ => Message.Length < 10
);

์œ„ ์ฝ”๋“œ์—์„œ๋Š” ๋ฉ”์‹œ์ง€ ๊ธธ์ด๊ฐ€ 10์ž ๋ฏธ๋งŒ์ผ ๋•Œ๋งŒ ๋ฒ„ํŠผ์ด ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.
WPF๋Š” CommandManager.RequerySuggested๋ฅผ ํ†ตํ•ด ์ž๋™์œผ๋กœ UI ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

6. ๋งˆ๋ฌด๋ฆฌ ๋ฐ ๋‹ค์Œ ๊ธ€ ์•ˆ๋‚ด

MVVM์—์„œ Command ํŒจํ„ด์€ View์™€ ViewModel์„ ์—ฐ๊ฒฐํ•˜๋Š” ํ•ต์‹ฌ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
RelayCommand๋Š” ์ด๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ์ด๋ฉฐ, ์‹ค๋ฌด์—์„œ๋„ ๊ฑฐ์˜ ๋ชจ๋“  ํ”„๋กœ์ ํŠธ์—์„œ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์‹œ๋ฆฌ์ฆˆ์—์„œ๋Š” UserControl๊ณผ CustomControl์˜ ์ฐจ์ด์ ๊ณผ ์‹ค์ œ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์†Œ๊ฐœํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.


์ด ๊ธ€์ด ๋„์›€์ด ๋˜์…จ๋‹ค๋ฉด ๋Œ“๊ธ€๊ณผ ๊ณต์œ  ๋ถ€ํƒ๋“œ๋ ค์š”! ๐Ÿ™Œ