방법: ICommandSource 구현

이 예제에서는 ICommandSource를 구현하여 명령 원본을 만드는 방법을 보여 줍니다. 명령 원본은 명령을 호출하는 방법을 알고 있는 개체입니다. ICommandSource 인터페이스는 다음 세 멤버를 노출합니다.

  • Command: 호출될 명령입니다.
  • CommandParameter: 명령 원본에서 명령을 처리하는 메서드로 전달되는 사용자 정의 데이터 형식입니다.
  • CommandTarget: 명령이 실행되는 개체입니다.

이 예제에서는 Slider 컨트롤에서 상속되고 ICommandSource 인터페이스를 구현하는 클래스가 만들어집니다.

예제

WPF는 ICommandSource를 구현하는 다양한 클래스(예: Button, MenuItemHyperlink)를 제공합니다. 명령 원본은 명령을 호출하는 방법을 정의합니다. 이러한 클래스는 클릭할 때 명령을 호출하며, Command 속성이 설정된 경우에만 명령 원본 이 됩니다.

이 예제에서는 슬라이더가 이동되거나 더 정확하게는 Value 속성이 변경되면 명령을 호출합니다.

다음은 클래스 정의입니다.

public class CommandSlider : Slider, ICommandSource
{
    public CommandSlider() : base()
    {
    }
Public Class CommandSlider
    Inherits Slider
    Implements ICommandSource
    Public Sub New()
        MyBase.New()

    End Sub

다음 단계는 ICommandSource 멤버를 구현하는 것입니다. 이 예제에서는 속성이 DependencyProperty 개체로 구현됩니다. 이렇게 하면 속성에서 데이터 바인딩을 사용할 수 있습니다. DependencyProperty 클래스에 대한 자세한 내용은 종속성 속성 개요를 참조하세요. 데이터 바인딩에 대한 자세한 내용은 데이터 바인딩 개요를 참조하세요.

Command 속성만 여기에 표시됩니다.

// Make Command a dependency property so it can use databinding.
public static readonly DependencyProperty CommandProperty =
    DependencyProperty.Register(
        "Command",
        typeof(ICommand),
        typeof(CommandSlider),
        new PropertyMetadata((ICommand)null,
        new PropertyChangedCallback(CommandChanged)));

public ICommand Command
{
    get
    {
        return (ICommand)GetValue(CommandProperty);
    }
    set
    {
        SetValue(CommandProperty, value);
    }
}
' Make Command a dependency property so it can use databinding.
Public Shared ReadOnly CommandProperty As DependencyProperty =
    DependencyProperty.Register("Command", GetType(ICommand),
        GetType(CommandSlider),
        New PropertyMetadata(CType(Nothing, ICommand),
            New PropertyChangedCallback(AddressOf CommandChanged)))

Public ReadOnly Property Command1() As ICommand Implements ICommandSource.Command
    Get
        Return CType(GetValue(CommandProperty), ICommand)
    End Get
End Property

Public Property Command() As ICommand
    Get
        Return CType(GetValue(CommandProperty), ICommand)
    End Get
    Set(ByVal value As ICommand)
        SetValue(CommandProperty, value)
    End Set
End Property

다음은 DependencyProperty 변경 콜백입니다.

// Command dependency property change callback.
private static void CommandChanged(DependencyObject d,
    DependencyPropertyChangedEventArgs e)
{
    CommandSlider cs = (CommandSlider)d;
    cs.HookUpCommand((ICommand)e.OldValue,(ICommand)e.NewValue);
}
' Command dependency property change callback.
Private Shared Sub CommandChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    Dim cs As CommandSlider = CType(d, CommandSlider)
    cs.HookUpCommand(CType(e.OldValue, ICommand), CType(e.NewValue, ICommand))
End Sub

다음 단계는 명령 원본과 연결된 명령을 추가하고 제거하는 것입니다. 이전 명령과 연결된 이벤트 처리기가 있는 경우 먼저 제거해야 하므로 새 명령이 추가될 때 Command 속성을 덮어쓸 수 없습니다.

// Add a new command to the Command Property.
private void HookUpCommand(ICommand oldCommand, ICommand newCommand)
{
    // If oldCommand is not null, then we need to remove the handlers.
    if (oldCommand != null)
    {
        RemoveCommand(oldCommand, newCommand);
    }
    AddCommand(oldCommand, newCommand);
}

// Remove an old command from the Command Property.
private void RemoveCommand(ICommand oldCommand, ICommand newCommand)
{
    EventHandler handler = CanExecuteChanged;
    oldCommand.CanExecuteChanged -= handler;
}

// Add the command.
private void AddCommand(ICommand oldCommand, ICommand newCommand)
{
    EventHandler handler = new EventHandler(CanExecuteChanged);
    canExecuteChangedHandler = handler;
    if (newCommand != null)
    {
        newCommand.CanExecuteChanged += canExecuteChangedHandler;
    }
}
' Add a new command to the Command Property.
Private Sub HookUpCommand(ByVal oldCommand As ICommand, ByVal newCommand As ICommand)
    ' If oldCommand is not null, then we need to remove the handlers.
    If oldCommand IsNot Nothing Then
        RemoveCommand(oldCommand, newCommand)
    End If
    AddCommand(oldCommand, newCommand)
End Sub

' Remove an old command from the Command Property.
Private Sub RemoveCommand(ByVal oldCommand As ICommand, ByVal newCommand As ICommand)
    Dim handler As EventHandler = AddressOf CanExecuteChanged
    RemoveHandler oldCommand.CanExecuteChanged, handler
End Sub

' Add the command.
Private Sub AddCommand(ByVal oldCommand As ICommand, ByVal newCommand As ICommand)
    Dim handler As New EventHandler(AddressOf CanExecuteChanged)
    canExecuteChangedHandler = handler
    If newCommand IsNot Nothing Then
        AddHandler newCommand.CanExecuteChanged, canExecuteChangedHandler
    End If
End Sub

다음 단계는 CanExecuteChanged 처리기에 대한 논리를 만드는 것입니다.

CanExecuteChanged 이벤트는 현재 명령 대상에서 명령을 실행할 수 있는 기능이 변경되었을 수 있음을 명령 소스에 알립니다. 명령 원본이 이 이벤트를 수신하면 일반적으로 명령에서 CanExecute 메서드를 호출합니다. 명령이 현재 명령 대상에서 실행될 수 없는 경우 명령 원본은 일반적으로 그 자체를 비활성화합니다. 명령이 현재 명령 대상에서 실행될 수 있는 경우 명령 원본은 일반적으로 그 자체를 활성화합니다.

private void CanExecuteChanged(object sender, EventArgs e)
{

    if (this.Command != null)
    {
        RoutedCommand command = this.Command as RoutedCommand;

        // If a RoutedCommand.
        if (command != null)
        {
            if (command.CanExecute(CommandParameter, CommandTarget))
            {
                this.IsEnabled = true;
            }
            else
            {
                this.IsEnabled = false;
            }
        }
        // If a not RoutedCommand.
        else
        {
            if (Command.CanExecute(CommandParameter))
            {
                this.IsEnabled = true;
            }
            else
            {
                this.IsEnabled = false;
            }
        }
    }
}
Private Sub CanExecuteChanged(ByVal sender As Object, ByVal e As EventArgs)

    If Me.Command IsNot Nothing Then
        Dim command As RoutedCommand = TryCast(Me.Command, RoutedCommand)

        ' If a RoutedCommand.
        If command IsNot Nothing Then
            If command.CanExecute(CommandParameter, CommandTarget) Then
                Me.IsEnabled = True
            Else
                Me.IsEnabled = False
            End If
            ' If a not RoutedCommand.
        Else
            If Me.Command.CanExecute(CommandParameter) Then
                Me.IsEnabled = True
            Else
                Me.IsEnabled = False
            End If
        End If
    End If
End Sub

마지막 단계는 Execute 메서드입니다. 명령이 RoutedCommand이면 RoutedCommandExecute 메서드가 호출되고, 그렇지 않으면 ICommandExecute 메서드가 호출됩니다.

// If Command is defined, moving the slider will invoke the command;
// Otherwise, the slider will behave normally.
protected override void OnValueChanged(double oldValue, double newValue)
{
    base.OnValueChanged(oldValue, newValue);

    if (this.Command != null)
    {
        RoutedCommand command = Command as RoutedCommand;

        if (command != null)
        {
            command.Execute(CommandParameter, CommandTarget);
        }
        else
        {
            ((ICommand)Command).Execute(CommandParameter);
        }
    }
}
' If Command is defined, moving the slider will invoke the command;
' Otherwise, the slider will behave normally.
Protected Overrides Sub OnValueChanged(ByVal oldValue As Double, ByVal newValue As Double)
    MyBase.OnValueChanged(oldValue, newValue)

    If Me.Command IsNot Nothing Then
        Dim command As RoutedCommand = TryCast(Me.Command, RoutedCommand)

        If command IsNot Nothing Then
            command.Execute(CommandParameter, CommandTarget)
        Else
            CType(Me.Command, ICommand).Execute(CommandParameter)
        End If
    End If
End Sub

참고 항목