WPF Command - Part1

프로그래밍 2009.01.12 19:18 Posted by 아일레프

들어가기 전에

얼마 전에 MSDN MagazineUnderstanding RoutedEvent and Command라는 제목의 article이 올라왔습니다. RoutedEvent Command는 자주 사용했던 기능이기에 깊게 읽어보지는 않았습니다. “이 정도는 나도 알고 있다고라는 마음이었죠. 몇 줄 읽어보고 말았습니다..

그 후 Composite Application Guidance for WPF를 공부하기 시작했는데 “DelegateCommand<T>”라는 녀석이 나오더군요. 정말 전혀 모르겠더군요. Command에 대해 정확한 개념이 없다는 것을 알게 된 후 MSDN Magazine Command 부분을 다시 읽어 보았는데 영어가 암호문처럼 느껴지면서 적지 않은 절망감을 느꼈습니다. Article을 이해하기 위해 출퇴근 하는 지하철 안에서 몇 번을 읽었는지 모릅니다. 그리고 집에 와 Command Pattern에 대한 자료와 예제를 반복해 보았습니다. 만약 여러분이 저와 같은 고생을 하지 않고 간단히 WPF Command를 책하나 혹은 MSDN만으로 이해했다면 여러분을 낳아주신 부모님께 감사드릴 일입니다. 그리고 이 Post를 더 이상 읽을 필요도 없을 것입니다.

그러나 Command라는 노래만을 듣고 악보를 그려낼 만할 음감은 없지만 알고 싶다는 호기심과 끈기가 있는 분이라면 이 Post를 읽어주셨으면 합니다.(저는 이런 분들을 더 좋아합니다.) 여러분께 조금의 도움은 될 수 있을 것입니다.

서론은 그만두고, 본격적으로 포스팅을 하도록 하겠습니다. 바라건대 기본적인 RoutedCommand 사용법(CommandBinding, InputBinding) RoutedEvent를 공부하신 후 Magazine Article Understandeing RoutedEvent and Command을 읽어보시기 바랍니다. 그리고 궁금한 부분이나 도무지 풀리지 않을 때 이 포스트를 읽으시면 될 것 같습니다. 그럼 시작해보겠습니다.(해당 Article이 이해가 완전히 되시는 분은 이 포스트을 읽지 않으셔도 됩니다.)

 

Command Overview

RoutedCommand에 대해 알아보기 전에 Command에 대해 알아보도록 하겠습니다. Command1-특정 작업을 서술하는 녀석 그리고 그 2-특정 작업을 요청하는 녀석과 3-특정 작업을 처리하는 녀석을 분리하기 위해 그 필요성이 생겼습니다. ( 1,2,3번은 중요합니다. 모든 커맨드를 사용할 때 항상 1,2,3번에 해당하는 녀석이 무엇인지 마음속으로 추측 해보시기 바랍니다.)

보통 특정 작업을 서술하는 녀석을 Command Object라고 합니다. 그리고 특정 작업을 요청하는 녀석은 Command Invoker 혹은 Command Source, 특정 작업을 처리하는 녀석을 Command Target이라고 합니다. 정리하고 넘어가죠.

1.     특정 작업을 서술하는 녀석 – Command Object

2.     특정 작업을 요청하는 녀석 – Command Invoker, Command Source

3.     특정 작업을 처리하는 녀석 – Command Target


 여러분이 맥도널드에서 특정 햄버거를 주문한다고 가정해보죠. 여러분은 Command Invoker가 되는 것이고, 햄버거 주문을 받는 웨이터는 Command Object, 직접 햄버거를 만드는 사람은 Command Target이 되는 것입니다. 여러분이 햄버거를 주문했을 때 그 내부에서 햄버거를 어떻게 만드는 지 전혀 알지 못해도 되는 이유는 맥도널드가 이 Command Pattern을 사용하고 있기 때문입니다.

 

잠깐 MSDN Article의 예를 보고 넘어가도록 하겠습니다.

<Button Command=”ApplicationCommands.Save”>SAVE</Button>

 

<UserControl …>

           <UserCotrol.CommandBindings>

                     <CommandBinding Command=”ApplicationCommands.Save”

                       CanExecute=”OnCanExecute” Executed=”OnExecute”/>

           </UserControl.CommandBindings>

 

Public class MyControl : UserControl

{

           Private void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)

        {

                   e.CanExecute = true;

           }

 

           Private void OnExecute(object sender, ExecuteRoutedEventArgs e)

        {

                   DoSaveOperation();

           }

           Private void DoSaveOperation()

        {

                  

           }

}

위 예에서 1,2,3번에 해당하는 것이 무엇인지 알아보도록 할까요?

1.     Command Object : “ApplicationCommands.Save” static Command 객체

2.     Command Invoker : Button 객체

3.     Command Target : MyControl 객체

 
 맞추셨습니까? 너무 쉽다 구요? ^^;;; 다행입니다. 이것이 기본입니다. 다시 강조 드리지만 반드시 1,2,3번에 해당하는 녀석이 무엇인지 머리 속으로 쭉 생각하시기 바랍니다.

더 나가서 위 예제가 무엇을 하는 녀석인지 볼까요? WPF에는 미리 정의된 Command객체들이 있습니다. ApplicationCommand.Save도 그 중 하나입니다. Button을 누르면(Invoker가 작업을 요청하면) ApplicationCommand.Save Command Target을 찾습니다.(어떻게 찾는지는 다음 Posting에 설명드리겠습니다.) Command Target으로 MyControl 객체가 선택되고 DoSaveOperation()을 실행합니다.

 보시다시피 작업을 요청하는 녀석과 작업을 기술하는 녀석, 작업을 실행하는 녀석이 분리되어있습니다. 이런 것을 CommandPattern이라 부른답니다.

 

Command 만들어보기

위 예제의 세부적인 내용은 일단 미뤄두고 Command객체를 만들어 보도록 하겠습니다. Command 객체를 만들기 위해서는 ICommand라는 Interface를 상속받으면 됩니다. Interface는 이렇게 생겼습니다.

public interface ICommand

    {

event EventHandler CanExecuteChanged;

bool CanExecute(object parameter);

void Execute(object parameter);

    }

CanExecuteChanged이벤트와 CanExecute메소드는 잘 모르더라도 Execute 메소드의 역할은 알 것 같죠? 아마도 Invoker가 이 Command Invoke하면 Execute가 실행될 것 같습니다. 이 정도의 정보만으로도 충분합니다. 한번 AddCommand라는 녀석을 만들어보겠습니다.

public class AddCommand : ICommand

    {

        private string description ="Add";

        public string Description

        {

            get { return description; }

        }

        public bool CanExecute(object parameter)

        {

            return true;

        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)

        {

            System.Diagnostics.Debug.WriteLine("Add명령 실행");

        }

    }

CanExecute메소드는 일단 true를 반환하게 하고 Execute 메소드는 “Add명령 실행이라는 문자열을 출력하게끔 했습니다. 그리고 Description이라는 Property를 만들어 Command의 내용을 기술하게 했습니다. , 그럼 이 Command를 사용해볼까요?

<Window x:Class="CommandTest.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:loacl="clr-namespace:CommandTest"

    Title="Window1" Height="300" Width="300">

    <Window.Resources>

        <loacl:AddCommand x:Key="AddCommand"/>

    </Window.Resources>

    <Button Command="{StaticResource AddCommand}"

            Content="{Binding RelativeSource={RelativeSource Self}

                              ,Path=Command.Description}"></Button>

</Window>

먼저 Command객체를 XAML로 생성하기 위해 local이라는 prefix namespace를 추가했습니다. 그리고 객체를 생성해 Resources에 등록 했습니다. . 제가 여러분을 너무 무시하나요? 나머지는 그냥 스킵 하겠습니다. 하여간 위 프로그램을 실행하면

 

위와 같은 창이 생기게 되고 Add버튼을 누르면 “ADD 명령 실행이라는 문자열이 Output창에 출력될 것 입니다. 훌륭하군요. 제가 생각하는 결과가 실행되었습니다. 혹시 글을 읽으시면서 Command Object, Command Invoker, Command Target이 무엇인지 생각해보셨습니까? 그랬다면 여러분은 멋진 분입니다. 1,2,3에 해당하는 것이 무엇이지 알아봅시다.

1.     Command Object : AddCommand 객체

2.     Command Invoker : Button 객체

3.     Command Target : AddCommand 객체

? Command Object Command Target이 같네요. 분리되지 않았습니다. 게다가 방금 제가 만든 Command객체는 Command를 실행한 후의 작업이 항상 “Add 명령 실행이라는 문자열을 실행하게 됩니다. 이 작업을 수정하려면 Command객체를 수정하고 다시 컴파일 해야겠네요. 이건 전혀 훌륭하지 않습니다. 망했습니다. .

n  문제 : 어떻게 Command Object Command Target을 분리하지?

다음 장에는 WPF에서 RoutedCommand를 사용해 어떻게 Command Object Command Target을 분리하는 지 알아보도록 하겠습니다. 여러분도 이 문제를 어떻게 해결할 지 생각해보세요 ^^;;


신고
TAG ,