다음을 통해 공유


연습: MenuAction 만들기

업데이트: 2007년 11월

이 연습에서는 WPF(Windows Presentation Foundation) 사용자 지정 컨트롤에 대한 디자인 타임 메뉴 공급자를 만드는 방법을 보여 줍니다. 이 바로 가기 메뉴 항목을 사용하여 사용자 지정 단추 컨트롤의 Background 속성 값을 설정할 수 있습니다. 전체 코드는 방법: MenuAction 만들기를 참조하십시오.

이 연습에서는 다음 작업을 수행합니다.

  • WPF 사용자 지정 컨트롤 라이브러리 프로젝트를 만듭니다.

  • 디자인 타임 메타데이터를 위한 별도의 어셈블리를 만듭니다.

  • 메뉴 공급자를 구현합니다.

  • 디자인 타임에 컨트롤을 사용합니다.

이 연습을 마치면 사용자 지정 컨트롤에 대한 메뉴 공급자를 만드는 방법을 이해하게 됩니다.

참고:

실제 설정이나 버전에 따라서 화면에 나타나는 대화 상자와 메뉴 명령이 도움말의 설명과 다를 수 있습니다. 설정을 변경하려면 도구 메뉴에서 설정 가져오기 및 내보내기를 선택합니다. 자세한 내용은 Visual Studio 설정을 참조하십시오.

사전 요구 사항

이 연습을 완료하려면 다음 구성 요소가 필요합니다.

  • Visual Studio 2008.

사용자 지정 컨트롤 만들기

첫 번째 단계로 사용자 지정 컨트롤에 대한 프로젝트를 만듭니다. 이 컨트롤은 디자인 타임 코드가 약간 사용되는 간단한 단추이며, 이 코드에서는 GetIsInDesignMode 메서드를 사용하여 디자인 타임 동작을 구현합니다.

사용자 지정 컨트롤을 만들려면

  1. Visual Basic 또는 Visual C#에서 CustomControlLibrary라는 새 WPF 사용자 지정 컨트롤 라이브러리 프로젝트를 만듭니다.

    코드 편집기에 CustomControl1의 코드가 열립니다.

  2. 솔루션 탐색기에서 코드 파일의 이름을 ButtonWithDesignTime.cs 또는 ButtonWithDesignTime.vb로 변경합니다. 이 프로젝트의 모든 참조에서 이름을 바꿀지 묻는 메시지 상자가 표시되면 예를 클릭합니다.

  3. 솔루션 탐색기에서 테마 폴더를 확장합니다.

  4. Generic.xaml을 두 번 클릭합니다.

    WPF Designer에서 Generic.xaml이 열립니다.

  5. XAML 뷰에서 발견되는 모든 "CustomControl1"을 "ButtonWithDesignTime"으로 바꿉니다.

  6. 코드 편집기에서 ButtonWithDesignTime.cs 또는 ButtonWithDesignTime.vb를 엽니다.

  7. 자동으로 생성된 코드를 다음 코드로 바꿉니다. 이 코드는 Button에서 상속되며 디자이너에서 단추가 나타날 때 "Design mode active"라는 텍스트를 표시합니다.

    Imports System
    Imports System.Collections.Generic
    Imports System.Text
    Imports System.Windows.Controls
    Imports System.Windows.Media
    Imports System.ComponentModel
    
    Public Class ButtonWithDesignTime
        Inherits Button
    
        Public Sub New()
            ' The GetIsInDesignMode check and the following design-time 
            ' code are optional and shown only for demonstration.
            If DesignerProperties.GetIsInDesignMode(Me) Then
                Content = "Design mode active"
            End If
    
        End Sub
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.ComponentModel;
    
    namespace CustomControlLibrary
    {
        public class ButtonWithDesignTime : Button
        {
            public ButtonWithDesignTime()
            {
                // The GetIsInDesignMode check and the following design-time 
                // code are optional and shown only for demonstration.
                if (DesignerProperties.GetIsInDesignMode(this))
                {
                    Content = "Design mode active";
                }
            }
        }
    }
    
  8. 프로젝트의 출력 경로를 "bin\"으로 설정합니다.

  9. 솔루션을 빌드합니다.

디자인 타임 메타데이터 어셈블리 만들기

디자인 타임 코드는 특수 메타데이터 어셈블리에 배포됩니다. 이 연습에서는 상황에 맞는 메뉴 구현을 CustomControlLibrary.VisualStudio.Design이라는 어셈블리에 배포합니다.

디자인 타임 메타데이터 어셈블리를 만들려면

  1. Visual Basic 또는 Visual C#에서 CustomControlLibrary.VisualStudio.Design이라는 새 클래스 라이브러리 프로젝트를 솔루션에 추가합니다.

  2. 프로젝트의 출력 경로를 "..\CustomControlLibrary\bin\"으로 설정합니다. 이렇게 하면 컨트롤의 어셈블리와 메타데이터 어셈블리가 같은 폴더에 유지되므로 디자이너에서 메타데이터를 검색할 수 있습니다.

  3. 다음 WPF 어셈블리에 대한 참조를 추가합니다.

    • PresentationCore

    • PresentationFramework

    • WindowsBase

  4. 다음 WPF Designer 어셈블리에 대한 참조를 추가합니다.

    • Microsoft.Windows.Design

    • Microsoft.Windows.Design.Extensibility

    • Microsoft.Windows.Design.Interaction

  5. CustomControlLibrary 프로젝트에 대한 참조를 추가합니다.

  6. 솔루션 탐색기에서 Class1 코드 파일의 이름을 Metadata.cs 또는 Metadata.vb로 변경합니다. 이 프로젝트의 모든 참조에서 이름을 바꿀지 묻는 메시지 상자가 표시되면 예를 클릭합니다.

  7. 자동으로 생성된 코드를 다음 코드로 바꿉니다. 이 코드는 사용자 지정 디자인 타임 구현을 ButtonWithDesignTime 클래스에 연결하는 AttributeTable을 만듭니다.

    Imports System
    Imports System.Collections.Generic
    Imports System.Text
    Imports System.ComponentModel
    Imports System.Windows.Media
    Imports System.Windows.Controls
    Imports System.Windows
    Imports CustomControlLibrary
    Imports Microsoft.Windows.Design.Features
    Imports Microsoft.Windows.Design.Metadata
    Imports CustomControlLibrary.VisualStudio.Design.SliderAdornerLib
    
    ' Container for any general design-time metadata to initialize.
    ' Designers look for a type in the design-time assembly that 
    ' implements IRegisterMetadata. If found, designers instantiate 
    ' this class and call its Register() method automatically.
    Friend Class Metadata
        Implements IRegisterMetadata
    
        ' Called by the designer to register any design-time metadata.
        Public Sub Register() Implements IRegisterMetadata.Register
            Dim builder As New AttributeTableBuilder()
    
            ' Add the menu provider to the design-time metadata.
            builder.AddCustomAttributes(GetType(ButtonWithDesignTime), _
                                        New FeatureAttribute(GetType(CustomContextMenuProvider)))
    
            MetadataStore.AddAttributeTable(builder.CreateTable())
        End Sub
    
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.ComponentModel;
    using System.Windows.Media;
    using System.Windows.Controls;
    using System.Windows;
    
    using CustomControlLibrary;
    using Microsoft.Windows.Design.Features;
    using Microsoft.Windows.Design.Metadata;
    using SliderAdornerLib;
    
    namespace CiderPropertiesTester
    {
        // Container for any general design-time metadata to initialize.
        // Designers look for a type in the design-time assembly that 
        // implements IRegisterMetadata. If found, designers instantiate 
        // this class and call its Register() method automatically.
        internal class Metadata : IRegisterMetadata
        {
            // Called by the designer to register any design-time metadata.
            public void Register()
            {
                AttributeTableBuilder builder = new AttributeTableBuilder();
    
                // Add the menu provider to the design-time metadata.
                builder.AddCustomAttributes(
                    typeof(ButtonWithDesignTime), 
                    new FeatureAttribute(typeof(CustomContextMenuProvider)));
    
                MetadataStore.AddAttributeTable(builder.CreateTable());
            }
        }
    }
    
  8. 솔루션을 저장합니다.

메뉴 공급자 구현

메뉴 공급자는 CustomContextMenuProvider라는 형식으로 구현됩니다. 제공된 MenuAction을 통해 디자인 타임에 컨트롤의 Background 속성을 설정할 수 있습니다.

메뉴 공급자를 구현하려면

  1. CustomControlLibrary.VisualStudio.Design 프로젝트에 CustomContextMenuProvider라는 새 클래스를 추가합니다.

  2. CustomContextMenuProvider에 대한 코드 편집기에서 자동으로 생성된 코드를 다음 코드로 바꿉니다. 이 코드는 사용자 지정 MenuAction을 제공하는 PrimarySelectionContextMenuProvider를 구현합니다.

    Imports System
    Imports System.Collections.Generic
    Imports System.Text
    Imports Microsoft.Windows.Design.Interaction
    Imports System.Windows
    Imports Microsoft.Windows.Design.Model
    Imports System.Windows.Controls
    Imports System.Windows.Media
    
    ' The CustomContextMenuProvider class provides two context menu items
    ' at design time. These are implemented with the MenuAction class.
    Class CustomContextMenuProvider
        Inherits PrimarySelectionContextMenuProvider
    
        Private setBackgroundToBlueMenuAction As MenuAction
        Private clearBackgroundMenuAction As MenuAction
    
        ' The provider's constructor sets up the MenuAction objects 
        ' and the the MenuGroup which holds them.
        Public Sub New()
    
            ' Set up the MenuAction which sets the control's 
            ' background to Blue.
            setBackgroundToBlueMenuAction = New MenuAction("Blue")
            setBackgroundToBlueMenuAction.Checkable = True
            AddHandler setBackgroundToBlueMenuAction.Execute, AddressOf SetBackgroundToBlue_Execute
    
            ' Set up the MenuAction which sets the control's 
            ' background to its default value.
            clearBackgroundMenuAction = New MenuAction("Cleared")
            clearBackgroundMenuAction.Checkable = True
            AddHandler clearBackgroundMenuAction.Execute, AddressOf ClearBackground_Execute
    
            ' Set up the MenuGroup which holds the MenuAction items.
            Dim backgroundFlyoutGroup As New MenuGroup("SetBackgroundsGroup", "Set Background")
    
            ' If HasDropDown is false, the group appears inline, 
            ' instead of as a flyout. Set to true.
            backgroundFlyoutGroup.HasDropDown = True
            backgroundFlyoutGroup.Items.Add(setBackgroundToBlueMenuAction)
            backgroundFlyoutGroup.Items.Add(clearBackgroundMenuAction)
            Me.Items.Add(backgroundFlyoutGroup)
    
            ' The UpdateItemStatus event is raised immediately before 
            ' this provider shows its tabs, which provides the opportunity 
            ' to set states.
            AddHandler UpdateItemStatus, AddressOf CustomContextMenuProvider_UpdateItemStatus
    
        End Sub
    
        ' The following method handles the UpdateItemStatus event.
        ' It sets the MenuAction states according to the state
        ' of the control's Background property. This method is
        ' called before the context menu is shown.
        Sub CustomContextMenuProvider_UpdateItemStatus( _
            ByVal sender As Object, _
            ByVal e As MenuActionEventArgs)
    
            ' Turn everything on, and then based on the value 
            ' of the BackgroundProperty, selectively turn some off.
            clearBackgroundMenuAction.Checked = False
            clearBackgroundMenuAction.Enabled = True
            setBackgroundToBlueMenuAction.Checked = False
            setBackgroundToBlueMenuAction.Enabled = True
    
            ' Get a ModelItem which represents the selected control. 
            Dim selectedControl As ModelItem = _
                e.Selection.PrimarySelection
    
            ' Get the value of the Background property from the ModelItem.
            Dim backgroundProperty As ModelProperty = _
                selectedControl.Properties(Control.BackgroundProperty)
    
            ' Set the MenuAction items appropriately.
            If Not backgroundProperty.IsSet Then
                clearBackgroundMenuAction.Checked = True
                clearBackgroundMenuAction.Enabled = False
            ElseIf backgroundProperty.ComputedValue.Equals(Brushes.Blue) Then
                setBackgroundToBlueMenuAction.Checked = True
                setBackgroundToBlueMenuAction.Enabled = False
            End If
    
        End Sub
    
        ' The following method handles the Execute event. 
        ' It sets the Background property to its default value.
        Sub ClearBackground_Execute( _
            ByVal sender As Object, _
            ByVal e As MenuActionEventArgs)
    
            Dim selectedControl As ModelItem = e.Selection.PrimarySelection
            selectedControl.Properties(Control.BackgroundProperty).ClearValue()
    
        End Sub
    
        ' The following method handles the Execute event. 
        ' It sets the Background property to Brushes.Blue.
        Sub SetBackgroundToBlue_Execute( _
            ByVal sender As Object, _
            ByVal e As MenuActionEventArgs)
    
            Dim selectedControl As ModelItem = e.Selection.PrimarySelection
            selectedControl.Properties(Control.BackgroundProperty).SetValue(Brushes.Blue)
    
        End Sub
    
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.Windows.Design.Interaction;
    using System.Windows;
    using Microsoft.Windows.Design.Model;
    using System.Windows.Controls;
    using System.Windows.Media;
    
    namespace SliderAdornerLib
    {
        // The CustomContextMenuProvider class provides two context menu items
        // at design time. These are implemented with the MenuAction class.
        class CustomContextMenuProvider : PrimarySelectionContextMenuProvider
        {
            private MenuAction setBackgroundToBlueMenuAction;
            private MenuAction clearBackgroundMenuAction;
    
            // The provider's constructor sets up the MenuAction objects 
            // and the the MenuGroup which holds them.
            public CustomContextMenuProvider()
            {   
                // Set up the MenuAction which sets the control's 
                // background to Blue.
                setBackgroundToBlueMenuAction = new MenuAction("Blue");
                setBackgroundToBlueMenuAction.Checkable = true;
                setBackgroundToBlueMenuAction.Execute += 
                    new EventHandler<MenuActionEventArgs>(SetBackgroundToBlue_Execute);
    
                // Set up the MenuAction which sets the control's 
                // background to its default value.
                clearBackgroundMenuAction = new MenuAction("Cleared");
                clearBackgroundMenuAction.Checkable = true;
                clearBackgroundMenuAction.Execute += 
                    new EventHandler<MenuActionEventArgs>(ClearBackground_Execute);
    
                // Set up the MenuGroup which holds the MenuAction items.
                MenuGroup backgroundFlyoutGroup = 
                    new MenuGroup("SetBackgroundsGroup", "Set Background");
    
                // If HasDropDown is false, the group appears inline, 
                // instead of as a flyout. Set to true.
                backgroundFlyoutGroup.HasDropDown = true;
                backgroundFlyoutGroup.Items.Add(setBackgroundToBlueMenuAction);
                backgroundFlyoutGroup.Items.Add(clearBackgroundMenuAction);
                this.Items.Add(backgroundFlyoutGroup);
    
                // The UpdateItemStatus event is raised immediately before 
                // this provider shows its tabs, which provides the opportunity 
                // to set states.
                UpdateItemStatus += 
                    new EventHandler<MenuActionEventArgs>(
                        CustomContextMenuProvider_UpdateItemStatus);
            }
    
            // The following method handles the UpdateItemStatus event.
            // It sets the MenuAction states according to the state
            // of the control's Background property. This method is
            // called before the context menu is shown.
            void CustomContextMenuProvider_UpdateItemStatus(
                object sender, 
                MenuActionEventArgs e)
            {
                // Turn everything on, and then based on the value 
                // of the BackgroundProperty, selectively turn some off.
                clearBackgroundMenuAction.Checked = false;
                clearBackgroundMenuAction.Enabled = true;
                setBackgroundToBlueMenuAction.Checked = false;
                setBackgroundToBlueMenuAction.Enabled = true;
    
                // Get a ModelItem which represents the selected control. 
                ModelItem selectedControl = e.Selection.PrimarySelection;
    
                // Get the value of the Background property from the ModelItem.
                ModelProperty backgroundProperty = 
                    selectedControl.Properties[Control.BackgroundProperty];
    
                // Set the MenuAction items appropriately.
                if (!backgroundProperty.IsSet)
                {
                    clearBackgroundMenuAction.Checked = true;
                    clearBackgroundMenuAction.Enabled = false;
                }
                else if (backgroundProperty.ComputedValue == Brushes.Blue)
                {
                    setBackgroundToBlueMenuAction.Checked = true;
                    setBackgroundToBlueMenuAction.Enabled = false;
                }
            }
    
            // The following method handles the Execute event. 
            // It sets the Background property to its default value.
            void ClearBackground_Execute(
                object sender, 
                MenuActionEventArgs e)
            {
                ModelItem selectedControl = e.Selection.PrimarySelection;
                selectedControl.Properties[Control.BackgroundProperty].ClearValue();
            }
    
            // The following method handles the Execute event. 
            // It sets the Background property to Brushes.Blue.
            void SetBackgroundToBlue_Execute(
                object sender, 
                MenuActionEventArgs e)
            {
                ModelItem selectedControl = e.Selection.PrimarySelection;
                selectedControl.Properties[Control.BackgroundProperty].SetValue(Brushes.Blue);
            }
        }
    }
    
  3. 솔루션을 빌드합니다.

디자인 타임 구현 테스트

ButtonWithDesignTime 컨트롤을 다른 WPF 컨트롤과 같은 방식으로 사용할 수 있습니다. WPF Designer에서는 모든 디자인 타임 개체의 생성을 처리합니다.

디자인 타임 구현을 테스트하려면

  1. Visual Basic 또는 Visual C#에서 CustomControlLibrary.VisualStudio.Design이라는 WPF 응용 프로그램 프로젝트를 솔루션에 추가합니다.

    WPF Designer에 Window1.xaml이 열립니다.

  2. CustomControlLibrary 프로젝트에 대한 참조를 추가합니다.

  3. XAML 뷰에서 자동으로 생성된 XAML을 다음 XAML로 바꿉니다. 이 XAML은 CustomControlLibrary 네임스페이스에 대한 참조를 추가하고 ButtonWithDesignTime 사용자 지정 컨트롤을 추가합니다. 디자인 뷰에서 단추가 "Design mode active"라는 텍스트와 함께 표시되어 디자인 모드임을 나타냅니다. 단추가 표시되지 않는 경우 디자이너 위쪽의 정보 표시줄을 클릭하여 뷰를 다시 로드해야 할 수 있습니다.

    <Window x:Class="DemoApplication.Window1"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cc="clr-namespace:CustomControlLibrary;assembly=CustomControlLibrary"
        Title="Window1" Height="300" Width="300">
        <Grid>
            <cc:ButtonWithDesignTime Margin="30,30,30,30" Background="#FFD4D0C8"></cc:ButtonWithDesignTime>
        </Grid>
    </Window>
    
    <Window x:Class="DemoApplication.Window1"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cc="clr-namespace:CustomControlLibrary;assembly=CustomControlLibrary"
        Title="Window1" Height="300" Width="300">
        <Grid>
            <cc:ButtonWithDesignTime Margin="30,30,30,30" Background="#FFD4D0C8"></cc:ButtonWithDesignTime>
        </Grid>
    </Window>
    
  4. 디자인 뷰에서 ButtonWithDesignTime 컨트롤을 클릭하여 선택합니다.

  5. ButtonWithDesignTime 컨트롤을 마우스 오른쪽 단추로 클릭하고 Background 설정을 가리킨 다음 파랑을 선택합니다.

    컨트롤의 배경이 파랑으로 설정됩니다. XAML 뷰에서 Background 속성이 메뉴 작업에서 지정된 값으로 설정됩니다.

  6. DemoApplication 프로젝트를 실행합니다.

    바로 가기 메뉴에서 설정한 배경이 런타임에 단추에 적용됩니다.

다음 단계

사용자 지정 컨트롤에 사용자 지정 디자인 타임 기능을 더 추가할 수 있습니다.

참고 항목

작업

방법: MenuAction 만들기

참조

PrimarySelectionContextMenuProvider

기타 리소스

고급 확장성 개념

WPF Designer 확장성