명령 개요

업데이트: 2007년 11월

명령은 장치 입력보다 더 의미적 수준에서 입력 처리를 제공하는 WPF(Windows Presentation Foundation)의 입력 메커니즘입니다. 이러한 명령의 예로 여러 응용 프로그램에서 볼 수 있는 복사, 잘라내기, 붙여넣기 작업이 있습니다.

이 개요 부분에서는 WPF에서 사용하는 명령 및 명령 모델을 구성하는 클래스에 대해 알아보고 응용 프로그램에서 명령을 사용하고 만드는 방법에 대해 설명합니다.

이 항목에는 다음 단원이 포함되어 있습니다.

  • 명령의 정의
  • WPF의 간단한 명령 예제
  • WPF 명령의 네 가지 주요 개념
  • 명령 라이브러리
  • 사용자 지정 명령 만들기
  • 관련 항목

명령의 정의

WPF의 명령 시스템은 RoutedCommandRoutedEvent를 기반으로 합니다. 명령은 반드시 RoutedCommand일 필요가 없으며 단순히 ICommand를 구현할 수도 있지만 이 개요에서는 주로 RoutedCommand 모델에 중점을 두어 설명합니다.

명령은 작업의 의미 체계 및 출처를 해당 논리와 구분한다는 점에서 단추에 연결된 간단한 이벤트 처리기와 다릅니다. 이와 같은 특징 때문에 서로 다른 여러 소스가 동일한 명령 논리를 호출할 수 있으며 명령 논리를 여러 대상에 대해 사용자 지정할 수 있습니다.

이러한 명령의 예로 여러 응용 프로그램에서 볼 수 있는 복사, 잘라내기, 붙여넣기 등의 편집 작업이 있습니다. 명령의 의미 체계는 응용 프로그램과 클래스 전체에서 일관되지만 작업 논리는 작업 대상 개체마다 다릅니다. Ctrl+X 키 조합은 텍스트 클래스, 이미지 클래스 및 웹 브라우저에서 잘라내기 명령을 호출하지만 잘라내기 작업을 수행하는 실제 논리는 명령을 호출한 소스가 아니라 잘라내기가 발생하는 개체 또는 응용 프로그램에 따라 정의됩니다. 예를 들어 텍스트 개체는 선택한 텍스트를 잘라내서 클립보드에 복사하는 반면 이미지 개체는 선택한 이미지를 잘라내지만 두 클래스 모두에서 동일한 명령 소스인 KeyGesture 또는 ToolBar 단추를 사용하여 명령을 호출할 수 있습니다.

WPF의 간단한 명령 예제

WPF에서 명령을 사용하는 가장 쉬운 방법은 명령 라이브러리 클래스 중 하나에서 미리 정의된 RoutedCommand를 사용하는 것입니다. 즉, 명령 처리 및 명령 호출을 기본적으로 지원하는 컨트롤을 사용하면 쉽습니다. Paste 명령은 ApplicationCommands 클래스의 미리 정의된 명령 중 하나입니다. TextBox 컨트롤에는 Paste 명령을 처리하는 논리가 기본적으로 포함되어 있습니다. 또한 MenuItem 클래스에는 명령 호출 기능이 기본적으로 포함되어 있습니다.

다음 예제에서는 클릭하면 TextBox에서 Paste 명령을 호출하도록 MenuItem을 설정하는 방법을 보여 줍니다. 이 예제에서는 TextBox에 키보드 포커스가 있다고 가정합니다.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

WPF 명령의 네 가지 주요 개념

WPF의 라우트된 명령 모델은 명령, 명령 소스, 명령 대상 및 명령 바인딩이라는 네 가지 주요 개념으로 나눌 수 있습니다.

  • 명령은 실행할 작업입니다.

  • 명령 소스는 명령을 호출하는 개체입니다.

  • 명령 대상은 명령이 실행되는 개체입니다.

  • 명령 바인딩은 명령 논리를 명령에 매핑하는 개체입니다.

앞의 예제에서는 Paste 명령이 명령이고, MenuItem이 명령 소스이고, TextBox가 명령 대상이며 TextBox 컨트롤이 명령 바인딩을 제공합니다. 명령 대상 클래스가 항상 CommandBinding을 제공하는 것은 아니며, 응용 프로그램 개발자가 CommandBinding을 직접 만들거나 CommandBinding이 명령 대상의 상위 요소에 연결되어 있는 경우도 많습니다.

명령

WPF의 명령은 ICommand 인터페이스를 구현하여 만들어집니다. ICommandExecuteCanExecute라는 두 가지 메서드와 CanExecuteChanged라는 하나의 이벤트를 노출합니다. Execute는 명령과 연결된 동작을 수행하고, CanExecute는 명령을 현재 명령 대상에 대해 실행할 수 있는지 여부를 결정합니다. 명령 작업을 중앙 관리하는 명령 관리자가 이미 발생했지만 명령 바인딩을 통해 아직 실행되지 않은 명령을 무효화할 수 있는 명령 소스 변경 사항을 발견하면 CanExecuteChanged가 발생합니다. WPF에서는 RoutedCommand 클래스가 ICommand의 구현이며 이 개요에서는 이 클래스에 대해 중점적으로 설명합니다.

WPF의 주요 입력 소스는 마우스, 키보드, 잉크 및 라우트된 명령입니다. 장치에서 발생하는 입력은 RoutedEvent를 통해 입력 이벤트가 발생했음을 응용 프로그램 페이지의 개체에 알립니다. RoutedCommand도 마찬가지입니다. RoutedCommandExecuteCanExecute 메서드는 명령에 대한 응용 프로그램 논리를 포함하지 않으며 대신 CommandBinding을 가진 개체를 찾을 때까지 요소 트리를 터널링 및 버블링하는 라우트된 이벤트를 발생시킵니다. CommandBinding에는 이러한 이벤트에 대한 처리기가 들어 있어 해당 처리기가 명령을 수행합니다. WPF의 이벤트 라우팅에 대한 자세한 내용은 라우트된 이벤트 개요를 참조하십시오.

RoutedCommandExecute 메서드는 명령 대상에서 PreviewExecutedExecuted 이벤트를 발생시킵니다. RoutedCommandCanExecute 메서드는 명령 대상에서 CanExecutePreviewCanExecute 이벤트를 발생시킵니다. 이러한 이벤트는 해당 명령의 CommandBinding을 가진 요소를 찾을 때까지 요소 트리를 통해 터널링 및 버블링됩니다.

WPF에서는 여러 클래스에 분산된 형태로 공용 라우트된 명령인 MediaCommands, ApplicationCommands, NavigationCommands, ComponentCommands, EditingCommands를 제공합니다. 이러한 클래스는 RoutedCommand 개체로만 구성되며 명령 논리는 구현하지 않습니다. 명령 논리는 명령이 실행되는 개체에서 구현합니다.

명령 소스

명령 소스는 명령을 호출하는 개체입니다. 명령 소스의 예로는 MenuItem, ButtonKeyGesture가 있습니다.

WPF의 명령 소스는 일반적으로 ICommandSource 인터페이스를 구현합니다.

ICommandSourceCommand, CommandTargetCommandParameter라는 세 가지 속성을 노출합니다.

ICommandSource를 구현하는 WPF 클래스로는 ButtonBase, MenuItem, HyperlinkInputBinding이 있습니다. ButtonBase, MenuItemHyperlink는 클릭될 때 명령을 호출하고, InputBinding은 연결된 InputGesture가 수행될 때 명령을 호출합니다.

다음 예제에서는 ContextMenuMenuItemProperties 명령의 명령 소스로 사용하는 방법을 보여 줍니다.

<StackPanel>
  <StackPanel.ContextMenu>
    <ContextMenu>
      <MenuItem Command="ApplicationCommands.Properties" />
    </ContextMenu>
  </StackPanel.ContextMenu>
</StackPanel>
StackPanel cmdSourcePanel = new StackPanel();
ContextMenu cmdSourceContextMenu = new ContextMenu();
MenuItem cmdSourceMenuItem = new MenuItem();

// Add ContextMenu to the StackPanel.
cmdSourcePanel.ContextMenu = cmdSourceContextMenu;
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem);

// Associate Command with MenuItem.
cmdSourceMenuItem.Command = ApplicationCommands.Properties;

일반적으로 명령 소스는 CanExecuteChanged 이벤트를 수신합니다. 이 이벤트는 현재 명령 대상에 대해 실행할 명령의 기능이 변경되었을 수 있음을 명령 소스에 알립니다. 명령 소스는 CanExecute 메서드를 사용하여 RoutedCommand의 현재 상태를 쿼리할 수 있습니다. 명령을 실행할 수 없는 경우 명령 소스는 자체적으로 비활성화됩니다. 명령을 실행할 수 없을 때 MenuItem이 회색으로 표시되는 경우가 여기에 해당합니다.

InputGesture를 명령 소스로 사용할 수 있습니다. WPF에는 KeyGestureMouseGesture라는 두 가지 유형의 입력 제스처가 있습니다. KeyGesture는 Ctrl+C 같은 바로 가기 키입니다. KeyGestureKeyModifierKeys의 집합으로 구성되고, MouseGestureMouseActionModifierKeys의 선택적 집합으로 구성됩니다.

InputGesture를 명령 소스로 사용하려면 여기에 명령을 연결해야 합니다. 이 작업은 몇 가지 방법으로 수행할 수 있습니다. 그 중 하나는 InputBinding을 사용하는 것입니다.

다음 예제에서는 KeyGestureRoutedCommand 사이에 KeyBinding을 만드는 방법을 보여 줍니다.

<Window.InputBindings>
  <KeyBinding Key="B"
              Modifiers="Control" 
              Command="ApplicationCommands.Open" />
</Window.InputBindings>
KeyGesture OpenKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

KeyBinding OpenCmdKeybinding = new KeyBinding(
    ApplicationCommands.Open,
    OpenKeyGesture);

this.InputBindings.Add(OpenCmdKeybinding);

RoutedCommandInputGestureCollectionInputGesture를 추가하여 InputGestureRoutedCommand에 연결할 수도 있습니다.

다음 예제에서는 RoutedCommandInputGestureCollectionKeyGesture를 추가하는 방법을 보여 줍니다.

KeyGesture OpenCmdKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture);

CommandBinding

CommandBinding은 명령을 구현하는 이벤트 처리기에 명령을 연결합니다.

CommandBinding 클래스에는 Command 속성과 PreviewExecuted, Executed, PreviewCanExecuteCanExecute 이벤트가 포함됩니다.

CommandCommandBinding을 연결하는 명령이고, PreviewExecutedExecuted 이벤트에 연결된 이벤트 처리기는 명령 논리를 구현합니다. PreviewCanExecuteCanExecute 이벤트에 연결된 이벤트 처리기는 현재 명령 대상에 대해 명령을 실행할 수 있는지 여부를 결정합니다.

다음 예제에서는 응용 프로그램의 루트 WindowCommandBinding을 만드는 방법을 보여 줍니다. CommandBindingOpen 명령을 ExecutedCanExecute 처리기와 연결합니다.

<Window.CommandBindings>
  <CommandBinding Command="ApplicationCommands.Open"
                  Executed="OpenCmdExecuted"
                  CanExecute="OpenCmdCanExecute"/>
</Window.CommandBindings>
// Creating CommandBinding and attaching an Executed and CanExecute handler
CommandBinding OpenCmdBinding = new CommandBinding(
    ApplicationCommands.Open,
    OpenCmdExecuted,
    OpenCmdCanExecute);

this.CommandBindings.Add(OpenCmdBinding);

그런 다음 ExecutedRoutedEventHandlerCanExecuteRoutedEventHandler를 만듭니다. ExecutedRoutedEventHandler는 명령이 실행되었음을 알리는 문자열을 표시하는 MessageBox를 엽니다. CanExecuteRoutedEventHandlerCanExecute 속성을 true로 설정합니다.

Private Sub OpenCmdExecuted(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
    Dim command, targetobj As String
    command = CType(e.Command, RoutedCommand).Name
    targetobj = CType(sender, FrameworkElement).Name
    MessageBox.Show("The " + command + " command has been invoked on target object " + targetobj)
End Sub
void OpenCmdExecuted(object target, ExecutedRoutedEventArgs e)
{
    String command, targetobj;
    command = ((RoutedCommand)e.Command).Name;
    targetobj = ((FrameworkElement)target).Name;
    MessageBox.Show("The " + command +  " command has been invoked on target object " + targetobj);
}
Private Sub OpenCmdCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
    e.CanExecute = True
End Sub
void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}

CommandBinding은 응용 프로그램이나 컨트롤의 루트 Window와 같은 특정 개체에 연결됩니다. CommandBinding이 연결된 개체에 따라 바인딩 범위가 정의됩니다. 예를 들어 Executed 이벤트로 명령 대상의 상위 요소에 연결된 CommandBinding을 찾을 수 있지만 명령 대상의 하위 요소에 연결된 CommandBinding은 찾을 수 없습니다. 이는 이벤트를 발생시킨 개체로부터 RoutedEvent가 터널링 및 버블링되는 방법 때문입니다.

TextBox 클래스와 Cut, CopyPaste 명령의 경우처럼 CommandBinding이 명령 대상 자체에 연결되기도 합니다. 그러나 대개 CommandBinding을 명령 대상의 상위 요소(예: 주 Window 또는 응용 프로그램 개체)에 연결하면 더 편리하며, 특히 동일한 CommandBinding을 여러 명령 대상에 사용할 수 있는 경우에는 이 방법을 사용하는 것이 좋습니다. 이러한 디자인 결정 사항은 명령 인프라를 직접 만들 때 고려하는 것이 좋습니다.

명령 대상

명령 대상은 명령이 실행될 요소입니다. RoutedCommand와 관련하여 명령 대상은 ExecutedCanExecute의 라우팅이 시작되는 요소입니다. 앞에서 설명했듯이 WPF에서는 ICommandRoutedCommand인 경우에만 ICommandSourceCommandTarget 속성을 적용할 수 있습니다. CommandTargetICommandSource에 설정되어 있고 해당 명령이 RoutedCommand가 아니면 명령 대상이 무시됩니다.

명령 소스가 명령 대상을 명시적으로 설정할 수 있습니다. 명령 대상이 정의되어 있지 않으면 키보드 포커스가 있는 요소가 명령 대상으로 사용됩니다. 키보드 포커스가 있는 요소를 명령 대상으로 사용하면 응용 프로그램 개발자가 명령 대상을 따로 관리하지 않고 같은 명령 소스를 사용하여 여러 대상에서 명령을 호출할 수 있다는 장점이 있습니다. 예를 들어 TextBox 컨트롤과 PasswordBox 컨트롤이 있는 응용 프로그램에서 MenuItem이 붙여넣기 명령을 호출할 경우, TextBoxPasswordBox 중 키보드 포커스가 어느 컨트롤에 있는지에 따라 대상이 결정됩니다.

다음 예제에서는 태그 및 코드 숨김에서 명령 대상을 명시적으로 설정하는 방법을 보여 줍니다.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste"
              CommandTarget="{Binding ElementName=mainTextBox}" />
  </Menu>
  <TextBox Name="mainTextBox"/>
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

CommandManager

CommandManager는 명령과 관련된 여러 가지 기능을 수행합니다. 예를 들어 특정 요소에서 PreviewExecuted, Executed, PreviewCanExecuteCanExecute 이벤트 처리기를 추가하고 제거하는 정적 메서드 집합을 제공하며, 특정 클래스에 CommandBindingInputBinding 개체를 등록하는 데 사용되기도 합니다. CommandManager는 또한 CanExecuteChanged 이벤트를 발생시켜야 하는 시점을 RequerySuggested 이벤트를 통해 명령에 알립니다.

InvalidateRequerySuggested 메서드는 CommandManager에서 RequerySuggested 이벤트를 발생시키도록 합니다. 이 기능은 명령을 비활성화/활성화해야 하지만 CommandManager에서 이를 인식할 수 없는 경우에 유용합니다. InvalidateRequerySuggested를 호출하여 CommandManager에서 RequerySuggested 이벤트를 강제로 발생시키도록 하는 방법의 예제를 보려면 Disable Command Source via Dispatcher Timer 샘플을 참조하십시오.

명령 라이브러리

WPF에서는 미리 정의된 명령 집합을 제공합니다. 명령 라이브러리는 ApplicationCommands, NavigationCommands, MediaCommands, EditingCommandsComponentCommands 클래스로 구성됩니다. 이러한 클래스는 Cut, BrowseBack, BrowseForwardPlay, Stop, Pause 같은 명령을 제공합니다.

이러한 명령 대부분에는 기본 입력 바인딩 집합이 있습니다. 예를 들어 응용 프로그램에서 복사 명령을 처리하도록 지정하면 "Ctrl+C" 키보드 바인딩이 자동으로 설정됩니다. 그 외에 Tablet PC 펜 제스처 및 음성 정보와 같이 다른 입력 장치에 대한 바인딩도 기본적으로 사용할 수 있습니다.

XAML을 사용하여 다양한 명령 라이브러리에서 명령을 참조하는 경우에는 정적 명령 속성을 노출하는 라이브러리의 클래스 이름을 생략할 수 있습니다. 일반적으로 명령 이름은 문자열같이 명확하며 소유 형식을 통해 명령이 논리적으로 그룹화되어 있지만 이러한 소유 형식은 명령을 명확하게 구분하는 데 반드시 필요한 것은 아닙니다. 예를 들어 Command="ApplicationCommands.Cut" 대신 Command="Cut"와 같이 간단하게 명령을 지정할 수 있습니다. 이 편리한 메커니즘은 명령에 사용할 수 있도록 WPF XAML 프로세서에서 기본적으로 구현됩니다. 엄밀하게 말하자면 이 메커니즘은 WPF XAML 프로세스가 로드 시 참조하는 ICommand의 형식 변환기 동작입니다.

사용자 지정 명령 만들기

명령 라이브러리 클래스의 명령으로 필요한 기능을 구현할 수 없는 경우에는 사용자 지정 명령을 만들 수 있습니다. 사용자 지정 명령은 두 가지 방법으로 만들 수 있습니다. 하나는 ICommand 인터페이스를 처음부터 만들어 구현하는 것이고, 다른 하나는 RoutedCommand 또는 RoutedUICommand를 만드는 방법으로, 이 방법이 더 일반적으로 사용됩니다.

사용자 지정 RoutedCommand를 만드는 예제를 보려면 사용자 지정 RoutedCommand 만들기 샘플을 참조하십시오.

참고 항목

작업

사용자 지정 RoutedCommand 만들기 샘플

방법: ICommandSource 구현

방법: MenuItem에 명령 추가

Disable Command Source via Dispatcher Timer 샘플

개념

입력 개요

라우트된 이벤트 개요

참조

RoutedCommand

CommandBinding

InputBinding

CommandManager