共用方式為


逐步解說:實作 UI 型別編輯器

更新:2007 年 11 月

您可以透過實作使用者介面 (UI) 型別編輯器,提供複雜屬性型別的自訂設計階段經驗。

這個逐步解說會說明:您如何為自訂型別撰寫自己的 UI 型別編輯器,並使用 PropertyGrid,顯示編輯介面。

這個逐步解說所說明的工作包括:

  • 定義自訂型別

  • 定義 UI 型別編輯器的檢視控制項

  • 定義從 UITypeEditor 衍生的類別

  • 覆寫 GetEditStyle 方法,以告知 PropertyGrid 編輯器將使用何種編輯器樣式

  • 覆寫 EditValue 方法,以處理使用者介面、使用者輸入處理,以及值指派

若要將此逐步解說中的程式碼複製為一份清單,請參閱 HOW TO:建立採用設計階段功能的 Windows Form 控制項

必要條件

若要完成這個逐步解說,您必須要有:

  • 有足夠的權限可在已安裝 .NET Framework 的電腦上建立並執行 Windows Form 應用程式專案。

定義自訂型別

您的自訂 UI 型別編輯器將顯示自訂型別。這個型別可以很複雜,也可以很簡單。在這個逐步解說中,您將以自訂設計階段編輯行為定義簡單型別。這個型別稱為 MarqueeLightShape,而且它是有兩個值 (Square 和 Circle) 的 enum。

若要定義自訂列舉型別

  • 在您的 Windows Form 控制項定義主體中,定義 MarqueeLightShape 型別。

    ' This defines the possible values for the MarqueeBorder
    ' control's LightShape property.
    Public Enum MarqueeLightShape
        Square
        Circle
    End Enum
    
    // This defines the possible values for the MarqueeBorder
    // control's LightShape property.
    public enum MarqueeLightShape
    {
        Square,
        Circle
    }
    

定義檢視控制項

您的自訂 UI 型別編輯器會使用 Windows Form 控制項,顯示編輯介面。這個控制項已命名為 LightShapeSelectionControl,而且是衍生自 UserControl。其建構函式將取得目前的屬性值和 IWindowsFormsEditorService 的參考。檢視控制項會使用 IWindowsFormsEditorService 上的 CloseDropDown 方法,在使用者按一下選取範圍時,關閉下拉式視窗。

若要定義檢視控制項

  • 在您的 Windows Form 控制項定義主體中,定義 LightShapeSelectionControl 控制項。

    ' This control provides the custom UI for the LightShape property
    ' of the MarqueeBorder. It is used by the LightShapeEditor.
    Public Class LightShapeSelectionControl
        Inherits System.Windows.Forms.UserControl
    
       Private lightShapeValue As MarqueeLightShape = MarqueeLightShape.Square
    
        Private editorService As IWindowsFormsEditorService
       Private squarePanel As System.Windows.Forms.Panel
       Private circlePanel As System.Windows.Forms.Panel
    
       ' Required designer variable.
       Private components As System.ComponentModel.Container = Nothing
    
    
       ' This constructor takes a MarqueeLightShape value from the
       ' design-time environment, which will be used to display
       ' the initial state.
        Public Sub New( _
        ByVal lightShape As MarqueeLightShape, _
        ByVal editorService As IWindowsFormsEditorService)
            ' This call is required by the Windows.Forms Form Designer.
            InitializeComponent()
    
            ' Cache the light shape value provided by the 
            ' design-time environment.
            Me.lightShapeValue = lightShape
    
            ' Cache the reference to the editor service.
            Me.editorService = editorService
    
            ' Handle the Click event for the two panels. 
            AddHandler Me.squarePanel.Click, AddressOf squarePanel_Click
            AddHandler Me.circlePanel.Click, AddressOf circlePanel_Click
        End Sub
    
        Protected Overrides Sub Dispose(ByVal disposing As Boolean)
            If disposing Then
    
                ' Be sure to unhook event handlers
                ' to prevent "lapsed listener" leaks.
                RemoveHandler Me.squarePanel.Click, AddressOf squarePanel_Click
                RemoveHandler Me.circlePanel.Click, AddressOf circlePanel_Click
    
                If (components IsNot Nothing) Then
                    components.Dispose()
                End If
    
            End If
            MyBase.Dispose(disposing)
        End Sub
    
        ' LightShape is the property for which this control provides
        ' a custom user interface in the Properties window.
        Public Property LightShape() As MarqueeLightShape
    
            Get
                Return Me.lightShapeValue
            End Get
    
            Set(ByVal Value As MarqueeLightShape)
                If Me.lightShapeValue <> Value Then
                    Me.lightShapeValue = Value
                End If
            End Set
    
        End Property
    
        Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
            MyBase.OnPaint(e)
    
            Dim gCircle As Graphics = Me.circlePanel.CreateGraphics()
            Try
                Dim gSquare As Graphics = Me.squarePanel.CreateGraphics()
                Try
                    ' Draw a filled square in the client area of
                    ' the squarePanel control.
                    gSquare.FillRectangle( _
                    Brushes.Red, _
                    0, _
                    0, _
                    Me.squarePanel.Width, _
                    Me.squarePanel.Height)
    
                    ' If the Square option has been selected, draw a 
                    ' border inside the squarePanel.
                    If Me.lightShapeValue = MarqueeLightShape.Square Then
                        gSquare.DrawRectangle( _
                        Pens.Black, _
                        0, _
                        0, _
                        Me.squarePanel.Width - 1, _
                        Me.squarePanel.Height - 1)
                    End If
    
                    ' Draw a filled circle in the client area of
                    ' the circlePanel control.
                    gCircle.Clear(Me.circlePanel.BackColor)
                    gCircle.FillEllipse( _
                    Brushes.Blue, _
                    0, _
                    0, _
                    Me.circlePanel.Width, _
                    Me.circlePanel.Height)
    
                    ' If the Circle option has been selected, draw a 
                    ' border inside the circlePanel.
                    If Me.lightShapeValue = MarqueeLightShape.Circle Then
                        gCircle.DrawRectangle( _
                        Pens.Black, _
                        0, _
                        0, _
                        Me.circlePanel.Width - 1, _
                        Me.circlePanel.Height - 1)
                    End If
                Finally
                    gSquare.Dispose()
                End Try
            Finally
                gCircle.Dispose()
            End Try
        End Sub
    
        Private Sub squarePanel_Click( _
        ByVal sender As Object, _
        ByVal e As EventArgs)
    
            Me.lightShapeValue = MarqueeLightShape.Square
            Me.Invalidate(False)
            Me.editorService.CloseDropDown()
    
        End Sub
    
    
        Private Sub circlePanel_Click( _
        ByVal sender As Object, _
        ByVal e As EventArgs)
    
            Me.lightShapeValue = MarqueeLightShape.Circle
            Me.Invalidate(False)
            Me.editorService.CloseDropDown()
    
        End Sub
    
    #Region "Component Designer generated code"
    
        '/ <summary> 
        '/ Required method for Designer support - do not modify 
        '/ the contents of this method with the code editor.
        '/ </summary>
        Private Sub InitializeComponent()
            Me.squarePanel = New System.Windows.Forms.Panel
            Me.circlePanel = New System.Windows.Forms.Panel
            Me.SuspendLayout()
            ' 
            ' squarePanel
            ' 
            Me.squarePanel.Location = New System.Drawing.Point(8, 10)
            Me.squarePanel.Name = "squarePanel"
            Me.squarePanel.Size = New System.Drawing.Size(60, 60)
            Me.squarePanel.TabIndex = 2
            ' 
            ' circlePanel
            ' 
            Me.circlePanel.Location = New System.Drawing.Point(80, 10)
            Me.circlePanel.Name = "circlePanel"
            Me.circlePanel.Size = New System.Drawing.Size(60, 60)
            Me.circlePanel.TabIndex = 3
            ' 
            ' LightShapeSelectionControl
            ' 
            Me.Controls.Add(squarePanel)
            Me.Controls.Add(circlePanel)
            Me.Name = "LightShapeSelectionControl"
            Me.Size = New System.Drawing.Size(150, 80)
            Me.ResumeLayout(False)
        End Sub
    
    #End Region
    
    End Class
    
        // This control provides the custom UI for the LightShape property
        // of the MarqueeBorder. It is used by the LightShapeEditor.
        public class LightShapeSelectionControl : System.Windows.Forms.UserControl
        {
            private MarqueeLightShape lightShapeValue = MarqueeLightShape.Square;
            private IWindowsFormsEditorService editorService = null;
            private System.Windows.Forms.Panel squarePanel;
            private System.Windows.Forms.Panel circlePanel;
    
            // Required designer variable.
            private System.ComponentModel.Container components = null;
    
            // This constructor takes a MarqueeLightShape value from the
            // design-time environment, which will be used to display
            // the initial state.
            public LightShapeSelectionControl( 
                MarqueeLightShape lightShape,
                IWindowsFormsEditorService editorService )
            {
                // This call is required by the designer.
                InitializeComponent();
    
                // Cache the light shape value provided by the 
                // design-time environment.
                this.lightShapeValue = lightShape;
    
                // Cache the reference to the editor service.
                this.editorService = editorService;
    
                // Handle the Click event for the two panels. 
                this.squarePanel.Click += new EventHandler(squarePanel_Click);
                this.circlePanel.Click += new EventHandler(circlePanel_Click);
            }
    
            protected override void Dispose( bool disposing )
            {
                if( disposing )
                {
                    // Be sure to unhook event handlers
                    // to prevent "lapsed listener" leaks.
                    this.squarePanel.Click -= 
                        new EventHandler(squarePanel_Click);
                    this.circlePanel.Click -= 
                        new EventHandler(circlePanel_Click);
    
                    if(components != null)
                    {
                        components.Dispose();
                    }
                }
                base.Dispose( disposing );
            }
    
            // LightShape is the property for which this control provides
            // a custom user interface in the Properties window.
            public MarqueeLightShape LightShape
            {
                get
                {
                    return this.lightShapeValue;
                }
    
                set
                {
                    if( this.lightShapeValue != value )
                    {
                        this.lightShapeValue = value;
                    }
                }
            }
    
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint (e);
    
                using( 
                    Graphics gSquare = this.squarePanel.CreateGraphics(),
                    gCircle = this.circlePanel.CreateGraphics() )
                {   
                    // Draw a filled square in the client area of
                    // the squarePanel control.
                    gSquare.FillRectangle(
                        Brushes.Red, 
                        0,
                        0,
                        this.squarePanel.Width,
                        this.squarePanel.Height
                        );
    
                    // If the Square option has been selected, draw a 
                    // border inside the squarePanel.
                    if( this.lightShapeValue == MarqueeLightShape.Square )
                    {
                        gSquare.DrawRectangle( 
                            Pens.Black,
                            0,
                            0,
                            this.squarePanel.Width-1,
                            this.squarePanel.Height-1);
                    }
    
                    // Draw a filled circle in the client area of
                    // the circlePanel control.
                    gCircle.Clear( this.circlePanel.BackColor );
                    gCircle.FillEllipse( 
                        Brushes.Blue, 
                        0,
                        0,
                        this.circlePanel.Width, 
                        this.circlePanel.Height
                        );
    
                    // If the Circle option has been selected, draw a 
                    // border inside the circlePanel.
                    if( this.lightShapeValue == MarqueeLightShape.Circle )
                    {
                        gCircle.DrawRectangle( 
                            Pens.Black,
                            0,
                            0,
                            this.circlePanel.Width-1,
                            this.circlePanel.Height-1);
                    }
                }   
            }
    
            private void squarePanel_Click(object sender, EventArgs e)
            {
                this.lightShapeValue = MarqueeLightShape.Square;
    
                this.Invalidate( false );
    
                this.editorService.CloseDropDown();
            }
    
            private void circlePanel_Click(object sender, EventArgs e)
            {
                this.lightShapeValue = MarqueeLightShape.Circle;
    
                this.Invalidate( false );
    
                this.editorService.CloseDropDown();
            }
    
            #region Component Designer generated code
            /// <summary> 
            /// Required method for Designer support - do not modify 
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                this.squarePanel = new System.Windows.Forms.Panel();
                this.circlePanel = new System.Windows.Forms.Panel();
                this.SuspendLayout();
    // 
    // squarePanel
    // 
                this.squarePanel.Location = new System.Drawing.Point(8, 10);
                this.squarePanel.Name = "squarePanel";
                this.squarePanel.Size = new System.Drawing.Size(60, 60);
                this.squarePanel.TabIndex = 2;
    // 
    // circlePanel
    // 
                this.circlePanel.Location = new System.Drawing.Point(80, 10);
                this.circlePanel.Name = "circlePanel";
                this.circlePanel.Size = new System.Drawing.Size(60, 60);
                this.circlePanel.TabIndex = 3;
    // 
    // LightShapeSelectionControl
    // 
                this.Controls.Add(this.squarePanel);
                this.Controls.Add(this.circlePanel);
                this.Name = "LightShapeSelectionControl";
                this.Size = new System.Drawing.Size(150, 80);
                this.ResumeLayout(false);
    
            }
            #endregion
    
    
        }
    

定義 UI 型別編輯器類別

若要實作 UI 型別編輯器行為,請從 UITypeEditor 基底類別衍生。這個類別稱為 LightShapeEditor。

若要定義 UI 型別編輯器類別

  1. 透過參考 System.Design 組件,並匯入 System.Drawing.DesignSystem.Windows.Forms.Design 命名空間,啟用 .NET Framework 設計階段支援的存取權。如需詳細資訊,請參閱 HOW TO:在 Windows Form 中存取設計階段支援

  2. 在您的 Windows Form 控制項定義主體中,定義 LightShapeEditor 類別。

    ' This class demonstrates the use of a custom UITypeEditor. 
    ' It allows the MarqueeBorder control's LightShape property
    ' to be changed at design time using a customized UI element
    ' that is invoked by the Properties window. The UI is provided
    ' by the LightShapeSelectionControl class.
    Friend Class LightShapeEditor
        Inherits UITypeEditor
    
    // This class demonstrates the use of a custom UITypeEditor. 
    // It allows the MarqueeBorder control's LightShape property
    // to be changed at design time using a customized UI element
    // that is invoked by the Properties window. The UI is provided
    // by the LightShapeSelectionControl class.
    internal class LightShapeEditor : UITypeEditor
    {
    

覆寫 GetEditStyle 方法

GetEditStyle 方法向設計環境指出您的 UI 型別編輯器實作了何種使用者介面。可能的值定義在 UITypeEditorEditStyle 型別中。LightShapeEditor 實作一個 DropDown UI 型別編輯器。

若要覆寫 GetEditStyle 方法

  • 在 LightShapeEditor 定義的主體中,覆寫 GetEditStyle 方法以傳回 DropDown

    Public Overrides Function GetEditStyle( _
    ByVal context As System.ComponentModel.ITypeDescriptorContext) _
    As UITypeEditorEditStyle
        Return UITypeEditorEditStyle.DropDown
    End Function
    
    
    public override UITypeEditorEditStyle GetEditStyle(
    System.ComponentModel.ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.DropDown;
    }
    

覆寫 EditValue 方法

EditValue 方法建立了設計環境與使用者介面之間的互動,以供編輯您的自訂型別。EditValue 方法會建立檢視控制項的執行個體或強制回應對話方塊 (Modal Dialog Box),使用者可用來編輯值。當使用者完成編輯時,EditValue 方法會傳回值至設計環境。

在像 LightShapeSelectionControl 檢視控制項的情形下,EditValue 方法可以傳送 IWindowsFormsEditorService 的參考給檢視控制項。此檢視控制項可以使用這個參考,在使用者選取值時,將本身關閉。這對強制回應對話方塊並非必要,因為表單本身可以自行關閉。

若要覆寫 EditValue 方法

  • 在 LightShapeEditor 定義的主體中,覆寫 EditValue 方法。

    Public Overrides Function EditValue( _
    ByVal context As ITypeDescriptorContext, _
    ByVal provider As IServiceProvider, _
    ByVal value As Object) As Object
        If (provider IsNot Nothing) Then
            editorService = _
            CType(provider.GetService(GetType(IWindowsFormsEditorService)), _
            IWindowsFormsEditorService)
        End If
    
        If (editorService IsNot Nothing) Then
            Dim selectionControl As _
            New LightShapeSelectionControl( _
            CType(value, MarqueeLightShape), _
            editorService)
    
            editorService.DropDownControl(selectionControl)
    
            value = selectionControl.LightShape
        End If
    
        Return value
    End Function
    
    public override object EditValue(
        ITypeDescriptorContext context,
        IServiceProvider provider,
        object value)
    {
        if (provider != null)
        {
            editorService =
                provider.GetService(
                typeof(IWindowsFormsEditorService))
                as IWindowsFormsEditorService;
        }
    
        if (editorService != null)
        {
            LightShapeSelectionControl selectionControl =
                new LightShapeSelectionControl(
                (MarqueeLightShape)value,
                editorService);
    
            editorService.DropDownControl(selectionControl);
    
            value = selectionControl.LightShape;
        }
    
        return value;
    }
    

覆寫 PaintValue 方法

您可以透過覆寫 PaintValue 方法,提供屬性值的圖形表示。

若要覆寫 PaintValue 方法

  • 在 LightShapeEditor 定義的主體中,覆寫 PaintValue 方法。同時也覆寫 GetPaintValueSupported 方法,以傳回 true。

    ' This method indicates to the design environment that
    ' the type editor will paint additional content in the
    ' LightShape entry in the PropertyGrid.
    Public Overrides Function GetPaintValueSupported( _
    ByVal context As ITypeDescriptorContext) As Boolean
    
        Return True
    
    End Function
    
    ' This method paints a graphical representation of the 
    ' selected value of the LightShpae property.
    Public Overrides Sub PaintValue( _
    ByVal e As PaintValueEventArgs)
    
        Dim shape As MarqueeLightShape = _
        CType(e.Value, MarqueeLightShape)
        Using p As Pen = Pens.Black
    
            If shape = MarqueeLightShape.Square Then
    
                e.Graphics.DrawRectangle(p, e.Bounds)
    
            Else
    
                e.Graphics.DrawEllipse(p, e.Bounds)
    
            End If
    
        End Using
    
    End Sub
    
    // This method indicates to the design environment that
    // the type editor will paint additional content in the
    // LightShape entry in the PropertyGrid.
    public override bool GetPaintValueSupported(
        ITypeDescriptorContext context)
    {  
        return true;
    }
    
    // This method paints a graphical representation of the 
    // selected value of the LightShpae property.
    public override void PaintValue(PaintValueEventArgs e)
    {   
        MarqueeLightShape shape = (MarqueeLightShape)e.Value;
        using (Pen p = Pens.Black)
        {
            if (shape == MarqueeLightShape.Square)
            {
                e.Graphics.DrawRectangle(p, e.Bounds);
            }
            else
            {
                e.Graphics.DrawEllipse(p, e.Bounds);
            }
        }   
    }
    

附加您的 UI 型別編輯器至屬性

當您的 UI 型別編輯器已準備就緒,可供您的自訂控制項使用時,將 LightShapeEditor 附加至屬性,根據 MarqueeLightShape 型別實作屬性,並套用 EditorAttribute 至此屬性。

若要附加您的 UI 型別編輯器至屬性

  • 在您的控制項定義主體中,宣告命名為 LightShape 的 MarqueeLightShape 屬性。同時也宣告型別 MarqueeLightShape 的執行個體欄位 (命名為 lightShapeValue),以支持此屬性。套用 EditorAttribute 至此屬性。
Private lightShapeValue As MarqueeLightShape

<Category("Marquee"), _
Browsable(True), _
EditorAttribute(GetType(LightShapeEditor), _
GetType(System.Drawing.Design.UITypeEditor))> _
Public Property LightShape() As MarqueeLightShape
    Get
        Return Me.lightShapeValue
    End Get
    Set(ByVal value As MarqueeLightShape)
        Me.lightShapeValue = value
    End Set
End Property
private MarqueeLightShape lightShapeValue;

[Category("Marquee")]
[Browsable(true)]
[EditorAttribute(typeof(LightShapeEditor),
typeof(System.Drawing.Design.UITypeEditor))]
public MarqueeLightShape LightShape
{
    get
    {
        return this.lightShapeValue;
    }

    set
    {
        this.lightShapeValue = value;
    }
}

測試您的 UI 型別編輯器

您可以透過使用 SelectedObject 屬性,建立自訂控制項的執行個體,並附加至 PropertyGrid 控制項,測試您的 UI 型別編輯器。

如果您是使用 Visual Studio,則可以建立新的 Windows 應用程式專案、參考您的控制項組件,並加入控制項的執行個體至表單。Visual Studio 中對此工作有相當廣泛的支援。

當控制項的屬性在設計階段顯示時,您可以選取 LightShape 屬性。選取時,隨即顯示下拉箭號 (屬性視窗向下鍵)。在箭號上按一下時,您的檢視控制項就會顯示在屬性項目之下。在圓圈或方塊上按一下,以選取該值。按一下之後,檢視控制項本身會自行關閉,而您所選取的值也會顯示在 PropertyGrid 中。

注意事項:

當您開發自訂 UITypeEditor 時,建議將組建編號設為依每個組建遞增。這會防止在設計環境中建立快取的舊版本 UITypeEditor

後續步驟

撰寫了您自己的 UI 型別編輯器之後,請探索其他方法,以與 PropertyGrid 和設計環境互動:

請參閱

工作

HOW TO:建立採用設計階段功能的 Windows Form 控制項

參考

UITypeEditor

EditorAttribute

PropertyGrid

其他資源

使用者介面型別編輯器