[WPF] 동기, 비동기 RelayCommand

728x90

WPF(Windows Presentation Foundation)에서 Command는 MVVM(Model-View-ViewModel) 디자인 패턴을 사용할 때 사용되는 중요한 요소 중 하나입니다. Command는 UI에서 발생하는 이벤트와 실행 로직(메서드 또는 기능) 사이의 결합을 제거하고, 코드를 더 모듈화하고 유연하게 만들어줍니다.

Command를 사용하는 주요 이유 중 하나는 코드의 재사용성과 유지보수성을 높이는 데에 있습니다. 일반적으로, UI 요소(예: Button)에 직접적으로 클릭 이벤트 핸들러를 연결하는 대신 Command를 사용하여 해당 이벤트를 처리합니다.

 

예제를 통해 배워보겠습니다.

 

구성은 아래그림처럼 되어있습니다.

etc-image-0

 

그리고 비동기 command를 사용하기 위해서는 nuget패키지를 하나 설치해야합니다.

 

nuget패키지 관리자에서 아래 패키지를 설치합니다.

etc-image-1

 

설치가 완료되었다면 아래 xaml,클래스에서 코드를 입력하면 됩니다.

 

MainWindow.xaml

<Window x:Class="WpfTriggerAndCommand.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfTriggerAndCommand"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel>
            <ProgressBar Height="20" Value="{Binding ProgressValue}"></ProgressBar>
            <TextBox x:Name="textBox1"></TextBox>
            <Button Content="버튼" Command="{Binding Command}" CommandParameter="{Binding ElementName=textBox1,Path=Text}"></Button>
        </StackPanel>
    </Grid>
</Window>

 

MainWindow.cs

using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WpfTriggerAndCommand.ViewModels;

namespace WpfTriggerAndCommand
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        MainViewModel viewModel;

        public MainWindow()
        {
            InitializeComponent();

            viewModel = new MainViewModel();

            //데이터를 뷰모델과 연결            
            DataContext = viewModel;
        }
    }
}

 

 

MainViewModel.cs

using Microsoft.Toolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using WpfTriggerAndCommand.Commands;

namespace WpfTriggerAndCommand.ViewModels
{
    internal class MainViewModel : INotifyPropertyChanged
    {

        private int progressValue;
        
        public ICommand Command { get; set; }

        public MainViewModel()
        {
            //동기식
            //Command = new RelayComnmand<object>(ExcuteButton, CanButton);

            //비동기식
            Command = new AsyncRelayCommand(ExcuteButton2);
            //task로 wraping되기에 Task로 참조할수 있음
            Task t = ExcuteButton2();
        }

        public int ProgressValue
        {
            get { return progressValue; }
            set 
            { 
                progressValue = value;
                NotifyPropertyChanged(nameof(ProgressValue));
            }
        }

        


        bool CanButton(object param)
        {
            if(param == null)
            {
                return true;
            }

            return param.ToString().Equals("aaa")?true:false;
        }

        void ExcuteButton(object param)
        {
            //비동기작업 실행
            int w = 0;
            Task task1 = Task.Run(() => 
            {
                for(int i = 0; i < 10; i++)
                {
                    ProgressValue = i;
                }
            });

            Task task2 = Task.Run(() =>
            {
                for (int i = 0; i < 50; i++)
                {
                    ProgressValue = i;
                    w = i;
                 
                }
            });

            //앞서 작업이 끝날때까지 해당 코드에서 대기함
            task2.Wait();
            MessageBox.Show(w + ""); //49 출력
            
        }


        //async를 사용하면 비동기 실행이 가능하며 await 키워드를 사용가능
        //async를 사용하면 메인스레드를 정지시키지 않고 작업을 기다림
        public async Task ExcuteButton2()
        {
            int w = 0;
          
            //Task안의 제너레이터는 리턴 유형
            Task<int> task1 = Task.Run(() =>
            {
                int j = 0;
                for (int i = 0; i < 50; i++)
                {
                    ProgressValue = i;
                    j  = i;
                    
                }
                
                return j;
            });


            w = await task1;
            MessageBox.Show(w + ""); //49 출력
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

 

 

RelayCommand.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace WpfTriggerAndCommand.Commands
{
        internal class RelayCommand<T> : ICommand
    {
        readonly Action<T> _execute;
        readonly Predicate<T> _canexecute;

        public RelayCommand(Action<T> action, Predicate<T> predicate)
        {
            _execute = action;
            _canexecute = predicate;
        }

        //CanExecute에 위임할 수 없을때도 생성자 생성 가능
        public RelayCommand(Action<T> action) : this(action, null)
        {

        }


        public event EventHandler? CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public bool CanExecute(object? parameter)
        {
            return _canexecute == null ? true : _canexecute((T)parameter);
        }

        public void Execute(object? parameter)
        {
            _execute((T)parameter);
        }
    }
}



'C# Programming > WPF' 카테고리의 다른 글

[WPF] 디자인 깨질때 해결방법  (0) 2024.05.16
[WPF] Settings.settings 만들기  (0) 2024.05.16
[WPF] 트리거(Trigger)  (0) 2024.05.16
[WPF] 스타일(Xaml 리소스)  (0) 2024.05.16
[WPF] 모델 생성과 바인딩  (0) 2024.05.16