Tutorial: Crear un proveedor de menús
En este tutorial se muestra cómo crear un proveedor de menú en tiempo de diseño para un control personalizado de Windows Presentation Foundation (WPF). Puede utilizar este elemento de menú contextual para establecer el valor de la propiedad Background en un control de botón personalizado. Para obtener el código completo, vea el ejemplo Context Menu Provider en el sitio WPF Designer Extensibility Samples.
En este tutorial realizará las siguientes tareas:
Crear un proyecto de biblioteca de controles personalizados de WPF.
Crear un ensamblado independiente para los metadatos en tiempo de diseño.
Implementar el proveedor de menú.
Utilizar el control en tiempo de diseño.
Cuando haya finalizado, sabrá crear un proveedor de menú para un control personalizado.
Nota
Los cuadros de diálogo y comandos de menú que se ven pueden diferir de los descritos en la Ayuda, en función de los valores de configuración o de edición activos. Para cambiar la configuración, elija la opción Importar y exportar configuraciones del menú Herramientas. Para obtener más información, vea Trabajar con valores de configuración.
Requisitos previos
Necesita los componentes siguientes para completar este tutorial:
- Visual Studio 2010.
Crear el control personalizado
El primer paso consiste en crear el proyecto para el control personalizado. El control es un botón simple con poco código en tiempo de diseño, que utiliza el método GetIsInDesignMode para implementar un comportamiento en tiempo de diseño.
Para crear el control personalizado
Cree un nuevo proyecto de biblioteca de controles personalizados de WPF en Visual Basic o Visual C# denominado CustomControlLibrary.
El código de CustomControl1 se abre en el Editor de código.
En el Explorador de soluciones, cambie el nombre del archivo de código a ButtonWithDesignTime.cs o ButtonWithDesignTime.vb. Si se muestra un cuadro de mensaje que le pregunta si desea cambiar el nombre de todas las referencias de este proyecto, haga clic en Sí.
Abra ButtonWithDesignTime.cs o ButtonWithDesignTime.vb en el Editor de código.
Reemplace el código generado automáticamente por el código siguiente. Este código hereda de Button y muestra el texto "Design mode active" cuando el botón aparece en el diseñador.
Imports System Imports System.Collections.Generic Imports System.Text Imports System.Windows.Controls Imports System.Windows.Media Imports System.ComponentModel Public Class ButtonWithDesignTime Inherits Button Public Sub New() ' The GetIsInDesignMode check and the following design-time ' code are optional and shown only for demonstration. If DesignerProperties.GetIsInDesignMode(Me) Then Content = "Design mode active" End If End Sub End Class
using System; using System.Collections.Generic; using System.Text; using System.Windows.Controls; using System.Windows.Media; using System.ComponentModel; namespace CustomControlLibrary { public class ButtonWithDesignTime : Button { public ButtonWithDesignTime() { // The GetIsInDesignMode check and the following design-time // code are optional and shown only for demonstration. if (DesignerProperties.GetIsInDesignMode(this)) { Content = "Design mode active"; } } } }
Establezca la ruta de acceso de los resultados del proyecto en "bin\".
Genere la solución.
Crear el ensamblado de metadatos en tiempo de diseño
El código en tiempo de diseño se implementa en ensamblados de metadatos especiales. Para este tutorial, la implementación de menú contextual se implementa en un ensamblado denominado a CustomControlLibrary.VisualStudio.Design. Para obtener más información, vea Proporcionar metadatos en tiempo de diseño.
Para crear el ensamblado de metadatos en tiempo de diseño
Agregue un nuevo proyecto de biblioteca de clases en Visual Basic o Visual C# denominado CustomControlLibrary.VisualStudio.Design a la solución.
Establezca la ruta de acceso de salida del proyecto en ".. \CustomControlLibrary\bin\". De este modo, el ensamblado del control y el ensamblado de metadatos permanecen en la misma carpeta, lo que permite que los diseñadores detecten los metadatos.
Agregue referencias a los siguientes ensamblados de WPF.
PresentationCore
PresentationFramework
System.Xaml
WindowsBase
Agregue referencias a los siguientes ensamblados de WPF Designer.
Microsoft.Windows.Design.Extensibility
Microsoft.Windows.Design.Interaction
Agregue una referencia al proyecto CustomControlLibrary.
En el Explorador de soluciones, cambie el nombre del archivo de código Class1 a Metadata.cs o Metadata.vb. Si se muestra un cuadro de mensaje que le pregunta si desea cambiar el nombre de todas las referencias de este proyecto, haga clic en Sí.
Reemplace el código generado automáticamente por el código siguiente. Este código crea un objeto AttributeTable que asocia la implementación en tiempo de diseño personalizada a la clase ButtonWithDesignTime.
Imports System Imports System.Collections.Generic Imports System.Text Imports System.ComponentModel Imports System.Windows.Media Imports System.Windows.Controls Imports System.Windows Imports CustomControlLibrary Imports Microsoft.Windows.Design.Features Imports Microsoft.Windows.Design.Metadata 'Imports CustomControlLibrary.VisualStudio.Design.Slid ' The ProvideMetadata assembly-level attribute indicates to designers ' that this assembly contains a class that provides an attribute table. <Assembly: ProvideMetadata(GetType(CustomControlLibrary.VisualStudio.Design.Metadata))> ' Container for any general design-time metadata to initialize. ' Designers look for a type in the design-time assembly that ' implements IProvideAttributeTable. If found, designers instantiate ' this class and access its AttributeTable property automatically. Friend Class Metadata Implements IProvideAttributeTable ' Accessed by the designer to register any design-time metadata. Public ReadOnly Property AttributeTable() As AttributeTable _ Implements IProvideAttributeTable.AttributeTable Get Dim builder As New AttributeTableBuilder() ' Add the menu provider to the design-time metadata. builder.AddCustomAttributes(GetType(ButtonWithDesignTime), _ New FeatureAttribute(GetType(CustomContextMenuProvider))) Return builder.CreateTable() End Get End Property End Class
using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; using System.Windows.Media; using System.Windows.Controls; using System.Windows; using CustomControlLibrary; using Microsoft.Windows.Design.Features; using Microsoft.Windows.Design.Metadata; using CustomControlLibrary.VisualStudio.Design; // The ProvideMetadata assembly-level attribute indicates to designers // that this assembly contains a class that provides an attribute table. [assembly: ProvideMetadata(typeof(CustomControlLibrary.VisualStudio.Design.Metadata))] namespace CustomControlLibrary.VisualStudio.Design { // Container for any general design-time metadata to initialize. // Designers look for a type in the design-time assembly that // implements IProvideAttributeTable. If found, designers instantiate // this class and access its AttributeTable property automatically. internal class Metadata : IProvideAttributeTable { // Accessed by the designer to register any design-time metadata. public AttributeTable AttributeTable { get { AttributeTableBuilder builder = new AttributeTableBuilder(); // Add the menu provider to the design-time metadata. builder.AddCustomAttributes( typeof(ButtonWithDesignTime), new FeatureAttribute(typeof(CustomContextMenuProvider))); return builder.CreateTable(); } } } }
Guarde la solución.
Implementar el proveedor de menú
El proveedor de menú se implementa en un tipo denominado CustomContextMenuProvider. El objeto MenuAction proporcionado permite establecer la propiedad Background del control en tiempo de diseño.
Para implementar el proveedor de menú
Agregue una nueva clase denominada CustomContextMenuProvider al proyecto CustomControlLibrary.VisualStudio.Design.
En el Editor de código para CustomContextMenuProvider, reemplace el código generado automáticamente con el código siguiente. Este código implementa una clase PrimarySelectionContextMenuProvider que proporciona una clase MenuAction personalizada.
Imports System Imports System.Collections.Generic Imports System.Text Imports Microsoft.Windows.Design.Interaction Imports System.Windows Imports Microsoft.Windows.Design.Model Imports System.Windows.Controls Imports System.Windows.Media ' The CustomContextMenuProvider class provides two context menu items ' at design time. These are implemented with the MenuAction class. Class CustomContextMenuProvider Inherits PrimarySelectionContextMenuProvider Private setBackgroundToBlueMenuAction As MenuAction Private clearBackgroundMenuAction As MenuAction ' The provider's constructor sets up the MenuAction objects ' and the the MenuGroup which holds them. Public Sub New() ' Set up the MenuAction which sets the control's ' background to Blue. setBackgroundToBlueMenuAction = New MenuAction("Blue") setBackgroundToBlueMenuAction.Checkable = True AddHandler setBackgroundToBlueMenuAction.Execute, AddressOf SetBackgroundToBlue_Execute ' Set up the MenuAction which sets the control's ' background to its default value. clearBackgroundMenuAction = New MenuAction("Cleared") clearBackgroundMenuAction.Checkable = True AddHandler clearBackgroundMenuAction.Execute, AddressOf ClearBackground_Execute ' Set up the MenuGroup which holds the MenuAction items. Dim backgroundFlyoutGroup As New MenuGroup("SetBackgroundsGroup", "Set Background") ' If HasDropDown is false, the group appears inline, ' instead of as a flyout. Set to true. backgroundFlyoutGroup.HasDropDown = True backgroundFlyoutGroup.Items.Add(setBackgroundToBlueMenuAction) backgroundFlyoutGroup.Items.Add(clearBackgroundMenuAction) Me.Items.Add(backgroundFlyoutGroup) ' The UpdateItemStatus event is raised immediately before ' this provider shows its tabs, which provides the opportunity ' to set states. AddHandler UpdateItemStatus, AddressOf CustomContextMenuProvider_UpdateItemStatus End Sub ' The following method handles the UpdateItemStatus event. ' It sets the MenuAction states according to the state ' of the control's Background property. This method is ' called before the context menu is shown. Sub CustomContextMenuProvider_UpdateItemStatus( _ ByVal sender As Object, _ ByVal e As MenuActionEventArgs) ' Turn everything on, and then based on the value ' of the BackgroundProperty, selectively turn some off. clearBackgroundMenuAction.Checked = False clearBackgroundMenuAction.Enabled = True setBackgroundToBlueMenuAction.Checked = False setBackgroundToBlueMenuAction.Enabled = True ' Get a ModelItem which represents the selected control. Dim selectedControl As ModelItem = _ e.Selection.PrimarySelection ' Get the value of the Background property from the ModelItem. Dim backgroundProperty As ModelProperty = _ selectedControl.Properties("Background") ' Set the MenuAction items appropriately. If Not backgroundProperty.IsSet Then clearBackgroundMenuAction.Checked = True clearBackgroundMenuAction.Enabled = False ElseIf backgroundProperty.ComputedValue.Equals(Brushes.Blue) Then setBackgroundToBlueMenuAction.Checked = True setBackgroundToBlueMenuAction.Enabled = False End If End Sub ' The following method handles the Execute event. ' It sets the Background property to its default value. Sub ClearBackground_Execute( _ ByVal sender As Object, _ ByVal e As MenuActionEventArgs) Dim selectedControl As ModelItem = e.Selection.PrimarySelection selectedControl.Properties("Background").ClearValue() End Sub ' The following method handles the Execute event. ' It sets the Background property to Brushes.Blue. Sub SetBackgroundToBlue_Execute( _ ByVal sender As Object, _ ByVal e As MenuActionEventArgs) Dim selectedControl As ModelItem = e.Selection.PrimarySelection selectedControl.Properties("Background").SetValue(Brushes.Blue) End Sub End Class
using System; using System.Collections.Generic; using System.Text; using Microsoft.Windows.Design.Interaction; using System.Windows; using Microsoft.Windows.Design.Model; using System.Windows.Controls; using System.Windows.Media; namespace CustomControlLibrary.VisualStudio.Design { // The CustomContextMenuProvider class provides two context menu items // at design time. These are implemented with the MenuAction class. class CustomContextMenuProvider : PrimarySelectionContextMenuProvider { private MenuAction setBackgroundToBlueMenuAction; private MenuAction clearBackgroundMenuAction; // The provider's constructor sets up the MenuAction objects // and the the MenuGroup which holds them. public CustomContextMenuProvider() { // Set up the MenuAction which sets the control's // background to Blue. setBackgroundToBlueMenuAction = new MenuAction("Blue"); setBackgroundToBlueMenuAction.Checkable = true; setBackgroundToBlueMenuAction.Execute += new EventHandler<MenuActionEventArgs>(SetBackgroundToBlue_Execute); // Set up the MenuAction which sets the control's // background to its default value. clearBackgroundMenuAction = new MenuAction("Cleared"); clearBackgroundMenuAction.Checkable = true; clearBackgroundMenuAction.Execute += new EventHandler<MenuActionEventArgs>(ClearBackground_Execute); // Set up the MenuGroup which holds the MenuAction items. MenuGroup backgroundFlyoutGroup = new MenuGroup("SetBackgroundsGroup", "Set Background"); // If HasDropDown is false, the group appears inline, // instead of as a flyout. Set to true. backgroundFlyoutGroup.HasDropDown = true; backgroundFlyoutGroup.Items.Add(setBackgroundToBlueMenuAction); backgroundFlyoutGroup.Items.Add(clearBackgroundMenuAction); this.Items.Add(backgroundFlyoutGroup); // The UpdateItemStatus event is raised immediately before // this provider shows its tabs, which provides the opportunity // to set states. UpdateItemStatus += new EventHandler<MenuActionEventArgs>( CustomContextMenuProvider_UpdateItemStatus); } // The following method handles the UpdateItemStatus event. // It sets the MenuAction states according to the state // of the control's Background property. This method is // called before the context menu is shown. void CustomContextMenuProvider_UpdateItemStatus( object sender, MenuActionEventArgs e) { // Turn everything on, and then based on the value // of the BackgroundProperty, selectively turn some off. clearBackgroundMenuAction.Checked = false; clearBackgroundMenuAction.Enabled = true; setBackgroundToBlueMenuAction.Checked = false; setBackgroundToBlueMenuAction.Enabled = true; // Get a ModelItem which represents the selected control. ModelItem selectedControl = e.Selection.PrimarySelection; // Get the value of the Background property from the ModelItem. ModelProperty backgroundProperty = selectedControl.Properties["Background"]; // Set the MenuAction items appropriately. if (!backgroundProperty.IsSet) { clearBackgroundMenuAction.Checked = true; clearBackgroundMenuAction.Enabled = false; } else if (backgroundProperty.ComputedValue == Brushes.Blue) { setBackgroundToBlueMenuAction.Checked = true; setBackgroundToBlueMenuAction.Enabled = false; } } // The following method handles the Execute event. // It sets the Background property to its default value. void ClearBackground_Execute( object sender, MenuActionEventArgs e) { ModelItem selectedControl = e.Selection.PrimarySelection; selectedControl.Properties["Background"].ClearValue(); } // The following method handles the Execute event. // It sets the Background property to Brushes.Blue. void SetBackgroundToBlue_Execute( object sender, MenuActionEventArgs e) { ModelItem selectedControl = e.Selection.PrimarySelection; selectedControl.Properties["Background"].SetValue(Brushes.Blue); } } }
Genere la solución.
Probar la implementación en tiempo de diseño
Puede utilizar el control ButtonWithDesignTime como cualquier otro control de WPF. WPF Designer administra la creación de todos los objetos en tiempo de diseño.
Para probar la implementación en tiempo de diseño
Agregue un nuevo proyecto de aplicación de WPF en Visual Basic o Visual C# denominado DemoApplication a la solución.
MainWindow.xaml se abrirá en WPF Designer.
Agregue una referencia al proyecto CustomControlLibrary.
En la vista XAML, reemplace el XAML generado automáticamente por el XAML siguiente. Este XAML agrega una referencia al espacio de nombres CustomControlLibrary y agrega el control personalizado ButtonWithDesignTime. El botón aparece en la Vista de diseño con el texto "Design mode active", que indica que se encuentra en modo de diseño. Si el botón no aparece, puede que sea necesario hacer clic en la barra de información situada en la parte superior del diseñador para volver a cargar la vista.
<Window x:Class="DemoApplication.MainWindow" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:cc="clr-namespace:CustomControlLibrary;assembly=CustomControlLibrary" Title="Window1" Height="300" Width="300"> <Grid> <cc:ButtonWithDesignTime Margin="30,30,30,30" Background="#FFD4D0C8"></cc:ButtonWithDesignTime> </Grid> </Window>
En la Vista de diseño, haga clic en el control ButtonWithDesignTime para seleccionarlo.
Haga clic con el botón secundario en el control ButtonWithDesignTime, elija Establecer fondo y seleccione Azul.
El fondo del control se establece en azul. En la vista XAML, la propiedad Background se establece en el valor especificado por la acción de menú.
Ejecute el proyecto DemoApplication.
En tiempo de ejecución, el botón tiene el fondo que estableció mediante el menú contextual.
Pasos siguientes
Puede agregar más características personalizadas en tiempo de diseño a los controles personalizados.
Agregue una etiqueta contextual personalizada en tiempo de diseño del control. Para obtener más información, vea Tutorial: Crear un adorno en tiempo de diseño.
Cree un editor para un tipo personalizado que puede utilizar en la ventana Propiedades. Para obtener más información, vea Cómo: Crear un editor de valores.
Vea también
Referencia
PrimarySelectionContextMenuProvider
Otros recursos
Conceptos de extensibilidad avanzada