다음을 통해 공유


방법: 디자인 모드에서 컨트롤의 모양과 동작 확장

사용자 지정 디자이너를 직접 만들어 디자인 타임 환경을 확장할 수 있습니다. 사용자 지정 디자이너에서는 컨트롤을 디자인하는 동안 컨트롤의 모양과 동작을 변경할 수 있습니다.

예제

다음 코드 예제에서는 사용자 지정 컨트롤을 디자인하기 위해 UI(사용자 인터페이스)를 확장하는 사용자 지정 디자이너를 만드는 방법을 보여 줍니다. DemoControlDesigner라는 디자이너 클래스를 DemoControl 클래스에 연결하여 다음 기능을 수행할 수 있습니다.

  • 새 DemoControl 인스턴스의 사용자 지정 초기화

  • 컨트롤의 MarginPadding 속성을 시각적으로 표시

  • 마우스 및 키보드를 사용한 Anchor 속성 설정

  • 스마트 태그 인터페이스를 통한 Anchor 속성 설정

참고

디자인 타임 어셈블리 System.Design.dll에 대한 참조를 추가해야 합니다. 이 어셈블리는 .NET Framework 4 Client Profile에 포함되지 않습니다. System.Design.dll에 대한 참조를 추가하려면 프로젝트의 대상 프레임워크를 .NET Framework 4로 변경해야 합니다.

Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing
Imports System.Drawing.Design
Imports System.Windows.Forms
Imports System.Windows.Forms.Design
Imports System.Windows.Forms.Design.Behavior

Public Class Form1
   Inherits Form
   Private demoControl1 As DemoControl
   Private demoControl2 As DemoControl

   Private components As System.ComponentModel.IContainer = Nothing

   Public Sub New()
      InitializeComponent()
    End Sub

   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposing AndAlso (components IsNot Nothing) Then
         components.Dispose()
      End If
      MyBase.Dispose(disposing)
    End Sub

   <STAThread()>  _
   Shared Sub Main()
      Application.EnableVisualStyles()
      Application.Run(New Form1())
    End Sub


   Private Sub InitializeComponent()
        Me.demoControl2 = New DemoControl
        Me.demoControl1 = New DemoControl
        Me.SuspendLayout()
        '
        'demoControl2
        '
        Me.demoControl2.Anchor = System.Windows.Forms.AnchorStyles.Left
        Me.demoControl2.BackColor = System.Drawing.Color.LightBlue
        Me.demoControl2.Location = New System.Drawing.Point(116, 119)
        Me.demoControl2.Margin = New System.Windows.Forms.Padding(20)
        Me.demoControl2.Name = "demoControl2"
        Me.demoControl2.Padding = New System.Windows.Forms.Padding(20)
        Me.demoControl2.Size = New System.Drawing.Size(135, 143)
        Me.demoControl2.TabIndex = 1
        '
        'demoControl1
        '
        Me.demoControl1.BackColor = System.Drawing.Color.LightBlue
        Me.demoControl1.Location = New System.Drawing.Point(282, 31)
        Me.demoControl1.Margin = New System.Windows.Forms.Padding(10)
        Me.demoControl1.Name = "demoControl1"
        Me.demoControl1.Padding = New System.Windows.Forms.Padding(10)
        Me.demoControl1.Size = New System.Drawing.Size(268, 251)
        Me.demoControl1.TabIndex = 0
        '
        'Form1
        '
        Me.ClientSize = New System.Drawing.Size(594, 352)
        Me.Controls.Add(Me.demoControl2)
        Me.Controls.Add(Me.demoControl1)
        Me.Name = "Form1"
        Me.Padding = New System.Windows.Forms.Padding(20)
        Me.Text = "a"
        Me.ResumeLayout(False)

    End Sub

    Private Sub demoControl1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)

    End Sub
End Class

' This control demonstrates the use of a custom designer.
<DesignerAttribute(GetType(DemoControlDesigner))>  _
Public Class DemoControl
   Inherits UserControl
   Private components As System.ComponentModel.IContainer = Nothing


   Public Sub New()
      InitializeComponent()
    End Sub


   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposing AndAlso (components IsNot Nothing) Then
         components.Dispose()
      End If
      MyBase.Dispose(disposing)
    End Sub


   Private Sub InitializeComponent()
    End Sub
End Class


' This class demonstrates how to build a custom designer.
' When an instance of the associated control type is created
' in a design environment like Visual Studio, this designer
' provides custom design-time behavior.
'
' When you drop an instance of DemoControl onto a form,
' this designer creates two adorner windows: one is used
' for glyphs that represent the Margin and Padding properties
' of the control, and the other is used for glyphs that
' represent the Anchor property.
'
' The AnchorGlyph type defines an AnchorBehavior type that
' allows you to change the value of the Anchor property 
' by double-clicking on an AnchorGlyph.
'
' This designer also offers a smart tag for changing the 
' Anchor property.
<System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
Public Class DemoControlDesigner
    Inherits ControlDesigner

    ' This adorner holds the glyphs that represent the Anchor property.
    Private anchorAdorner As Adorner = Nothing

    ' This adorner holds the glyphs that represent the Margin and
    ' Padding properties.
    Private marginAndPaddingAdorner As Adorner = Nothing

    ' This defines the size of the anchor glyphs.
    Private Const glyphSize As Integer = 6

    ' This defines the size of the hit bounds for an AnchorGlyph.
    Private Const hitBoundSize As Integer = glyphSize + 4

    ' References to designer services, for convenience.
    Private changeService As IComponentChangeService = Nothing
    Private selectionService As ISelectionService = Nothing
    Private behaviorSvc As BehaviorService = Nothing

    ' This is the collection of DesignerActionLists that
    ' defines the smart tags offered on the control. 
    Private actionListColl As DesignerActionListCollection = Nothing


    Public Sub New()
    End Sub


    Protected Overrides Sub Dispose(ByVal disposing As Boolean)

        If disposing Then
            If (Me.behaviorSvc IsNot Nothing) Then
                ' Remove the adorners added by this designer from
                ' the BehaviorService.Adorners collection.
                Me.behaviorSvc.Adorners.Remove(Me.marginAndPaddingAdorner)
                Me.behaviorSvc.Adorners.Remove(Me.anchorAdorner)
            End If
        End If

        MyBase.Dispose(disposing)

    End Sub


    ' This method is where the designer initializes its state when
    ' it is created.
    Public Overrides Sub Initialize(ByVal component As IComponent)
        MyBase.Initialize(component)

        ' Connect to various designer services.
        InitializeServices()

        ' Initialize adorners.
        Me.InitializeMarginAndPaddingAdorner()
        Me.InitializeAnchorAdorner()
    End Sub

    ' This demonstrates changing the appearance of a control while
    ' it is being designed. In this case, the BackColor property is
    ' set to LightBlue. 
    Public Overrides Sub InitializeNewComponent( _
    ByVal defaultValues As IDictionary)

        MyBase.InitializeNewComponent(defaultValues)

        Dim colorPropDesc As PropertyDescriptor = _
        TypeDescriptor.GetProperties(Component)("BackColor")

        If colorPropDesc IsNot Nothing AndAlso _
           colorPropDesc.PropertyType Is GetType(Color) AndAlso _
           Not colorPropDesc.IsReadOnly AndAlso _
           colorPropDesc.IsBrowsable Then
            colorPropDesc.SetValue(Component, Color.LightBlue)
        End If
    End Sub

    ' This utility method creates an adorner for the anchor glyphs.
    ' It then creates four AnchorGlyph objects and adds them to 
    ' the adorner's Glyphs collection.
    Private Sub InitializeAnchorAdorner()
        Me.anchorAdorner = New Adorner()
        Me.behaviorSvc.Adorners.Add(Me.anchorAdorner)

        Me.anchorAdorner.Glyphs.Add(New AnchorGlyph( _
        AnchorStyles.Left, _
        Me.behaviorSvc, _
        Me.changeService, _
        Me.selectionService, _
        Me, _
        Me.anchorAdorner))

        Me.anchorAdorner.Glyphs.Add(New AnchorGlyph( _
        AnchorStyles.Top, _
        Me.behaviorSvc, _
        Me.changeService, _
        Me.selectionService, _
        Me, _
        Me.anchorAdorner))

        Me.anchorAdorner.Glyphs.Add(New AnchorGlyph( _
        AnchorStyles.Right, _
        Me.behaviorSvc, _
        Me.changeService, _
        Me.selectionService, _
        Me, _
        Me.anchorAdorner))

        Me.anchorAdorner.Glyphs.Add(New AnchorGlyph( _
        AnchorStyles.Bottom, _
        Me.behaviorSvc, _
        Me.changeService, _
        Me.selectionService, _
        Me, _
        Me.anchorAdorner))

    End Sub

    ' This utility method creates an adorner for the margin and 
    ' padding glyphs. It then creates a MarginAndPaddingGlyph and 
    ' adds it to the adorner's Glyphs collection.
    Private Sub InitializeMarginAndPaddingAdorner()

        Me.marginAndPaddingAdorner = New Adorner()
        Me.behaviorSvc.Adorners.Add(Me.marginAndPaddingAdorner)

        Me.marginAndPaddingAdorner.Glyphs.Add(New MarginAndPaddingGlyph( _
        Me.behaviorSvc, _
        Me.changeService, _
        Me.selectionService, _
        Me, _
        Me.marginAndPaddingAdorner))

    End Sub

    ' This utility method connects the designer to various services.
    ' These references are cached for convenience.
    Private Sub InitializeServices()

        ' Acquire a reference to IComponentChangeService.
        Me.changeService = GetService(GetType(IComponentChangeService))

        ' Acquire a reference to ISelectionService.
        Me.selectionService = GetService(GetType(ISelectionService))

        ' Acquire a reference to BehaviorService.
        Me.behaviorSvc = GetService(GetType(BehaviorService))

    End Sub

    ' This method creates the DesignerActionList on demand, causing
    ' smart tags to appear on the control being designed.

    Public Overrides ReadOnly Property ActionLists() As _
    DesignerActionListCollection

        Get
            If actionListColl Is Nothing Then
                actionListColl = New DesignerActionListCollection()
                actionListColl.Add(New AnchorActionList(Me.Component))
            End If

            Return actionListColl
        End Get

    End Property

    ' This class defines the smart tags that appear on the control
    ' being designed. In this case, the Anchor property appears
    ' on the smart tag and its value can be changed through a 
    ' UI Type Editor created automatically by the 
    ' DesignerActionService.

    Public Class AnchorActionList
        Inherits System.ComponentModel.Design.DesignerActionList

        ' Cache a reference to the control.
        Private relatedControl As DemoControl

        'The constructor associates the control 
        'with the smart tag list.
        Public Sub New(ByVal component As IComponent)

            MyBase.New(component)
            Me.relatedControl = component

        End Sub

        ' Properties that are targets of 
        ' DesignerActionPropertyItem entries.
        Public Property Anchor() As AnchorStyles
            Get
                Return Me.relatedControl.Anchor
            End Get
            Set(ByVal value As AnchorStyles)
                Dim pdAnchor As PropertyDescriptor = _
                TypeDescriptor.GetProperties(Me.relatedControl)("Anchor")
                pdAnchor.SetValue(Me.relatedControl, Value)
            End Set
        End Property

        ' This method creates and populates the 
        ' DesignerActionItemCollection which is used to 
        ' display smart tag items.
        Public Overrides Function GetSortedActionItems() As _
        DesignerActionItemCollection

            Dim items As New DesignerActionItemCollection()

            ' Add a descriptive header.
            items.Add(New DesignerActionHeaderItem("Anchor Styles"))

            ' Add a DesignerActionPropertyItem for the Anchor
            ' property. This will be displayed in a panel using
            ' the AnchorStyles UI Type Editor.
            items.Add(New DesignerActionPropertyItem( _
            "Anchor", _
            "Anchor Style"))

            Return items
        End Function
    End Class


#Region "Glyph Implementations"

    ' This class implements a MarginAndPaddingGlyph, which draws 
    ' borders highlighting the value of the control's Margin 
    ' property and the value of the control's Padding property.
    '
    ' This glyph has no mouse or keyboard interaction, so its
    ' related behavior class, MarginAndPaddingBehavior, has no
    ' implementation.

    Public Class MarginAndPaddingGlyph
        Inherits Glyph
        Private behaviorService As BehaviorService = Nothing
        Private changeService As IComponentChangeService = Nothing
        Private selectionService As ISelectionService = Nothing
        Private relatedDesigner As IDesigner = Nothing
        Private marginAndPaddingAdorner As Adorner = Nothing
        Private relatedControl As Control = Nothing


        Public Sub New( _
        ByVal behaviorService As BehaviorService, _
        ByVal changeService As IComponentChangeService, _
        ByVal selectionService As ISelectionService, _
        ByVal relatedDesigner As IDesigner, _
        ByVal marginAndPaddingAdorner As Adorner)

            MyBase.New(New MarginAndPaddingBehavior())
            Me.behaviorService = behaviorService
            Me.changeService = changeService
            Me.selectionService = selectionService
            Me.relatedDesigner = relatedDesigner
            Me.marginAndPaddingAdorner = marginAndPaddingAdorner

            Me.relatedControl = Me.relatedDesigner.Component

            AddHandler Me.changeService.ComponentChanged, _
            AddressOf changeService_ComponentChanged

        End Sub

        Private Sub changeService_ComponentChanged( _
        ByVal sender As Object, _
        ByVal e As ComponentChangedEventArgs)

            If Object.ReferenceEquals( _
            e.Component, _
            Me.relatedControl) Then
                If e.Member.Name = "Margin" OrElse _
                   e.Member.Name = "Padding" Then
                    Me.marginAndPaddingAdorner.Invalidate()
                End If
            End If

        End Sub

        ' This glyph has no mouse or keyboard interaction, so 
        ' GetHitTest can return null.
        Public Overrides Function GetHitTest( _
        ByVal p As Point) As Cursor
            Return Nothing
        End Function

        ' This method renders the glyph as a simple focus rectangle.
        Public Overrides Sub Paint(ByVal e As PaintEventArgs)
            ControlPaint.DrawFocusRectangle(e.Graphics, Me.Bounds)

            ControlPaint.DrawFocusRectangle( _
            e.Graphics, _
            Me.PaddingBounds)
        End Sub

        ' This glyph's Bounds property is a Rectangle defined by 
        ' the value of the control's Margin property.

        Public Overrides ReadOnly Property Bounds() As Rectangle
            Get
                Dim c As Control = Me.relatedControl
                Dim controlRect As Rectangle = _
                Me.behaviorService.ControlRectInAdornerWindow( _
                Me.relatedControl)

                Dim boundsVal As New Rectangle( _
                controlRect.Left - c.Margin.Left, _
                controlRect.Top - c.Margin.Top, _
                controlRect.Width + c.Margin.Right * 2, _
                controlRect.Height + c.Margin.Bottom * 2)

                Return boundsVal

            End Get
        End Property

        ' The PaddingBounds property is a Rectangle defined by 
        ' the value of the control's Padding property.

        Public ReadOnly Property PaddingBounds() As Rectangle
            Get
                Dim c As Control = Me.relatedControl
                Dim controlRect As Rectangle = _
                Me.behaviorService.ControlRectInAdornerWindow( _
                Me.relatedControl)

                Dim boundsVal As New Rectangle( _
                controlRect.Left + c.Padding.Left, _
                controlRect.Top + c.Padding.Top, _
                controlRect.Width - c.Padding.Right * 2, _
                controlRect.Height - c.Padding.Bottom * 2)

                Return boundsVal

            End Get
        End Property

        ' There are no keyboard or mouse behaviors associated with 
        ' this glyph, but you could add them to this class.

        Friend Class MarginAndPaddingBehavior
            Inherits System.Windows.Forms.Design.Behavior.Behavior
        End Class

    End Class

    ' This class implements an AnchorGlyph, which draws grab handles
    ' that represent the value of the control's Anchor property.
    '
    ' This glyph has mouse and keyboard interactions, which are
    ' handled by the related behavior class, AnchorBehavior.
    ' Double-clicking on an AnchorGlyph causes its value to be 
    ' toggled between enabled and disable states. 

    Public Class AnchorGlyph
        Inherits Glyph

        ' This defines the bounds of the anchor glyph.
        Protected boundsValue As Rectangle

        ' This defines the bounds used for hit testing.
        ' These bounds are typically different than the bounds 
        ' of the glyph itself.
        Protected hitBoundsValue As Rectangle

        ' This is the cursor returned if hit test is positive.
        Protected hitTestCursor As Cursor = Cursors.Hand

        ' Cache references to services that will be needed.
        Private behaviorService As BehaviorService = Nothing
        Private changeService As IComponentChangeService = Nothing
        Private selectionService As ISelectionService = Nothing

        ' Keep a reference to the designer for convenience.
        Private relatedDesigner As IDesigner = Nothing

        ' Keep a reference to the adorner for convenience.
        Private anchorAdorner As Adorner = Nothing

        ' Keep a reference to the control being designed.
        Private relatedControl As Control = Nothing

        ' This defines the AnchorStyle which this glyph represents.
        Private anchorStyle As AnchorStyles


        Public Sub New( _
        ByVal anchorStyle As AnchorStyles, _
        ByVal behaviorService As BehaviorService, _
        ByVal changeService As IComponentChangeService, _
        ByVal selectionService As ISelectionService, _
        ByVal relatedDesigner As IDesigner, _
        ByVal anchorAdorner As Adorner)

            MyBase.New(New AnchorBehavior(relatedDesigner))
            ' Cache references for convenience.
            Me.anchorStyle = anchorStyle
            Me.behaviorService = behaviorService
            Me.changeService = changeService
            Me.selectionService = selectionService
            Me.relatedDesigner = relatedDesigner
            Me.anchorAdorner = anchorAdorner

            ' Cache a reference to the control being designed.
            Me.relatedControl = Me.relatedDesigner.Component

            ' Hook the SelectionChanged event. 
            AddHandler Me.selectionService.SelectionChanged, _
            AddressOf selectionService_SelectionChanged

            ' Hook the ComponentChanged event so the anchor glyphs
            ' can correctly track the control's bounds.
            AddHandler Me.changeService.ComponentChanged, _
            AddressOf changeService_ComponentChanged

        End Sub

#Region "Overrides"

        Public Overrides ReadOnly Property Bounds() As Rectangle
            Get
                Return Me.boundsValue
            End Get
        End Property


        ' This method renders the AnchorGlyph as a filled rectangle
        ' if the glyph is enabled, or as an open rectangle if the
        ' glyph is disabled.
        Public Overrides Sub Paint(ByVal e As PaintEventArgs)
            If Me.IsEnabled Then
                Dim b As New SolidBrush(Color.Tomato)
                Try
                    e.Graphics.FillRectangle(b, Me.Bounds)
                Finally
                    b.Dispose()
                End Try
            Else
                Dim p As New Pen(Color.Tomato)
                Try
                    e.Graphics.DrawRectangle(p, Me.Bounds)
                Finally
                    p.Dispose()
                End Try
            End If
        End Sub


        ' An AnchorGlyph has keyboard and mouse interaction, so it's
        ' important to return a cursor when the mouse is located in 
        ' the glyph's hit region. When this occurs, the 
        ' AnchorBehavior becomes active.
        Public Overrides Function GetHitTest( _
        ByVal p As Point) As Cursor

            If hitBoundsValue.Contains(p) Then
                Return hitTestCursor
            End If

            Return Nothing

        End Function

#End Region

#Region "Event Handlers"


        ' The AnchorGlyph objects should mimic the resize glyphs;
        ' they should only be visible when the control is the 
        ' primary selection. The adorner is enabled when the 
        ' control is the primary selection and disabled when 
        ' it is not.
        Private Sub selectionService_SelectionChanged( _
        ByVal sender As Object, _
        ByVal e As EventArgs)

            If Object.ReferenceEquals( _
            Me.selectionService.PrimarySelection, _
            Me.relatedControl) Then
                Me.ComputeBounds()
                Me.anchorAdorner.Enabled = True
            Else
                Me.anchorAdorner.Enabled = False
            End If

        End Sub

        ' If any of several properties change, the bounds of the 
        ' AnchorGlyph must be computed again.
        Private Sub changeService_ComponentChanged( _
        ByVal sender As Object, _
        ByVal e As ComponentChangedEventArgs)

            If Object.ReferenceEquals( _
            e.Component, Me.relatedControl) Then
                If e.Member.Name = "Anchor" OrElse _
                   e.Member.Name = "Size" OrElse _
                   e.Member.Name = "Height" OrElse _
                   e.Member.Name = "Width" OrElse _
                   e.Member.Name = "Location" Then

                    ' Compute the bounds of this glyph.
                    Me.ComputeBounds()

                    ' Tell the adorner to repaint itself.
                    Me.anchorAdorner.Invalidate()

                End If
            End If
        End Sub

#End Region

#Region "Implementation"


        ' This utility method computes the position and size of 
        ' the AnchorGlyph in the Adorner window's coordinates.
        ' It also computes the hit test bounds, which are
        ' slightly larger than the glyph's bounds.
        Private Sub ComputeBounds()
            Dim translatedBounds As New Rectangle( _
            Me.behaviorService.ControlToAdornerWindow(Me.relatedControl), _
            Me.relatedControl.Size)

            If (Me.anchorStyle And AnchorStyles.Top) = AnchorStyles.Top Then
                Me.boundsValue = New Rectangle( _
                translatedBounds.X + translatedBounds.Width / 2 - glyphSize / 2, _
                translatedBounds.Y + glyphSize, _
                glyphSize, _
                glyphSize)
            End If

            If (Me.anchorStyle And AnchorStyles.Bottom) = AnchorStyles.Bottom Then
                Me.boundsValue = New Rectangle( _
                translatedBounds.X + translatedBounds.Width / 2 - glyphSize / 2, _
                translatedBounds.Bottom - 2 * glyphSize, _
                glyphSize, _
                glyphSize)
            End If

            If (Me.anchorStyle And AnchorStyles.Left) = AnchorStyles.Left Then
                Me.boundsValue = New Rectangle( _
                translatedBounds.X + glyphSize, _
                translatedBounds.Y + translatedBounds.Height / 2 - glyphSize / 2, _
                glyphSize, _
                glyphSize)
            End If

            If (Me.anchorStyle And AnchorStyles.Right) = AnchorStyles.Right Then
                Me.boundsValue = New Rectangle( _
                translatedBounds.Right - 2 * glyphSize, _
                translatedBounds.Y + translatedBounds.Height / 2 - glyphSize / 2, _
                glyphSize, _
                glyphSize)
            End If

            Me.hitBoundsValue = New Rectangle( _
            Me.Bounds.Left - hitBoundSize / 2, _
            Me.Bounds.Top - hitBoundSize / 2, _
            hitBoundSize, _
            hitBoundSize)

        End Sub

        ' This utility property determines if the AnchorGlyph is 
        ' enabled, according to the value specified by the 
        ' control's Anchor property.

        Private ReadOnly Property IsEnabled() As Boolean
            Get
                Return (Me.anchorStyle And Me.relatedControl.Anchor) = Me.anchorStyle
            End Get
        End Property

#End Region



#Region "Behavior Implementation"

        ' This Behavior specifies mouse and keyboard handling when
        ' an AnchorGlyph is active. This happens when 
        ' AnchorGlyph.GetHitTest returns a non-null value.

        Friend Class AnchorBehavior
            Inherits System.Windows.Forms.Design.Behavior.Behavior
            Private relatedDesigner As IDesigner = Nothing
            Private relatedControl As Control = Nothing


            Friend Sub New(ByVal relatedDesigner As IDesigner)
                Me.relatedDesigner = relatedDesigner
                Me.relatedControl = relatedDesigner.Component
            End Sub

            ' When you double-click on an AnchorGlyph, the value of 
            ' the control's Anchor property is toggled.
            '
            ' Note that the value of the Anchor property is not set
            ' by direct assignment. Instead, the 
            ' PropertyDescriptor.SetValue method is used. This 
            ' enables notification of the design environment, so 
            ' related events can be raised, for example, the
            ' IComponentChangeService.ComponentChanged event.
            Public Overrides Function OnMouseDoubleClick( _
            ByVal g As Glyph, _
            ByVal button As MouseButtons, _
            ByVal mouseLoc As Point) As Boolean

                MyBase.OnMouseDoubleClick(g, button, mouseLoc)

                If button = MouseButtons.Left Then
                    Dim ag As AnchorGlyph = g

                    Dim pdAnchor As PropertyDescriptor = _
                    TypeDescriptor.GetProperties(ag.relatedControl)("Anchor")

                    If ag.IsEnabled Then
                        ' The glyph is enabled. 
                        ' Clear the AnchorStyle flag to disable the Glyph.
                        pdAnchor.SetValue(ag.relatedControl, _
                        ag.relatedControl.Anchor Xor ag.anchorStyle)
                    Else
                        ' The glyph is disabled. 
                        ' Set the AnchorStyle flag to enable the Glyph.
                        pdAnchor.SetValue(ag.relatedControl, _
                        ag.relatedControl.Anchor Or ag.anchorStyle)
                    End If
                End If

                Return True

            End Function

        End Class

#End Region

    End Class

#End Region
End Class

using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Windows.Forms.Design.Behavior;

public class Form1 : Form
{
    private DemoControl demoControl1;
    private DemoControl demoControl2;

    private System.ComponentModel.IContainer components = null;

    public Form1()
    {
        InitializeComponent();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.Run(new Form1());
    }

    private void InitializeComponent()
    {
        this.demoControl2 = new DemoControl();
        this.demoControl1 = new DemoControl();
        this.SuspendLayout();
        // 
        // demoControl2
        // 
        this.demoControl2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                    | System.Windows.Forms.AnchorStyles.Left)
                    | System.Windows.Forms.AnchorStyles.Right)));
        this.demoControl2.BackColor = System.Drawing.Color.LightBlue;
        this.demoControl2.Location = new System.Drawing.Point(40, 40);
        this.demoControl2.Margin = new System.Windows.Forms.Padding(20);
        this.demoControl2.Name = "demoControl2";
        this.demoControl2.Padding = new System.Windows.Forms.Padding(20);
        this.demoControl2.Size = new System.Drawing.Size(284, 177);
        this.demoControl2.TabIndex = 1;
        // 
        // demoControl1
        // 
        this.demoControl1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                    | System.Windows.Forms.AnchorStyles.Left)));
        this.demoControl1.BackColor = System.Drawing.Color.LightBlue;
        this.demoControl1.Location = new System.Drawing.Point(354, 21);
        this.demoControl1.Margin = new System.Windows.Forms.Padding(10);
        this.demoControl1.Name = "demoControl1";
        this.demoControl1.Padding = new System.Windows.Forms.Padding(10);
        this.demoControl1.Size = new System.Drawing.Size(184, 207);
        this.demoControl1.TabIndex = 0;
        // 
        // Form1
        // 
        this.ClientSize = new System.Drawing.Size(594, 352);
        this.Controls.Add(this.demoControl2);
        this.Controls.Add(this.demoControl1);
        this.Name = "Form1";
        this.Padding = new System.Windows.Forms.Padding(20);
        this.Text = "a";
        this.ResumeLayout(false);

    } 
}

// This control demonstrates the use of a custom designer.
[DesignerAttribute(typeof(DemoControlDesigner))]
public class DemoControl : UserControl
{   
    private System.ComponentModel.IContainer components = null;

    public DemoControl()
    {
        InitializeComponent();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
        // 
        // DemoControl
        // 
        this.Name = "DemoControl";

    }
}

// This class demonstrates how to build a custom designer.
// When an instance of the associated control type is created
// in a design environment like Visual Studio, this designer
// provides custom design-time behavior.
//
// When you drop an instance of DemoControl onto a form,
// this designer creates two adorner windows: one is used
// for glyphs that represent the Margin and Padding properties
// of the control, and the other is used for glyphs that
// represent the Anchor property.
//
// The AnchorGlyph type defines an AnchorBehavior type that
// allows you to change the value of the Anchor property 
// by double-clicking on an AnchorGlyph.
//
// This designer also offers a smart tag for changing the 
// Anchor property.
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] 
public class DemoControlDesigner : ControlDesigner
{
    // This adorner holds the glyphs that represent the Anchor property.
    private Adorner anchorAdorner = null;

    // This adorner holds the glyphs that represent the Margin and
    // Padding properties.
    private Adorner marginAndPaddingAdorner = null;

    // This defines the size of the anchor glyphs.
    private const int glyphSize = 6;

    // This defines the size of the hit bounds for an AnchorGlyph.
    private const int hitBoundSize = glyphSize + 4;

    // References to designer services, for convenience.
    private IComponentChangeService changeService = null;
    private ISelectionService selectionService = null;
    private BehaviorService behaviorSvc = null;

    // This is the collection of DesignerActionLists that
    // defines the smart tags offered on the control. 
    private DesignerActionListCollection actionLists = null;

    public DemoControlDesigner()
    {   
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (this.behaviorSvc != null)
            {   
                // Remove the adorners added by this designer from
                // the BehaviorService.Adorners collection.
                this.behaviorSvc.Adorners.Remove(this.marginAndPaddingAdorner);
                this.behaviorSvc.Adorners.Remove(this.anchorAdorner);
            }
        }

        base.Dispose(disposing);
    }

    // This method is where the designer initializes its state when
    // it is created.
    public override void Initialize(IComponent component)
    {
        base.Initialize(component);

        // Connect to various designer services.
        InitializeServices();

        // Initialize adorners.
        this.InitializeMarginAndPaddingAdorner();
        this.InitializeAnchorAdorner();

    }

    // This demonstrates changing the appearance of a control while
    // it is being designed. In this case, the BackColor property is
    // set to LightBlue. 

    public override void InitializeNewComponent(IDictionary defaultValues)
    {
        base.InitializeNewComponent(defaultValues);

        PropertyDescriptor colorPropDesc = 
            TypeDescriptor.GetProperties(Component)["BackColor"];

        if (colorPropDesc != null &&
            colorPropDesc.PropertyType == typeof(Color) &&
            !colorPropDesc.IsReadOnly &&
            colorPropDesc.IsBrowsable)
        {
            colorPropDesc.SetValue(Component, Color.LightBlue);
        }
    }

    // This utility method creates an adorner for the anchor glyphs.
    // It then creates four AnchorGlyph objects and adds them to 
    // the adorner's Glyphs collection.
    private void InitializeAnchorAdorner()
    {
        this.anchorAdorner = new Adorner();
        this.behaviorSvc.Adorners.Add(this.anchorAdorner);

        this.anchorAdorner.Glyphs.Add(new AnchorGlyph(
            AnchorStyles.Left,
            this.behaviorSvc,
            this.changeService,
            this.selectionService,
            this,
            this.anchorAdorner)
            );

        this.anchorAdorner.Glyphs.Add(new AnchorGlyph(
            AnchorStyles.Top,
            this.behaviorSvc,
            this.changeService,
            this.selectionService,
            this,
            this.anchorAdorner)
            );

        this.anchorAdorner.Glyphs.Add(new AnchorGlyph(
            AnchorStyles.Right,
            this.behaviorSvc,
            this.changeService,
            this.selectionService,
            this,
            this.anchorAdorner)
            );

        this.anchorAdorner.Glyphs.Add(new AnchorGlyph(
            AnchorStyles.Bottom,
            this.behaviorSvc,
            this.changeService,
            this.selectionService,
            this,
            this.anchorAdorner)
            );
    }

    // This utility method creates an adorner for the margin and 
    // padding glyphs. It then creates a MarginAndPaddingGlyph and 
    // adds it to the adorner's Glyphs collection.
    private void InitializeMarginAndPaddingAdorner()
    {
        this.marginAndPaddingAdorner = new Adorner();
        this.behaviorSvc.Adorners.Add(this.marginAndPaddingAdorner);

        this.marginAndPaddingAdorner.Glyphs.Add(new MarginAndPaddingGlyph(
            this.behaviorSvc,
            this.changeService,
            this.selectionService,
            this,
            this.marginAndPaddingAdorner));
    }

    // This utility method connects the designer to various services.
    // These references are cached for convenience.
    private void InitializeServices()
    {
        // Acquire a reference to IComponentChangeService.
        this.changeService = 
            GetService(typeof(IComponentChangeService)) 
            as IComponentChangeService;

        // Acquire a reference to ISelectionService.
        this.selectionService =
            GetService(typeof(ISelectionService))
            as ISelectionService;

        // Acquire a reference to BehaviorService.
        this.behaviorSvc =
            GetService(typeof(BehaviorService))
            as BehaviorService;
    }

    // This method creates the DesignerActionList on demand, causing
    // smart tags to appear on the control being designed.
    public override DesignerActionListCollection ActionLists
    {
        get
        {
            if (null == actionLists)
            {
                actionLists = new DesignerActionListCollection();
                actionLists.Add(
                    new AnchorActionList(this.Component));
            }

            return actionLists;
        }
    }

    // This class defines the smart tags that appear on the control
    // being designed. In this case, the Anchor property appears
    // on the smart tag and its value can be changed through a 
    // UI Type Editor created automatically by the 
    // DesignerActionService.
    public class AnchorActionList :
          System.ComponentModel.Design.DesignerActionList
    {   
        // Cache a reference to the control.
        private DemoControl relatedControl;

        //The constructor associates the control 
        //with the smart tag list.
        public AnchorActionList(IComponent component): base(component)
        {
            this.relatedControl = component as DemoControl;
        }

        // Properties that are targets of DesignerActionPropertyItem entries.
        public AnchorStyles Anchor
        {
            get
            {
                return this.relatedControl.Anchor;
            }
            set
            {
                PropertyDescriptor pdAnchor = TypeDescriptor.GetProperties(this.relatedControl)["Anchor"];
                pdAnchor.SetValue(this.relatedControl, value);
            }
        }

        // This method creates and populates the 
        // DesignerActionItemCollection which is used to 
        // display smart tag items.
        public override DesignerActionItemCollection GetSortedActionItems()
        {
            DesignerActionItemCollection items = 
                new DesignerActionItemCollection();

            // Add a descriptive header.
            items.Add(new DesignerActionHeaderItem("Anchor Styles"));

            // Add a DesignerActionPropertyItem for the Anchor
            // property. This will be displayed in a panel using
            // the AnchorStyles UI Type Editor.
            items.Add(new DesignerActionPropertyItem(
                "Anchor", 
                "Anchor Style") );

            return items;
        }
    }


    #region Glyph Implementations

    // This class implements a MarginAndPaddingGlyph, which draws 
    // borders highlighting the value of the control's Margin 
    // property and the value of the control's Padding property.
    //
    // This glyph has no mouse or keyboard interaction, so its
    // related behavior class, MarginAndPaddingBehavior, has no
    // implementation.

    public class MarginAndPaddingGlyph : Glyph
    {
        private BehaviorService behaviorService = null;
        private IComponentChangeService changeService = null;
        private ISelectionService selectionService = null;
        private IDesigner relatedDesigner = null;
        private Adorner marginAndPaddingAdorner = null;
        private Control relatedControl = null;

        public MarginAndPaddingGlyph(
            BehaviorService behaviorService,
            IComponentChangeService changeService,
            ISelectionService selectionService,
            IDesigner relatedDesigner,
            Adorner marginAndPaddingAdorner)
            : base(new MarginAndPaddingBehavior())
        {
            this.behaviorService = behaviorService;
            this.changeService = changeService;
            this.selectionService = selectionService;
            this.relatedDesigner = relatedDesigner;
            this.marginAndPaddingAdorner = marginAndPaddingAdorner;

            this.relatedControl = 
                this.relatedDesigner.Component as Control;

            this.changeService.ComponentChanged += new ComponentChangedEventHandler(changeService_ComponentChanged);
        }

        void changeService_ComponentChanged(object sender, ComponentChangedEventArgs e)
        {
            if (object.ReferenceEquals(
                e.Component, 
                this.relatedControl))
            {
                if (e.Member.Name == "Margin" ||
                    e.Member.Name == "Padding" )
                {
                    this.marginAndPaddingAdorner.Invalidate();
                }
            }
        }

        // This glyph has no mouse or keyboard interaction, so 
        // GetHitTest can return null.
        public override Cursor GetHitTest(Point p)
        {
            return null;
        }

        // This method renders the glyph as a simple focus rectangle.
        public override void Paint(PaintEventArgs e)
        {   
            ControlPaint.DrawFocusRectangle(
                    e.Graphics,
                    this.Bounds);

            ControlPaint.DrawFocusRectangle(
                    e.Graphics,
                    this.PaddingBounds);
        }

        // This glyph's Bounds property is a Rectangle defined by 
        // the value of the control's Margin property.
        public override Rectangle Bounds
        {
            get
            {
                Control c = this.relatedControl;
                Rectangle controlRect = 
                    this.behaviorService.ControlRectInAdornerWindow(this.relatedControl);

                Rectangle boundsVal = new Rectangle(
                    controlRect.Left - c.Margin.Left,
                    controlRect.Top - c.Margin.Top,
                    controlRect.Width + c.Margin.Right*2,
                    controlRect.Height + c.Margin.Bottom*2);

                return boundsVal;
            }
        }

        // The PaddingBounds property is a Rectangle defined by 
        // the value of the control's Padding property.
        public Rectangle PaddingBounds
        {
            get
            {
                Control c = this.relatedControl;
                Rectangle controlRect =
                    this.behaviorService.ControlRectInAdornerWindow(this.relatedControl);

                Rectangle boundsVal = new Rectangle(
                    controlRect.Left + c.Padding.Left,
                    controlRect.Top + c.Padding.Top,
                    controlRect.Width - c.Padding.Right * 2,
                    controlRect.Height - c.Padding.Bottom * 2);

                return boundsVal;
            }
        }

        // There are no keyboard or mouse behaviors associated with 
        // this glyph, but you could add them to this class.
        internal class MarginAndPaddingBehavior : Behavior
        {

        }
    }

    // This class implements an AnchorGlyph, which draws grab handles
    // that represent the value of the control's Anchor property.
    //
    // This glyph has mouse and keyboard interactions, which are
    // handled by the related behavior class, AnchorBehavior.
    // Double-clicking on an AnchorGlyph causes its value to be 
    // toggled between enabled and disable states. 

    public class AnchorGlyph : Glyph
    {
        // This defines the bounds of the anchor glyph.
        protected Rectangle boundsValue;

        // This defines the bounds used for hit testing.
        // These bounds are typically different than the bounds 
        // of the glyph itself.
        protected Rectangle hitBoundsValue;

        // This is the cursor returned if hit test is positive.
        protected Cursor hitTestCursor = Cursors.Hand;

        // Cache references to services that will be needed.
        private BehaviorService behaviorService = null;
        private IComponentChangeService changeService = null;
        private ISelectionService selectionService = null;

        // Keep a reference to the designer for convenience.
        private IDesigner relatedDesigner = null;

        // Keep a reference to the adorner for convenience.
        private Adorner anchorAdorner = null;

        // Keep a reference to the control being designed.
        private Control relatedControl = null;

        // This defines the AnchorStyle which this glyph represents.
        private AnchorStyles anchorStyle;

        public AnchorGlyph(
            AnchorStyles anchorStyle,
            BehaviorService behaviorService,
            IComponentChangeService changeService,
            ISelectionService selectionService,
            IDesigner relatedDesigner,
            Adorner anchorAdorner)
            : base(new AnchorBehavior(relatedDesigner))
        {   
            // Cache references for convenience.
            this.anchorStyle = anchorStyle;
            this.behaviorService = behaviorService;
            this.changeService = changeService;
            this.selectionService = selectionService;
            this.relatedDesigner = relatedDesigner;
            this.anchorAdorner = anchorAdorner;

            // Cache a reference to the control being designed.
            this.relatedControl = 
                this.relatedDesigner.Component as Control;

            // Hook the SelectionChanged event. 
            this.selectionService.SelectionChanged += 
                new EventHandler(selectionService_SelectionChanged);

            // Hook the ComponentChanged event so the anchor glyphs
            // can correctly track the control's bounds.
            this.changeService.ComponentChanged += 
                new ComponentChangedEventHandler(changeService_ComponentChanged);
        }

        #region Overrides

        public override Rectangle Bounds
        {
            get
            {
                return this.boundsValue;
            }
        }

        // This method renders the AnchorGlyph as a filled rectangle
        // if the glyph is enabled, or as an open rectangle if the
        // glyph is disabled.
        public override void Paint(PaintEventArgs e)
        {
            if (this.IsEnabled)
            {
                using (Brush b = new SolidBrush(Color.Tomato))
                {
                    e.Graphics.FillRectangle(b, this.Bounds);
                }
            }
            else
            {
                using (Pen p = new Pen(Color.Tomato))
                {
                    e.Graphics.DrawRectangle(p, this.Bounds);
                }
            }
        }

        // An AnchorGlyph has keyboard and mouse interaction, so it's
        // important to return a cursor when the mouse is located in 
        // the glyph's hit region. When this occurs, the 
        // AnchorBehavior becomes active.

        public override Cursor GetHitTest(Point p)
        {
            if (hitBoundsValue.Contains(p))
            {
                return hitTestCursor;
            }

            return null;
        }

        #endregion 

        #region Event Handlers

        // The AnchorGlyph objects should mimic the resize glyphs;
        // they should only be visible when the control is the 
        // primary selection. The adorner is enabled when the 
        // control is the primary selection and disabled when 
        // it is not.

        void selectionService_SelectionChanged(object sender, EventArgs e)
        {
            if (object.ReferenceEquals(
                this.selectionService.PrimarySelection,
                this.relatedControl))
            {
                this.ComputeBounds();
                this.anchorAdorner.Enabled = true;
            }
            else
            {
                this.anchorAdorner.Enabled = false;
            }
        }

        // If any of several properties change, the bounds of the 
        // AnchorGlyph must be computed again.
        void changeService_ComponentChanged(
            object sender, 
            ComponentChangedEventArgs e)
        {
            if (object.ReferenceEquals(
                e.Component,
                this.relatedControl))
            {
                if (e.Member.Name == "Anchor" ||
                    e.Member.Name == "Size" ||
                    e.Member.Name == "Height" ||
                    e.Member.Name == "Width" ||
                    e.Member.Name == "Location")
                {
                    // Compute the bounds of this glyph.
                    this.ComputeBounds();

                    // Tell the adorner to repaint itself.
                    this.anchorAdorner.Invalidate();
                }
            }
        }

        #endregion

        #region Implementation

        // This utility method computes the position and size of 
        // the AnchorGlyph in the Adorner window's coordinates.
        // It also computes the hit test bounds, which are
        // slightly larger than the glyph's bounds.
        private void ComputeBounds()
        {
            Rectangle translatedBounds = new Rectangle(
                this.behaviorService.ControlToAdornerWindow(this.relatedControl),
                this.relatedControl.Size);

            if ((this.anchorStyle & AnchorStyles.Top) == AnchorStyles.Top)
            {
                this.boundsValue = new Rectangle(
                    translatedBounds.X + (translatedBounds.Width / 2) - (glyphSize / 2),
                    translatedBounds.Y + glyphSize,
                    glyphSize,
                    glyphSize);
            }
            if ((this.anchorStyle & AnchorStyles.Bottom) == AnchorStyles.Bottom)
            {
                this.boundsValue = new Rectangle(
                    translatedBounds.X + (translatedBounds.Width / 2) - (glyphSize / 2),
                    translatedBounds.Bottom - 2*glyphSize,
                    glyphSize,
                    glyphSize);
            }
            if ((this.anchorStyle & AnchorStyles.Left) == AnchorStyles.Left)
            {
                this.boundsValue = new Rectangle(
                    translatedBounds.X + glyphSize,
                    translatedBounds.Y + (translatedBounds.Height / 2) - (glyphSize / 2),
                    glyphSize,
                    glyphSize);
            }
            if ((this.anchorStyle & AnchorStyles.Right) == AnchorStyles.Right)
            {
                this.boundsValue = new Rectangle(
                    translatedBounds.Right - 2*glyphSize,
                    translatedBounds.Y + (translatedBounds.Height / 2) - (glyphSize / 2),
                    glyphSize,
                    glyphSize);
            }

            this.hitBoundsValue = new Rectangle(
                this.Bounds.Left - hitBoundSize / 2,
                this.Bounds.Top - hitBoundSize / 2,
                hitBoundSize,
                hitBoundSize );
        }

        // This utility property determines if the AnchorGlyph is 
        // enabled, according to the value specified by the 
        // control's Anchor property.
        private bool IsEnabled
        {
            get
            {
                return 
                    ((this.anchorStyle & this.relatedControl.Anchor) == 
                    this.anchorStyle);
            }
        }

        #endregion 




        #region Behavior Implementation


        // This Behavior specifies mouse and keyboard handling when
        // an AnchorGlyph is active. This happens when 
        // AnchorGlyph.GetHitTest returns a non-null value.
        internal class AnchorBehavior : Behavior
        {
            private IDesigner relatedDesigner = null;
            private Control relatedControl = null;

            internal AnchorBehavior(IDesigner relatedDesigner)
            {
                this.relatedDesigner = relatedDesigner;
                this.relatedControl = relatedDesigner.Component as Control;
            }

            // When you double-click on an AnchorGlyph, the value of 
            // the control's Anchor property is toggled.
            //
            // Note that the value of the Anchor property is not set
            // by direct assignment. Instead, the 
            // PropertyDescriptor.SetValue method is used. This 
            // enables notification of the design environment, so 
            // related events can be raised, for example, the
            // IComponentChangeService.ComponentChanged event.

            public override bool OnMouseDoubleClick(
                Glyph g, 
                MouseButtons button, 
                Point mouseLoc)
            {
                base.OnMouseDoubleClick(g, button, mouseLoc);

                if (button == MouseButtons.Left)
                {
                    AnchorGlyph ag = g as AnchorGlyph;
                    PropertyDescriptor pdAnchor = 
                        TypeDescriptor.GetProperties(ag.relatedControl)["Anchor"];

                    if (ag.IsEnabled)
                    {
                        // The glyph is enabled. 
                        // Clear the AnchorStyle flag to disable the Glyph.
                        pdAnchor.SetValue(
                            ag.relatedControl, 
                            ag.relatedControl.Anchor ^ ag.anchorStyle );
                    }
                    else
                    {
                        // The glyph is disabled. 
                        // Set the AnchorStyle flag to enable the Glyph.
                        pdAnchor.SetValue(
                            ag.relatedControl,
                            ag.relatedControl.Anchor | ag.anchorStyle);
                    }

                }

                return true;
            }
        }


        #endregion
    }
    #endregion
}

DemoControl 클래스는 UserControl 클래스에서 파생되지만 디자인 타임 UI를 확장하기 위한 특수한 논리가 필요하지 않습니다. 디자인 타임 UI는 DemoControlDesigner 클래스에서 구현됩니다.

DemoControlDesigner 클래스는 Glyph, BehaviorAdorner 클래스를 사용하여 DemoControl의 디자인 타임 환경을 확장합니다. 확장된 UI의 모양은 GlyphAdorner 클래스로 구현됩니다. 마우스 및 키보드 상호 작용은 Behavior 클래스에서 구현됩니다.

OnPaintAdornmentsOnMouseEnter와 같은 ControlDesigner 메서드만 재정의해도 디자이너의 디자인 타임 모양과 동작을 확장할 수 있지만 Glyph 클래스를 사용하면 디자이너에서 모양과 동작 논리를 쉽게 제외할 수 있습니다.

모양 확장

Glyph 클래스를 구현하여 사용자 지정 디자인 UI의 모양을 확장합니다. MarginAndPaddingGlyph 클래스는 Glyph 클래스에서 파생됩니다. 이 클래스는 컨트롤의 MarginPadding 속성의 값을 나타내는 두 개의 사각형을 그립니다. MarginAndPaddingGlyph 클래스는 ComponentChanged 이벤트를 처리하여 컨트롤의 Margin 또는 Padding 속성 값이 바뀔 때 화면 표시를 업데이트합니다.

다음 코드 예제에서는 Glyph에서 파생되는 MarginAndPaddingGlyph 클래스를 구현하는 방법을 보여 줍니다.

' This class implements a MarginAndPaddingGlyph, which draws 
' borders highlighting the value of the control's Margin 
' property and the value of the control's Padding property.
'
' This glyph has no mouse or keyboard interaction, so its
' related behavior class, MarginAndPaddingBehavior, has no
' implementation.

Public Class MarginAndPaddingGlyph
    Inherits Glyph
    Private behaviorService As BehaviorService = Nothing
    Private changeService As IComponentChangeService = Nothing
    Private selectionService As ISelectionService = Nothing
    Private relatedDesigner As IDesigner = Nothing
    Private marginAndPaddingAdorner As Adorner = Nothing
    Private relatedControl As Control = Nothing


    Public Sub New( _
    ByVal behaviorService As BehaviorService, _
    ByVal changeService As IComponentChangeService, _
    ByVal selectionService As ISelectionService, _
    ByVal relatedDesigner As IDesigner, _
    ByVal marginAndPaddingAdorner As Adorner)

        MyBase.New(New MarginAndPaddingBehavior())
        Me.behaviorService = behaviorService
        Me.changeService = changeService
        Me.selectionService = selectionService
        Me.relatedDesigner = relatedDesigner
        Me.marginAndPaddingAdorner = marginAndPaddingAdorner

        Me.relatedControl = Me.relatedDesigner.Component

        AddHandler Me.changeService.ComponentChanged, _
        AddressOf changeService_ComponentChanged

    End Sub

    Private Sub changeService_ComponentChanged( _
    ByVal sender As Object, _
    ByVal e As ComponentChangedEventArgs)

        If Object.ReferenceEquals( _
        e.Component, _
        Me.relatedControl) Then
            If e.Member.Name = "Margin" OrElse _
               e.Member.Name = "Padding" Then
                Me.marginAndPaddingAdorner.Invalidate()
            End If
        End If

    End Sub

    ' This glyph has no mouse or keyboard interaction, so 
    ' GetHitTest can return null.
    Public Overrides Function GetHitTest( _
    ByVal p As Point) As Cursor
        Return Nothing
    End Function

    ' This method renders the glyph as a simple focus rectangle.
    Public Overrides Sub Paint(ByVal e As PaintEventArgs)
        ControlPaint.DrawFocusRectangle(e.Graphics, Me.Bounds)

        ControlPaint.DrawFocusRectangle( _
        e.Graphics, _
        Me.PaddingBounds)
    End Sub

    ' This glyph's Bounds property is a Rectangle defined by 
    ' the value of the control's Margin property.

    Public Overrides ReadOnly Property Bounds() As Rectangle
        Get
            Dim c As Control = Me.relatedControl
            Dim controlRect As Rectangle = _
            Me.behaviorService.ControlRectInAdornerWindow( _
            Me.relatedControl)

            Dim boundsVal As New Rectangle( _
            controlRect.Left - c.Margin.Left, _
            controlRect.Top - c.Margin.Top, _
            controlRect.Width + c.Margin.Right * 2, _
            controlRect.Height + c.Margin.Bottom * 2)

            Return boundsVal

        End Get
    End Property

    ' The PaddingBounds property is a Rectangle defined by 
    ' the value of the control's Padding property.

    Public ReadOnly Property PaddingBounds() As Rectangle
        Get
            Dim c As Control = Me.relatedControl
            Dim controlRect As Rectangle = _
            Me.behaviorService.ControlRectInAdornerWindow( _
            Me.relatedControl)

            Dim boundsVal As New Rectangle( _
            controlRect.Left + c.Padding.Left, _
            controlRect.Top + c.Padding.Top, _
            controlRect.Width - c.Padding.Right * 2, _
            controlRect.Height - c.Padding.Bottom * 2)

            Return boundsVal

        End Get
    End Property

    ' There are no keyboard or mouse behaviors associated with 
    ' this glyph, but you could add them to this class.

    Friend Class MarginAndPaddingBehavior
        Inherits System.Windows.Forms.Design.Behavior.Behavior
    End Class

End Class
// This class implements a MarginAndPaddingGlyph, which draws 
// borders highlighting the value of the control's Margin 
// property and the value of the control's Padding property.
//
// This glyph has no mouse or keyboard interaction, so its
// related behavior class, MarginAndPaddingBehavior, has no
// implementation.

public class MarginAndPaddingGlyph : Glyph
{
    private BehaviorService behaviorService = null;
    private IComponentChangeService changeService = null;
    private ISelectionService selectionService = null;
    private IDesigner relatedDesigner = null;
    private Adorner marginAndPaddingAdorner = null;
    private Control relatedControl = null;

    public MarginAndPaddingGlyph(
        BehaviorService behaviorService,
        IComponentChangeService changeService,
        ISelectionService selectionService,
        IDesigner relatedDesigner,
        Adorner marginAndPaddingAdorner)
        : base(new MarginAndPaddingBehavior())
    {
        this.behaviorService = behaviorService;
        this.changeService = changeService;
        this.selectionService = selectionService;
        this.relatedDesigner = relatedDesigner;
        this.marginAndPaddingAdorner = marginAndPaddingAdorner;

        this.relatedControl = 
            this.relatedDesigner.Component as Control;

        this.changeService.ComponentChanged += new ComponentChangedEventHandler(changeService_ComponentChanged);
    }

    void changeService_ComponentChanged(object sender, ComponentChangedEventArgs e)
    {
        if (object.ReferenceEquals(
            e.Component, 
            this.relatedControl))
        {
            if (e.Member.Name == "Margin" ||
                e.Member.Name == "Padding" )
            {
                this.marginAndPaddingAdorner.Invalidate();
            }
        }
    }

    // This glyph has no mouse or keyboard interaction, so 
    // GetHitTest can return null.
    public override Cursor GetHitTest(Point p)
    {
        return null;
    }

    // This method renders the glyph as a simple focus rectangle.
    public override void Paint(PaintEventArgs e)
    {   
        ControlPaint.DrawFocusRectangle(
                e.Graphics,
                this.Bounds);

        ControlPaint.DrawFocusRectangle(
                e.Graphics,
                this.PaddingBounds);
    }

    // This glyph's Bounds property is a Rectangle defined by 
    // the value of the control's Margin property.
    public override Rectangle Bounds
    {
        get
        {
            Control c = this.relatedControl;
            Rectangle controlRect = 
                this.behaviorService.ControlRectInAdornerWindow(this.relatedControl);

            Rectangle boundsVal = new Rectangle(
                controlRect.Left - c.Margin.Left,
                controlRect.Top - c.Margin.Top,
                controlRect.Width + c.Margin.Right*2,
                controlRect.Height + c.Margin.Bottom*2);

            return boundsVal;
        }
    }

    // The PaddingBounds property is a Rectangle defined by 
    // the value of the control's Padding property.
    public Rectangle PaddingBounds
    {
        get
        {
            Control c = this.relatedControl;
            Rectangle controlRect =
                this.behaviorService.ControlRectInAdornerWindow(this.relatedControl);

            Rectangle boundsVal = new Rectangle(
                controlRect.Left + c.Padding.Left,
                controlRect.Top + c.Padding.Top,
                controlRect.Width - c.Padding.Right * 2,
                controlRect.Height - c.Padding.Bottom * 2);

            return boundsVal;
        }
    }

    // There are no keyboard or mouse behaviors associated with 
    // this glyph, but you could add them to this class.
    internal class MarginAndPaddingBehavior : Behavior
    {

    }
}

동작 확장

Behavior 클래스를 구현하여 사용자 지정 디자인 UI의 동작을 확장합니다. Behavior 클래스는 Glyph 클래스의 하위 수준입니다.

경고

디자인 환경에서는 Glyph 개체에 연결되지 않은 Behavior 개체를 사용할 수 없습니다.

Glyph 형식의 생성자에서 Behavior 개체를 Glyph 개체에 연결합니다.

다음 코드 예제에서는 AnchorGlyph 생성자를 구현하는 방법을 보여 줍니다.

Public Sub New( _
ByVal anchorStyle As AnchorStyles, _
ByVal behaviorService As BehaviorService, _
ByVal changeService As IComponentChangeService, _
ByVal selectionService As ISelectionService, _
ByVal relatedDesigner As IDesigner, _
ByVal anchorAdorner As Adorner)

    MyBase.New(New AnchorBehavior(relatedDesigner))
    ' Cache references for convenience.
    Me.anchorStyle = anchorStyle
    Me.behaviorService = behaviorService
    Me.changeService = changeService
    Me.selectionService = selectionService
    Me.relatedDesigner = relatedDesigner
    Me.anchorAdorner = anchorAdorner

    ' Cache a reference to the control being designed.
    Me.relatedControl = Me.relatedDesigner.Component

    ' Hook the SelectionChanged event. 
    AddHandler Me.selectionService.SelectionChanged, _
    AddressOf selectionService_SelectionChanged

    ' Hook the ComponentChanged event so the anchor glyphs
    ' can correctly track the control's bounds.
    AddHandler Me.changeService.ComponentChanged, _
    AddressOf changeService_ComponentChanged

End Sub
public AnchorGlyph(
    AnchorStyles anchorStyle,
    BehaviorService behaviorService,
    IComponentChangeService changeService,
    ISelectionService selectionService,
    IDesigner relatedDesigner,
    Adorner anchorAdorner)
    : base(new AnchorBehavior(relatedDesigner))
{   
    // Cache references for convenience.
    this.anchorStyle = anchorStyle;
    this.behaviorService = behaviorService;
    this.changeService = changeService;
    this.selectionService = selectionService;
    this.relatedDesigner = relatedDesigner;
    this.anchorAdorner = anchorAdorner;

    // Cache a reference to the control being designed.
    this.relatedControl = 
        this.relatedDesigner.Component as Control;

    // Hook the SelectionChanged event. 
    this.selectionService.SelectionChanged += 
        new EventHandler(selectionService_SelectionChanged);

    // Hook the ComponentChanged event so the anchor glyphs
    // can correctly track the control's bounds.
    this.changeService.ComponentChanged += 
        new ComponentChangedEventHandler(changeService_ComponentChanged);
}

AnchorGlyph 클래스는 컨트롤의 Anchor 속성 값에 해당하는 선택 핸들을 그립니다. 마우스 포인터를 문자 모양의 핫 스폿 위에 놓을 때 Cursor 개체를 반환하도록 GetHitTest 메서드를 재정의합니다. 디자인 환경에서는 GetHitTest 메서드에서 null이 아닌 값을 받을 경우 Glyph에 연결된 Behavior 개체를 활성화합니다.

다음 코드 예제에서는 AnchorGlyph 클래스를 구현하는 방법을 보여 줍니다.

    ' This class implements an AnchorGlyph, which draws grab handles
    ' that represent the value of the control's Anchor property.
    '
    ' This glyph has mouse and keyboard interactions, which are
    ' handled by the related behavior class, AnchorBehavior.
    ' Double-clicking on an AnchorGlyph causes its value to be 
    ' toggled between enabled and disable states. 

    Public Class AnchorGlyph
        Inherits Glyph

        ' This defines the bounds of the anchor glyph.
        Protected boundsValue As Rectangle

        ' This defines the bounds used for hit testing.
        ' These bounds are typically different than the bounds 
        ' of the glyph itself.
        Protected hitBoundsValue As Rectangle

        ' This is the cursor returned if hit test is positive.
        Protected hitTestCursor As Cursor = Cursors.Hand

        ' Cache references to services that will be needed.
        Private behaviorService As BehaviorService = Nothing
        Private changeService As IComponentChangeService = Nothing
        Private selectionService As ISelectionService = Nothing

        ' Keep a reference to the designer for convenience.
        Private relatedDesigner As IDesigner = Nothing

        ' Keep a reference to the adorner for convenience.
        Private anchorAdorner As Adorner = Nothing

        ' Keep a reference to the control being designed.
        Private relatedControl As Control = Nothing

        ' This defines the AnchorStyle which this glyph represents.
        Private anchorStyle As AnchorStyles


        Public Sub New( _
        ByVal anchorStyle As AnchorStyles, _
        ByVal behaviorService As BehaviorService, _
        ByVal changeService As IComponentChangeService, _
        ByVal selectionService As ISelectionService, _
        ByVal relatedDesigner As IDesigner, _
        ByVal anchorAdorner As Adorner)

            MyBase.New(New AnchorBehavior(relatedDesigner))
            ' Cache references for convenience.
            Me.anchorStyle = anchorStyle
            Me.behaviorService = behaviorService
            Me.changeService = changeService
            Me.selectionService = selectionService
            Me.relatedDesigner = relatedDesigner
            Me.anchorAdorner = anchorAdorner

            ' Cache a reference to the control being designed.
            Me.relatedControl = Me.relatedDesigner.Component

            ' Hook the SelectionChanged event. 
            AddHandler Me.selectionService.SelectionChanged, _
            AddressOf selectionService_SelectionChanged

            ' Hook the ComponentChanged event so the anchor glyphs
            ' can correctly track the control's bounds.
            AddHandler Me.changeService.ComponentChanged, _
            AddressOf changeService_ComponentChanged

        End Sub

#Region "Overrides"

        Public Overrides ReadOnly Property Bounds() As Rectangle
            Get
                Return Me.boundsValue
            End Get
        End Property


        ' This method renders the AnchorGlyph as a filled rectangle
        ' if the glyph is enabled, or as an open rectangle if the
        ' glyph is disabled.
        Public Overrides Sub Paint(ByVal e As PaintEventArgs)
            If Me.IsEnabled Then
                Dim b As New SolidBrush(Color.Tomato)
                Try
                    e.Graphics.FillRectangle(b, Me.Bounds)
                Finally
                    b.Dispose()
                End Try
            Else
                Dim p As New Pen(Color.Tomato)
                Try
                    e.Graphics.DrawRectangle(p, Me.Bounds)
                Finally
                    p.Dispose()
                End Try
            End If
        End Sub


        ' An AnchorGlyph has keyboard and mouse interaction, so it's
        ' important to return a cursor when the mouse is located in 
        ' the glyph's hit region. When this occurs, the 
        ' AnchorBehavior becomes active.
        Public Overrides Function GetHitTest( _
        ByVal p As Point) As Cursor

            If hitBoundsValue.Contains(p) Then
                Return hitTestCursor
            End If

            Return Nothing

        End Function

#End Region

#Region "Event Handlers"


        ' The AnchorGlyph objects should mimic the resize glyphs;
        ' they should only be visible when the control is the 
        ' primary selection. The adorner is enabled when the 
        ' control is the primary selection and disabled when 
        ' it is not.
        Private Sub selectionService_SelectionChanged( _
        ByVal sender As Object, _
        ByVal e As EventArgs)

            If Object.ReferenceEquals( _
            Me.selectionService.PrimarySelection, _
            Me.relatedControl) Then
                Me.ComputeBounds()
                Me.anchorAdorner.Enabled = True
            Else
                Me.anchorAdorner.Enabled = False
            End If

        End Sub

        ' If any of several properties change, the bounds of the 
        ' AnchorGlyph must be computed again.
        Private Sub changeService_ComponentChanged( _
        ByVal sender As Object, _
        ByVal e As ComponentChangedEventArgs)

            If Object.ReferenceEquals( _
            e.Component, Me.relatedControl) Then
                If e.Member.Name = "Anchor" OrElse _
                   e.Member.Name = "Size" OrElse _
                   e.Member.Name = "Height" OrElse _
                   e.Member.Name = "Width" OrElse _
                   e.Member.Name = "Location" Then

                    ' Compute the bounds of this glyph.
                    Me.ComputeBounds()

                    ' Tell the adorner to repaint itself.
                    Me.anchorAdorner.Invalidate()

                End If
            End If
        End Sub

#End Region

#Region "Implementation"


        ' This utility method computes the position and size of 
        ' the AnchorGlyph in the Adorner window's coordinates.
        ' It also computes the hit test bounds, which are
        ' slightly larger than the glyph's bounds.
        Private Sub ComputeBounds()
            Dim translatedBounds As New Rectangle( _
            Me.behaviorService.ControlToAdornerWindow(Me.relatedControl), _
            Me.relatedControl.Size)

            If (Me.anchorStyle And AnchorStyles.Top) = AnchorStyles.Top Then
                Me.boundsValue = New Rectangle( _
                translatedBounds.X + translatedBounds.Width / 2 - glyphSize / 2, _
                translatedBounds.Y + glyphSize, _
                glyphSize, _
                glyphSize)
            End If

            If (Me.anchorStyle And AnchorStyles.Bottom) = AnchorStyles.Bottom Then
                Me.boundsValue = New Rectangle( _
                translatedBounds.X + translatedBounds.Width / 2 - glyphSize / 2, _
                translatedBounds.Bottom - 2 * glyphSize, _
                glyphSize, _
                glyphSize)
            End If

            If (Me.anchorStyle And AnchorStyles.Left) = AnchorStyles.Left Then
                Me.boundsValue = New Rectangle( _
                translatedBounds.X + glyphSize, _
                translatedBounds.Y + translatedBounds.Height / 2 - glyphSize / 2, _
                glyphSize, _
                glyphSize)
            End If

            If (Me.anchorStyle And AnchorStyles.Right) = AnchorStyles.Right Then
                Me.boundsValue = New Rectangle( _
                translatedBounds.Right - 2 * glyphSize, _
                translatedBounds.Y + translatedBounds.Height / 2 - glyphSize / 2, _
                glyphSize, _
                glyphSize)
            End If

            Me.hitBoundsValue = New Rectangle( _
            Me.Bounds.Left - hitBoundSize / 2, _
            Me.Bounds.Top - hitBoundSize / 2, _
            hitBoundSize, _
            hitBoundSize)

        End Sub

        ' This utility property determines if the AnchorGlyph is 
        ' enabled, according to the value specified by the 
        ' control's Anchor property.

        Private ReadOnly Property IsEnabled() As Boolean
            Get
                Return (Me.anchorStyle And Me.relatedControl.Anchor) = Me.anchorStyle
            End Get
        End Property

#End Region

    // This class implements an AnchorGlyph, which draws grab handles
    // that represent the value of the control's Anchor property.
    //
    // This glyph has mouse and keyboard interactions, which are
    // handled by the related behavior class, AnchorBehavior.
    // Double-clicking on an AnchorGlyph causes its value to be 
    // toggled between enabled and disable states. 

    public class AnchorGlyph : Glyph
    {
        // This defines the bounds of the anchor glyph.
        protected Rectangle boundsValue;

        // This defines the bounds used for hit testing.
        // These bounds are typically different than the bounds 
        // of the glyph itself.
        protected Rectangle hitBoundsValue;

        // This is the cursor returned if hit test is positive.
        protected Cursor hitTestCursor = Cursors.Hand;

        // Cache references to services that will be needed.
        private BehaviorService behaviorService = null;
        private IComponentChangeService changeService = null;
        private ISelectionService selectionService = null;

        // Keep a reference to the designer for convenience.
        private IDesigner relatedDesigner = null;

        // Keep a reference to the adorner for convenience.
        private Adorner anchorAdorner = null;

        // Keep a reference to the control being designed.
        private Control relatedControl = null;

        // This defines the AnchorStyle which this glyph represents.
        private AnchorStyles anchorStyle;

        public AnchorGlyph(
            AnchorStyles anchorStyle,
            BehaviorService behaviorService,
            IComponentChangeService changeService,
            ISelectionService selectionService,
            IDesigner relatedDesigner,
            Adorner anchorAdorner)
            : base(new AnchorBehavior(relatedDesigner))
        {   
            // Cache references for convenience.
            this.anchorStyle = anchorStyle;
            this.behaviorService = behaviorService;
            this.changeService = changeService;
            this.selectionService = selectionService;
            this.relatedDesigner = relatedDesigner;
            this.anchorAdorner = anchorAdorner;

            // Cache a reference to the control being designed.
            this.relatedControl = 
                this.relatedDesigner.Component as Control;

            // Hook the SelectionChanged event. 
            this.selectionService.SelectionChanged += 
                new EventHandler(selectionService_SelectionChanged);

            // Hook the ComponentChanged event so the anchor glyphs
            // can correctly track the control's bounds.
            this.changeService.ComponentChanged += 
                new ComponentChangedEventHandler(changeService_ComponentChanged);
        }

        #region Overrides

        public override Rectangle Bounds
        {
            get
            {
                return this.boundsValue;
            }
        }

        // This method renders the AnchorGlyph as a filled rectangle
        // if the glyph is enabled, or as an open rectangle if the
        // glyph is disabled.
        public override void Paint(PaintEventArgs e)
        {
            if (this.IsEnabled)
            {
                using (Brush b = new SolidBrush(Color.Tomato))
                {
                    e.Graphics.FillRectangle(b, this.Bounds);
                }
            }
            else
            {
                using (Pen p = new Pen(Color.Tomato))
                {
                    e.Graphics.DrawRectangle(p, this.Bounds);
                }
            }
        }

        // An AnchorGlyph has keyboard and mouse interaction, so it's
        // important to return a cursor when the mouse is located in 
        // the glyph's hit region. When this occurs, the 
        // AnchorBehavior becomes active.

        public override Cursor GetHitTest(Point p)
        {
            if (hitBoundsValue.Contains(p))
            {
                return hitTestCursor;
            }

            return null;
        }

        #endregion 

        #region Event Handlers

        // The AnchorGlyph objects should mimic the resize glyphs;
        // they should only be visible when the control is the 
        // primary selection. The adorner is enabled when the 
        // control is the primary selection and disabled when 
        // it is not.

        void selectionService_SelectionChanged(object sender, EventArgs e)
        {
            if (object.ReferenceEquals(
                this.selectionService.PrimarySelection,
                this.relatedControl))
            {
                this.ComputeBounds();
                this.anchorAdorner.Enabled = true;
            }
            else
            {
                this.anchorAdorner.Enabled = false;
            }
        }

        // If any of several properties change, the bounds of the 
        // AnchorGlyph must be computed again.
        void changeService_ComponentChanged(
            object sender, 
            ComponentChangedEventArgs e)
        {
            if (object.ReferenceEquals(
                e.Component,
                this.relatedControl))
            {
                if (e.Member.Name == "Anchor" ||
                    e.Member.Name == "Size" ||
                    e.Member.Name == "Height" ||
                    e.Member.Name == "Width" ||
                    e.Member.Name == "Location")
                {
                    // Compute the bounds of this glyph.
                    this.ComputeBounds();

                    // Tell the adorner to repaint itself.
                    this.anchorAdorner.Invalidate();
                }
            }
        }

        #endregion

        #region Implementation

        // This utility method computes the position and size of 
        // the AnchorGlyph in the Adorner window's coordinates.
        // It also computes the hit test bounds, which are
        // slightly larger than the glyph's bounds.
        private void ComputeBounds()
        {
            Rectangle translatedBounds = new Rectangle(
                this.behaviorService.ControlToAdornerWindow(this.relatedControl),
                this.relatedControl.Size);

            if ((this.anchorStyle & AnchorStyles.Top) == AnchorStyles.Top)
            {
                this.boundsValue = new Rectangle(
                    translatedBounds.X + (translatedBounds.Width / 2) - (glyphSize / 2),
                    translatedBounds.Y + glyphSize,
                    glyphSize,
                    glyphSize);
            }
            if ((this.anchorStyle & AnchorStyles.Bottom) == AnchorStyles.Bottom)
            {
                this.boundsValue = new Rectangle(
                    translatedBounds.X + (translatedBounds.Width / 2) - (glyphSize / 2),
                    translatedBounds.Bottom - 2*glyphSize,
                    glyphSize,
                    glyphSize);
            }
            if ((this.anchorStyle & AnchorStyles.Left) == AnchorStyles.Left)
            {
                this.boundsValue = new Rectangle(
                    translatedBounds.X + glyphSize,
                    translatedBounds.Y + (translatedBounds.Height / 2) - (glyphSize / 2),
                    glyphSize,
                    glyphSize);
            }
            if ((this.anchorStyle & AnchorStyles.Right) == AnchorStyles.Right)
            {
                this.boundsValue = new Rectangle(
                    translatedBounds.Right - 2*glyphSize,
                    translatedBounds.Y + (translatedBounds.Height / 2) - (glyphSize / 2),
                    glyphSize,
                    glyphSize);
            }

            this.hitBoundsValue = new Rectangle(
                this.Bounds.Left - hitBoundSize / 2,
                this.Bounds.Top - hitBoundSize / 2,
                hitBoundSize,
                hitBoundSize );
        }

        // This utility property determines if the AnchorGlyph is 
        // enabled, according to the value specified by the 
        // control's Anchor property.
        private bool IsEnabled
        {
            get
            {
                return 
                    ((this.anchorStyle & this.relatedControl.Anchor) == 
                    this.anchorStyle);
            }
        }

        #endregion 

AnchorBehavior 클래스는 사용자 지정 마우스 상호 작용을 구현합니다. OnMouseEnter와 같은 Behavior 클래스 메서드를 재정의하여 사용자 지정 UI를 정의합니다.

다음 코드 예제에서는 AnchorBehavior 클래스를 구현하는 방법을 보여 줍니다.

        ' This Behavior specifies mouse and keyboard handling when
        ' an AnchorGlyph is active. This happens when 
        ' AnchorGlyph.GetHitTest returns a non-null value.

        Friend Class AnchorBehavior
            Inherits System.Windows.Forms.Design.Behavior.Behavior
            Private relatedDesigner As IDesigner = Nothing
            Private relatedControl As Control = Nothing


            Friend Sub New(ByVal relatedDesigner As IDesigner)
                Me.relatedDesigner = relatedDesigner
                Me.relatedControl = relatedDesigner.Component
            End Sub

            ' When you double-click on an AnchorGlyph, the value of 
            ' the control's Anchor property is toggled.
            '
            ' Note that the value of the Anchor property is not set
            ' by direct assignment. Instead, the 
            ' PropertyDescriptor.SetValue method is used. This 
            ' enables notification of the design environment, so 
            ' related events can be raised, for example, the
            ' IComponentChangeService.ComponentChanged event.
            Public Overrides Function OnMouseDoubleClick( _
            ByVal g As Glyph, _
            ByVal button As MouseButtons, _
            ByVal mouseLoc As Point) As Boolean

                MyBase.OnMouseDoubleClick(g, button, mouseLoc)

                If button = MouseButtons.Left Then
                    Dim ag As AnchorGlyph = g

                    Dim pdAnchor As PropertyDescriptor = _
                    TypeDescriptor.GetProperties(ag.relatedControl)("Anchor")

                    If ag.IsEnabled Then
                        ' The glyph is enabled. 
                        ' Clear the AnchorStyle flag to disable the Glyph.
                        pdAnchor.SetValue(ag.relatedControl, _
                        ag.relatedControl.Anchor Xor ag.anchorStyle)
                    Else
                        ' The glyph is disabled. 
                        ' Set the AnchorStyle flag to enable the Glyph.
                        pdAnchor.SetValue(ag.relatedControl, _
                        ag.relatedControl.Anchor Or ag.anchorStyle)
                    End If
                End If

                Return True

            End Function

        End Class

#End Region

    End Class

        // This Behavior specifies mouse and keyboard handling when
        // an AnchorGlyph is active. This happens when 
        // AnchorGlyph.GetHitTest returns a non-null value.
        internal class AnchorBehavior : Behavior
        {
            private IDesigner relatedDesigner = null;
            private Control relatedControl = null;

            internal AnchorBehavior(IDesigner relatedDesigner)
            {
                this.relatedDesigner = relatedDesigner;
                this.relatedControl = relatedDesigner.Component as Control;
            }

            // When you double-click on an AnchorGlyph, the value of 
            // the control's Anchor property is toggled.
            //
            // Note that the value of the Anchor property is not set
            // by direct assignment. Instead, the 
            // PropertyDescriptor.SetValue method is used. This 
            // enables notification of the design environment, so 
            // related events can be raised, for example, the
            // IComponentChangeService.ComponentChanged event.

            public override bool OnMouseDoubleClick(
                Glyph g, 
                MouseButtons button, 
                Point mouseLoc)
            {
                base.OnMouseDoubleClick(g, button, mouseLoc);

                if (button == MouseButtons.Left)
                {
                    AnchorGlyph ag = g as AnchorGlyph;
                    PropertyDescriptor pdAnchor = 
                        TypeDescriptor.GetProperties(ag.relatedControl)["Anchor"];

                    if (ag.IsEnabled)
                    {
                        // The glyph is enabled. 
                        // Clear the AnchorStyle flag to disable the Glyph.
                        pdAnchor.SetValue(
                            ag.relatedControl, 
                            ag.relatedControl.Anchor ^ ag.anchorStyle );
                    }
                    else
                    {
                        // The glyph is disabled. 
                        // Set the AnchorStyle flag to enable the Glyph.
                        pdAnchor.SetValue(
                            ag.relatedControl,
                            ag.relatedControl.Anchor | ag.anchorStyle);
                    }

                }

                return true;
            }
        }

디자인 타임 사용자 인터페이스 활성화

Adorner 창을 만들고 이를 Glyphs 컬렉션에 추가하여 문자 모양을 활성화합니다. Adorner 창을 BehaviorServiceAdorners 컬렉션에 추가하여 사용자 지정 디자인 타임 UI를 활성화합니다. 디자이너의 Initialize 메서드에서 이 작업을 수행합니다.

다음 코드 예제에서는 디자인 타임 UI를 활성화하는 방법을 보여 줍니다.

' This method is where the designer initializes its state when
' it is created.
Public Overrides Sub Initialize(ByVal component As IComponent)
    MyBase.Initialize(component)

    ' Connect to various designer services.
    InitializeServices()

    ' Initialize adorners.
    Me.InitializeMarginAndPaddingAdorner()
    Me.InitializeAnchorAdorner()
End Sub
// This method is where the designer initializes its state when
// it is created.
public override void Initialize(IComponent component)
{
    base.Initialize(component);

    // Connect to various designer services.
    InitializeServices();

    // Initialize adorners.
    this.InitializeMarginAndPaddingAdorner();
    this.InitializeAnchorAdorner();

}
' This utility method creates an adorner for the anchor glyphs.
' It then creates four AnchorGlyph objects and adds them to 
' the adorner's Glyphs collection.
Private Sub InitializeAnchorAdorner()
    Me.anchorAdorner = New Adorner()
    Me.behaviorSvc.Adorners.Add(Me.anchorAdorner)

    Me.anchorAdorner.Glyphs.Add(New AnchorGlyph( _
    AnchorStyles.Left, _
    Me.behaviorSvc, _
    Me.changeService, _
    Me.selectionService, _
    Me, _
    Me.anchorAdorner))

    Me.anchorAdorner.Glyphs.Add(New AnchorGlyph( _
    AnchorStyles.Top, _
    Me.behaviorSvc, _
    Me.changeService, _
    Me.selectionService, _
    Me, _
    Me.anchorAdorner))

    Me.anchorAdorner.Glyphs.Add(New AnchorGlyph( _
    AnchorStyles.Right, _
    Me.behaviorSvc, _
    Me.changeService, _
    Me.selectionService, _
    Me, _
    Me.anchorAdorner))

    Me.anchorAdorner.Glyphs.Add(New AnchorGlyph( _
    AnchorStyles.Bottom, _
    Me.behaviorSvc, _
    Me.changeService, _
    Me.selectionService, _
    Me, _
    Me.anchorAdorner))

End Sub
// This utility method creates an adorner for the anchor glyphs.
// It then creates four AnchorGlyph objects and adds them to 
// the adorner's Glyphs collection.
private void InitializeAnchorAdorner()
{
    this.anchorAdorner = new Adorner();
    this.behaviorSvc.Adorners.Add(this.anchorAdorner);

    this.anchorAdorner.Glyphs.Add(new AnchorGlyph(
        AnchorStyles.Left,
        this.behaviorSvc,
        this.changeService,
        this.selectionService,
        this,
        this.anchorAdorner)
        );

    this.anchorAdorner.Glyphs.Add(new AnchorGlyph(
        AnchorStyles.Top,
        this.behaviorSvc,
        this.changeService,
        this.selectionService,
        this,
        this.anchorAdorner)
        );

    this.anchorAdorner.Glyphs.Add(new AnchorGlyph(
        AnchorStyles.Right,
        this.behaviorSvc,
        this.changeService,
        this.selectionService,
        this,
        this.anchorAdorner)
        );

    this.anchorAdorner.Glyphs.Add(new AnchorGlyph(
        AnchorStyles.Bottom,
        this.behaviorSvc,
        this.changeService,
        this.selectionService,
        this,
        this.anchorAdorner)
        );
}
' This utility method creates an adorner for the margin and 
' padding glyphs. It then creates a MarginAndPaddingGlyph and 
' adds it to the adorner's Glyphs collection.
Private Sub InitializeMarginAndPaddingAdorner()

    Me.marginAndPaddingAdorner = New Adorner()
    Me.behaviorSvc.Adorners.Add(Me.marginAndPaddingAdorner)

    Me.marginAndPaddingAdorner.Glyphs.Add(New MarginAndPaddingGlyph( _
    Me.behaviorSvc, _
    Me.changeService, _
    Me.selectionService, _
    Me, _
    Me.marginAndPaddingAdorner))

End Sub
// This utility method creates an adorner for the margin and 
// padding glyphs. It then creates a MarginAndPaddingGlyph and 
// adds it to the adorner's Glyphs collection.
private void InitializeMarginAndPaddingAdorner()
{
    this.marginAndPaddingAdorner = new Adorner();
    this.behaviorSvc.Adorners.Add(this.marginAndPaddingAdorner);

    this.marginAndPaddingAdorner.Glyphs.Add(new MarginAndPaddingGlyph(
        this.behaviorSvc,
        this.changeService,
        this.selectionService,
        this,
        this.marginAndPaddingAdorner));
}

코드 컴파일

디자인 타임 어셈블리 System.Design.dll에 대한 참조를 추가해야 합니다.

구성 요소의 디자인 타임 측면을 변경한 경우 컨트롤 프로젝트를 다시 빌드해야 합니다. 또한, 현재 열려 있고 이 구성 요소를 사용하는 다른 Windows Forms 프로젝트가 있는 경우 변경 내용을 보려면 프로젝트에 대한 새로 고침을 수행해야 합니다. 일반적으로 구성 요소가 있는 디자인 창을 닫고 다시 열어야 합니다.

참고 항목

작업

방법: Windows Forms 구성 요소에 스마트 태그 연결

참조

Glyph

Behavior

Adorner

기타 리소스

사용자 지정 디자이너