Tutorial: Implementar un editor de tipos con interfaz de usuario

Actualización: noviembre 2007

Puede proporcionar una experiencia personalizada en tiempo de diseño para tipos complejos de propiedades implementando un editor de tipos con interfaz de usuario (IU).

En este tutorial se explica la forma de crear su propio editor de tipos con interfaz de usuario para un tipo personalizado y la forma de mostrar la interfaz de edición mediante el uso de PropertyGrid.

Entre las tareas que se explican en este tutorial se incluyen las siguientes:

  • Definir un tipo personalizado.

  • Definir un control de vista para el editor de tipos con interfaz de usuario.

  • Definir una clase derivada de UITypeEditor.

  • Reemplazar el método GetEditStyle para informar a PropertyGrid del tipo de estilo de editor que va a utilizar el editor.

  • Reemplazar el método EditValue para controlar la interfaz de usuario, el procesamiento de los datos proporcionados por el usuario y la asignación de valores.

Para copiar el código de este tutorial como un listado sencillo, vea Cómo: Crear un control de formularios Windows Forms que aproveche las características en tiempo de diseño.

Requisitos previos

Para poder completar este tutorial, necesitará:

  • Permisos suficientes para poder crear y ejecutar proyectos de aplicaciones de Windows Forms en el equipo donde esté instalado .NET Framework.

Definir un tipo personalizado

El editor de tipos con interfaz de usuario personalizado mostrará un tipo personalizado. Este tipo puede ser simple o complejo. En este tutorial, definirá un tipo simple con un comportamiento de edición personalizado en tiempo de diseño. Este tipo se denomina MarqueeLightShape y es una enum con dos valores, Square y Circle.

Para definir un tipo de enumeración personalizado

  • En el cuerpo de la definición del control de Windows Forms, defina el tipo 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
    }
    

Definir un control de vista

El editor de tipos personalizado con interfaz de usuario muestra la interfaz de edición mediante un control de Windows Forms. Este control se denomina LightShapeSelectionControl y se deriva de UserControl. Su constructor toma el valor actual de la propiedad y una referencia a IWindowsFormsEditorService. El control de vista utiliza el método CloseDropDown en IWindowsFormsEditorService para cerrar la ventana desplegable cuando el usuario hace clic en una selección.

Para definir un control de vista

  • En el cuerpo de la definición del control de Windows Forms, defina el control 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
    
    
        }
    

Definir una clase de editor de tipos con interfaz de usuario

Para implementar el comportamiento de un editor de tipos con interfaz de usuario, derive de la clase base UITypeEditor. Esta clase se denomina LightShapeEditor.

Para definir una clase de editor de tipos con interfaz de usuario

  1. Habilite el acceso a la compatibilidad en tiempo de diseño de .NET Framework haciendo referencia al ensamblado System.Design e importando los espacios de nombres System.Drawing.Design y System.Windows.Forms.Design. Para obtener más información, vea Cómo: Obtener acceso a las funciones en tiempo de diseño de formularios Windows Forms.

  2. En el cuerpo de la definición del control de Windows Forms, defina la clase 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
    {
    

Reemplazar el método GetEditStyle

El método GetEditStyle indica al entorno de diseño qué tipo de interfaz de usuario implementa el editor de tipos con interfaz de usuario. Los valores posibles se definen en el tipo UITypeEditorEditStyle. LightShapeEditor implementa un editor de tipos con interfaz de usuario DropDown.

Para reemplazar el método GetEditStyle

  • En el cuerpo de la definición de LightShapeEditor, reemplace el método GetEditStyle para devolver 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;
    }
    

Reemplazar el método EditValue

El método EditValue establece la interacción entre el entorno de diseño y una interfaz de usuario para editar el tipo personalizado. El método EditValue crea una instancia del control de vista o del cuadro de diálogo modal con el que el usuario edita el valor. Cuando el usuario finaliza la edición, el método EditValue devuelve el valor al entorno de diseño.

En el caso de un control de vista como LightShapeSelectionControl, el método EditValue puede pasar una referencia a IWindowsFormsEditorService al control de vista. El control de vista puede utilizar esta referencia para cerrarse cuando usuario selecciona un valor. Esto no es necesario en el caso de un cuadro de diálogo modal, ya que un formulario puede cerrarse por sí solo.

Para reemplazar el método EditValue

  • En el cuerpo de la definición de LightShapeEditor, reemplace el método 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;
    }
    

Reemplazar el método PaintValue

Puede proporcionar una representación gráfica del valor de la propiedad reemplazando el método PaintValue.

Para reemplazar el método PaintValue

  • En el cuerpo de la definición de LightShapeEditor, reemplace el método PaintValue. Reemplace también el método GetPaintValueSupported para que devuelva 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);
            }
        }   
    }
    

Anexar el editor de tipos con interfaz de usuario a una propiedad

Cuando el editor de tipos con interfaz de usuario esté listo para su uso en el control personalizado, anexe LightShapeEditor a una propiedad, implemente la propiedad basándose en el tipo MarqueeLightShape y aplique EditorAttribute a la propiedad.

Para anexar el editor de tipos con interfaz de usuario a una propiedad

  • En el cuerpo de la definición del control, declare una propiedad MarqueeLightShape denominada LightShape. Declare también un campo de instancia denominado lightShapeValue de tipo MarqueeLightShape para complementar la propiedad. Aplique el atributo EditorAttribute a la propiedad.
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;
    }
}

Probar el editor de tipos con interfaz de usuario

Puede probar el editor de tipos con interfaz de usuario creando una instancia del control personalizado y anexándola a un control PropertyGrid mediante la propiedad SelectedObject.

Si utiliza Visual Studio, puede crear un nuevo proyecto de aplicación para Windows, hacer referencia al ensamblado del control y agregar una instancia del control al formulario. Visual Studio ofrece una amplia compatibilidad para esta tarea.

Cuando las propiedades del control se muestren en tiempo de diseño, podrá seleccionar la propiedad LightShape. Una vez seleccionada, aparecerá una flecha de lista desplegable (). Cuando haga clic en la flecha, el control de vista aparecerá bajo la entrada de la propiedad. Haga clic en el círculo o cuadrado para seleccionar el valor. Después de hacer clic, el control de vista se descarta y el valor seleccionado aparece en PropertyGrid.

Nota:

Al desarrollar el UITypeEditor personalizado, se recomienda que establezca el número de versión para que se incremente con cada generación. De esta forma evita que las versión anteriores almacenadas en caché del UITypeEditor se creen en el entorno de diseño.

Pasos siguientes

Una vez que haya creado su propio editor de tipos con interfaz de usuario, busque otras formas de interactuar con PropertyGrid y el entorno de diseño:

Vea también

Tareas

Cómo: Crear un control de formularios Windows Forms que aproveche las características en tiempo de diseño

Referencia

UITypeEditor

EditorAttribute

PropertyGrid

Otros recursos

Editores de tipos de interfaz de usuario