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
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.
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:
Escriba un editor de tipos con interfaz de usuario basado en un cuadro de diálogo modal en lugar de en un control de vista.
Escriba un convertidor de tipos para un tipo personalizado utilizando la clase TypeConverter. Para obtener más información, vea Cómo: Implementar un convertidor de tipos.
Escriba un diseñador para su control personalizado. Para obtener más información, vea Cómo: Crear un control de formularios Windows Forms que aproveche las características en tiempo de diseño.