Tutorial: Agregar etiquetas inteligentes a un componente de formularios Windows Forms
Las etiquetas inteligentes son elementos de interfaz de usuario de tipo menú que proporcionan opciones en tiempo de diseño que se utilizan frecuentemente. La mayoría de los componentes y controles estándar proporcionados con .NET Framework contienen mejoras por lo que respecta a las etiquetas inteligentes y a los verbos de diseñador. En los procedimientos de este tutorial se muestra cómo agregar la compatibilidad con etiquetas inteligentes a los componentes y controles personalizados.
Puede agregar etiquetas inteligentes a componentes de formularios Windows Forms para proporcionar opciones en tiempo de diseño que se utilizan con frecuencia. En un panel de etiquetas inteligentes, los elementos se agrupan de forma lógica por categoría, y es posible duplicar instancias DesignerActionMethodItem como entradas de verbo de diseñador. Muchos de los componentes y controles estándar proporcionados con .NET Framework contienen mejoras por lo que respecta a las etiquetas inteligentes y a los verbos de diseñador. Los autores de componentes y controles personalizados también pueden agregar compatibilidad con etiquetas inteligentes; por lo general, mediante el modelo de inserción.
Para agregar etiquetas inteligentes con el modelo de inserción es necesario realizar las siguientes adiciones al proyecto de componentes:
Implementación de una clase, derivada de DesignerActionList, que define los métodos y las propiedades que son destinos de los elementos de menú de etiquetas inteligentes. Esta clase también puede proporcionar un método GetSortedActionItems reemplazado que devuelva una matriz de instancias DesignerActionItem.
La clase de diseñador asociada al componente debe implementar la propiedad ActionLists. Cuando se recupera esta propiedad, se proporciona la colección DesignerActionListCollection que contiene todas las instancias DesignerActionList asociadas a un único menú de etiquetas inteligentes. A menudo, sólo hay una lista en este tipo de colección.
Nota
Los paneles de etiquetas inteligentes no admiten el desplazamiento ni el paginando, por lo que es necesario tener cuidado para no rellenar los paneles con muchos elementos de etiquetas inteligentes. Demasiados elementos pueden hacer que el panel de etiquetas inteligentes se extienda más allá de los límites de la pantalla.
En el siguiente procedimiento se muestra cómo agregar etiquetas inteligentes mediante código a partir de un control de ejemplo sencillo, ColorLabel, que deriva del control estándar de formularios Windows Forms Label. Este control tiene un diseñador asociado denominado ColorLabelDesigner.
Para copiar el código de este tema como un listado sencillo, vea Cómo: Asociar etiquetas inteligentes a un componente de formularios Windows Forms.
Requisitos previos
Para poder completar este tutorial, necesitará:
- Permisos suficientes para poder crear y ejecutar proyectos de aplicación de Windows Forms en el equipo donde esté instalado .NET Framework.
Para implementar una clase derivada de DesignerActionList
En el mismo espacio de nombres que el componente, agregue la declaración para su clase derivada de DesignerActionList.
Nota
Debe agregar una referencia al ensamblado en tiempo de diseño, System.Design.dll. Este ensamblado no está incluido en .NET Framework 4 Client Profile. Para agregar una referencia a System.Design.dll, debe cambiar la versión de .NET Framework de destino del proyecto a .NET Framework 4.
Public Class ColorLabelActionList Inherits System.ComponentModel.Design.DesignerActionList
public class ColorLabelActionList : System.ComponentModel.Design.DesignerActionList
Agregue a esta clase un constructor que tome una instancia del control asociado. Proporcione un campo privado para que contenga una referencia a esta instancia. Proporcione también un campo privado para almacenar en memoria caché una referencia a DesignerActionService. Se utilizará para actualizar la lista.
Private colLabel As ColorLabel ... Private designerActionUISvc As DesignerActionUIService = Nothing ... Public Sub New(ByVal component As IComponent) MyBase.New(component) Me.colLabel = component ' Cache a reference to DesignerActionUIService, so the ' DesigneractionList can be refreshed. Me.designerActionUISvc = _ CType(GetService(GetType(DesignerActionUIService)), _ DesignerActionUIService) End Sub
private ColorLabel colLabel; ... private DesignerActionUIService designerActionUISvc = null; ... public ColorLabelActionList( IComponent component ) : base(component) { this.colLabel = component as ColorLabel; // Cache a reference to DesignerActionUIService, so the // DesigneractionList can be refreshed. this.designerActionUISvc = GetService(typeof(DesignerActionUIService)) as DesignerActionUIService; }
Agregue métodos y propiedades que desee asociar a los elementos de etiqueta inteligente. Los métodos se ejecutarán cuando se seleccione la entrada de etiqueta inteligente correspondiente. Las propiedades deben tener secciones de captador para que se muestren sus valores actuales; opcionalmente, pueden tener secciones de establecedor que utilicen el método GetProperties si tiene que existir la posibilidad de modificar sus valores desde la entrada de etiqueta inteligente correspondiente.
Nota
Al igual que ocurre en todo el entorno en tiempo de diseño, una propiedad sólo se puede modificar si .NET Framework proporciona uno de los tipos base, el tipo se puede convertir en tipo base mediante un objeto TypeConverter suministrado, o se suministra un objeto UITypeEditor personalizado.
Public Property ForeColor() As Color Get Return colLabel.ForeColor End Get Set(ByVal value As Color) GetPropertyByName("ForeColor").SetValue(colLabel, value) End Set End Property ... 'Boolean properties are automatically displayed with binary ' UI (such as a checkbox). Public Property LockColors() As Boolean Get Return colLabel.ColorLocked End Get Set(ByVal value As Boolean) GetPropertyByName("ColorLocked").SetValue(colLabel, value) ' Refresh the list. Me.designerActionUISvc.Refresh(Me.Component) End Set End Property ... Public Sub InvertColors() Dim currentBackColor As Color = colLabel.BackColor BackColor = Color.FromArgb( _ 255 - currentBackColor.R, _ 255 - currentBackColor.G, _ 255 - currentBackColor.B) Dim currentForeColor As Color = colLabel.ForeColor ForeColor = Color.FromArgb( _ 255 - currentForeColor.R, _ 255 - currentForeColor.G, _ 255 - currentForeColor.B) End Sub
public Color ForeColor { get { return colLabel.ForeColor; } set { GetPropertyByName("ForeColor").SetValue(colLabel, value); } } ... // Boolean properties are automatically displayed with binary // UI (such as a checkbox). public bool LockColors { get { return colLabel.ColorLocked; } set { GetPropertyByName("ColorLocked").SetValue(colLabel, value); // Refresh the list. this.designerActionUISvc.Refresh(this.Component); } } ... public void InvertColors() { Color currentBackColor = colLabel.BackColor; BackColor = Color.FromArgb( 255 - currentBackColor.R, 255 - currentBackColor.G, 255 - currentBackColor.B); Color currentForeColor = colLabel.ForeColor; ForeColor = Color.FromArgb( 255 - currentForeColor.R, 255 - currentForeColor.G, 255 - currentForeColor.B); }
Opcionalmente, puede implementar una versión reemplazada del método GetSortedActionItems para que se devuelva una matriz de instancias de DesignerActionItem, en la que cada elemento esté asociado a una propiedad o a un método creados en el paso anterior. Puede realizar esta operación para cambiar el orden de los elementos, asignarles categorías u, opcionalmente, hacer que se muestren. La lista también puede incluir elementos estáticos, como títulos de grupos lógicos.
Public Overrides Function GetSortedActionItems() _ As DesignerActionItemCollection Dim items As New DesignerActionItemCollection() 'Define static section header entries. items.Add(New DesignerActionHeaderItem("Appearance")) items.Add(New DesignerActionHeaderItem("Information")) 'Boolean property for locking color selections. items.Add(New DesignerActionPropertyItem( _ "LockColors", _ "Lock Colors", _ "Appearance", _ "Locks the color properties.")) If Not LockColors Then items.Add( _ New DesignerActionPropertyItem( _ "BackColor", _ "Back Color", _ "Appearance", _ "Selects the background color.")) items.Add( _ New DesignerActionPropertyItem( _ "ForeColor", _ "Fore Color", _ "Appearance", _ "Selects the foreground color.")) 'This next method item is also added to the context menu ' (as a designer verb). items.Add( _ New DesignerActionMethodItem( _ Me, _ "InvertColors", _ "Invert Colors", _ "Appearance", _ "Inverts the fore and background colors.", _ True)) End If items.Add( _ New DesignerActionPropertyItem( _ "Text", _ "Text String", _ "Appearance", _ "Sets the display text.")) 'Create entries for static Information section. Dim location As New StringBuilder("Location: ") location.Append(colLabel.Location) Dim size As New StringBuilder("Size: ") size.Append(colLabel.Size) items.Add( _ New DesignerActionTextItem( _ location.ToString(), _ "Information")) items.Add( _ New DesignerActionTextItem( _ size.ToString(), _ "Information")) Return items End Function
public override DesignerActionItemCollection GetSortedActionItems() { DesignerActionItemCollection items = new DesignerActionItemCollection(); //Define static section header entries. items.Add(new DesignerActionHeaderItem("Appearance")); items.Add(new DesignerActionHeaderItem("Information")); //Boolean property for locking color selections. items.Add(new DesignerActionPropertyItem("LockColors", "Lock Colors", "Appearance", "Locks the color properties.")); if (!LockColors) { items.Add(new DesignerActionPropertyItem("BackColor", "Back Color", "Appearance", "Selects the background color.")); items.Add(new DesignerActionPropertyItem("ForeColor", "Fore Color", "Appearance", "Selects the foreground color.")); //This next method item is also added to the context menu // (as a designer verb). items.Add(new DesignerActionMethodItem(this, "InvertColors", "Invert Colors", "Appearance", "Inverts the fore and background colors.", true)); } items.Add(new DesignerActionPropertyItem("Text", "Text String", "Appearance", "Sets the display text.")); //Create entries for static Information section. StringBuilder location = new StringBuilder("Location: "); location.Append(colLabel.Location); StringBuilder size = new StringBuilder("Size: "); size.Append(colLabel.Size); items.Add(new DesignerActionTextItem(location.ToString(), "Information")); items.Add(new DesignerActionTextItem(size.ToString(), "Information")); return items; }
Para actualizar la clase de diseñador asociada con el fin de implementar la propiedad ActionLists
Busque la clase de diseñador para el control. Si no existe, cree una clase de diseñador y asóciela a la clase de control. Para obtener más información sobre diseñadores, vea Clases base del diseñador.
Como técnica de optimización, agregue un campo privado de tipo DesignerActionListCollection.
Private lists As DesignerActionListCollection
private DesignerActionListCollection actionLists;
Agregue la propiedad ActionLists reemplazada para que se devuelva una nueva instancia de la clase ColorLabelActionList que creó anteriormente.
Public Overrides ReadOnly Property ActionLists() _ As DesignerActionListCollection Get If lists Is Nothing Then lists = New DesignerActionListCollection() lists.Add( _ New ColorLabelActionList(Me.Component)) End If Return lists End Get End Property
public override DesignerActionListCollection ActionLists { get { if (null == actionLists) { actionLists = new DesignerActionListCollection(); actionLists.Add( new ColorLabelActionList(this.Component)); } return actionLists; } }
Comentarios
Hay varias áreas de código que merecen una explicación más detallada:
Cuando una propiedad o un método de la clase que se deriva de DesignerActionList cambia el estado del control asociado, los cambios no deben ser realizados por llamadas directas de establecedores a las propiedades del componente. En su lugar, los cambios se deben realizar mediante un objeto PropertyDescriptor debidamente creado. Este método indirecto garantiza que funcionen correctamente las actualizaciones de la interfaz de usuario y las acciones de deshacer etiquetas inteligentes.
Puede actualizar dinámicamente el panel de etiquetas inteligentes llamando a DesignerActionUIService.Refresh. Este proceso se puede utilizar para cambiar dinámicamente el contenido del panel de etiquetas inteligentes. En el ejemplo, las etiquetas inteligentes relacionadas con los cambios de color se incluyen condicionalmente, según el estado de la propiedad LockColors. Esta propiedad Boolean también esta asociada a una etiqueta inteligente, de modo que el desarrollador puede bloquear o desbloquear la selección de colores actual, al menos a través del menú.
Se puede incluir una entrada de etiqueta inteligente de tipo DesignerActionMethodItem en el menú contextual para el control asociado si se establece el parámetro includeAsDesignerVerb del constructor como true. A continuación, .NET Framework crea implícitamente un objeto DesignerVerb correspondiente y lo agrega al menú contextual automáticamente. En este ejemplo, se trata así al elemento InvertColors.
Los elementos de etiqueta inteligente se agrupan en un panel por su propiedad Category, que se establece en el constructor para cada elemento. Si no se establece esta propiedad explícitamente, se asigna a la categoría predeterminada. Los elementos se ordenan en el panel de etiquetas inteligentes por categorías y por orden de aparición en la matriz DesignerActionItem devuelta por la clase derivada de la clase DesignerActionList. Este ejemplo contiene dos categorías: Appearance y Information.
Nota
No se proporciona ningún objeto DesignerActionHeaderItem para la segunda categoría.
Se puede implementar una entrada que muestre información de texto estático mediante un objeto DesignerActionTextItem o un objeto DesignerActionPropertyItem cuya propiedad asociada sólo contiene un establecedor. En este ejemplo se utiliza el método anterior.
Pasos siguientes
Cuando ha iniciado la integración del componente en el entorno en tiempo de diseño, considere la posibilidad de expandir su compatibilidad con diseñadores.
Agregue atributos a los miembros para facilitar la comunicación con el entorno en tiempo de diseño. Para obtener más información, vea Atributos en controles de formularios Windows Forms.
Escriba su propio diseñador 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.
Vea también
Referencia
Conceptos
Comandos del diseñador y modelo de objetos de DesignerAction para formularios Windows Forms