HOW TO:在設計模式中擴充控制項的外觀和行為
更新:2007 年 11 月
您可以透過自行撰寫自訂的設計工具,擴充設計階段環境。您的自訂設計工具可以在使用者設計控制項時,變更控制項的外觀和行為。
範例
下列程式碼範例將示範如何建立自訂設計工具,擴充使用者介面 (UI) 以設計自訂控制項。將名為 DemoControlDesigner 的設計工具類別附加至 DemoControl 類別,啟用下列作業:
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、Behavior 和 Adorner 類別,以擴充 DemoControl 的設計階段經驗。已擴充之 UI 的視覺化方面是利用 Glyph 和 Adorner 類別進行實作。滑鼠和鍵盤互動是在 Behavior 類別中實作。
您只要透過覆寫 ControlDesigner 方法 (例如 OnPaintAdornments 和 OnMouseEnter),就可以擴充設計工具的設計階段外觀和行為,但是 Glyph 類別提供簡便的方式,可以從您的設計工具整理外觀和行為邏輯。
擴充外觀
您可以透過實作 Glyph 類別,擴充自訂設計 UI 的外觀。MarginAndPaddingGlyph 類別衍生自 Glyph 類別。它會繪製兩個矩形,代表控制項的 Margin 和 Padding 屬性值。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 類別之下。
警告: |
---|
您將 Behavior 物件附加至 Glyph 型別之建構函式中的 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 屬性值的選取範圍控點。在滑鼠指標位於圖像 (Glyph) 的作用點 (Hot Spot) 之上時,您要覆寫 GetHitTest 方法,以傳回 Cursor 物件。當設計環境從非 null 的 GetHitTest 方法接到值時,它會啟動與 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 類別會實作自訂滑鼠互動作業。您要覆寫 Behavior 類別方法,如 OnMouseEnter,以定義自訂 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 視窗至 BehaviorService 的 Adorners 集合,啟動您的自訂設計階段 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));
}
編譯程式碼
當您對元件的設計階段方面進行變更時,必須重新建置控制項專案。此外,如果目前有開啟另一個 Windows Form 專案,而且該專案使用這個元件,您可能需要重新整理專案才能看到變更。您通常必須先關閉包含元件的設計視窗,然後再重新開啟一次。
請參閱
工作
HOW TO:將智慧標籤附加至 Windows Form 元件