Compartir a través de


Tutorial: Crear MenuAction

Actualización: noviembre 2007

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ú de acceso directo para establecer el valor de la propiedad Background en un control de botón personalizado. Hay un ejemplo de código completo en Cómo: Crear MenuAction.

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, dependiendo de los valores de configuración o de edición activos. Para cambiar la configuración, elija Importar y exportar configuraciones en el menú Herramientas. Para obtener más información, vea Valores de configuración de Visual Studio.

Requisitos previos

Necesita los componentes siguientes para completar este tutorial:

  • Visual Studio 2008.

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

  1. 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.

  2. 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í.

  3. En el Explorador de soluciones, expanda la carpeta Themes.

  4. Haga doble clic en Generic.xaml.

    Generic.xaml se abre en WPF Designer.

  5. En la vista XAML, reemplace todas las apariciones de "CustomControl1" por "ButtonWithDesignTime".

  6. Abra ButtonWithDesignTime.cs o ButtonWithDesignTime.vb en el Editor de código.

  7. 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";
                }
            }
        }
    }
    
  8. Establezca la ruta de acceso de salida del proyecto en "bin\".

  9. 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 crear el ensamblado de metadatos en tiempo de diseño

  1. Agregue un nuevo proyecto de biblioteca de clases en Visual Basic o Visual C# denominado CustomControlLibrary.VisualStudio.Design a la solución.

  2. 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.

  3. Agregue referencias a los ensamblados de WPF siguientes.

    • PresentationCore

    • PresentationFramework

    • WindowsBase

  4. Agregue referencias a los ensamblados de WPF Designer siguientes.

    • Microsoft.Windows.Design

    • Microsoft.Windows.Design.Extensibility

    • Microsoft.Windows.Design.Interaction

  5. Agregue una referencia al proyecto CustomControlLibrary.

  6. 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í.

  7. 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.SliderAdornerLib
    
    ' Container for any general design-time metadata to initialize.
    ' Designers look for a type in the design-time assembly that 
    ' implements IRegisterMetadata. If found, designers instantiate 
    ' this class and call its Register() method automatically.
    Friend Class Metadata
        Implements IRegisterMetadata
    
        ' Called by the designer to register any design-time metadata.
        Public Sub Register() Implements IRegisterMetadata.Register
            Dim builder As New AttributeTableBuilder()
    
            ' Add the menu provider to the design-time metadata.
            builder.AddCustomAttributes(GetType(ButtonWithDesignTime), _
                                        New FeatureAttribute(GetType(CustomContextMenuProvider)))
    
            MetadataStore.AddAttributeTable(builder.CreateTable())
        End Sub
    
    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 SliderAdornerLib;
    
    namespace CiderPropertiesTester
    {
        // Container for any general design-time metadata to initialize.
        // Designers look for a type in the design-time assembly that 
        // implements IRegisterMetadata. If found, designers instantiate 
        // this class and call its Register() method automatically.
        internal class Metadata : IRegisterMetadata
        {
            // Called by the designer to register any design-time metadata.
            public void Register()
            {
                AttributeTableBuilder builder = new AttributeTableBuilder();
    
                // Add the menu provider to the design-time metadata.
                builder.AddCustomAttributes(
                    typeof(ButtonWithDesignTime), 
                    new FeatureAttribute(typeof(CustomContextMenuProvider)));
    
                MetadataStore.AddAttributeTable(builder.CreateTable());
            }
        }
    }
    
  8. 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ú

  1. Agregue una nueva clase denominada CustomContextMenuProvider al proyecto CustomControlLibrary.VisualStudio.Design.

  2. En el Editor de código para CustomContextMenuProvider, reemplace el código generado automáticamente con el código siguiente. Este código implementa un objeto PrimarySelectionContextMenuProvider que proporciona un objeto MenuAction personalizado.

    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(Control.BackgroundProperty)
    
            ' 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(Control.BackgroundProperty).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(Control.BackgroundProperty).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 SliderAdornerLib
    {
        // 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[Control.BackgroundProperty];
    
                // 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[Control.BackgroundProperty].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[Control.BackgroundProperty].SetValue(Brushes.Blue);
            }
        }
    }
    
  3. 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 de tiempo de diseño.

Para probar la implementación en tiempo de diseño

  1. Agregue un nuevo proyecto de aplicación de WPF en Visual Basic o Visual C# denominado DemoApplication a la solución.

    Window1.xaml se abrirá en el WPF Designer.

  2. Agregue una referencia al proyecto CustomControlLibrary.

  3. En la vista XAML, reemplace el XAML generado automáticamente por lo siguiente. Este XAML agrega una referencia al espacio de nombres CustomControlLibrary y agrega el control personalizado ButtonWithDesignTime. El botón aparece en la vista 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.Window1"
        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>
    
    <Window x:Class="DemoApplication.Window1"
        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>
    
  4. En la vista Diseño, haga clic en el control ButtonWithDesignTime para seleccionarlo.

  5. 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ú.

  6. Ejecute el proyecto DemoApplication.

    En tiempo de ejecución, el botón tiene el fondo que estableció mediante el menú de acceso directo.

Pasos siguientes

Puede agregar más características personalizadas en tiempo de diseño a los controles personalizados.

Vea también

Tareas

Cómo: Crear MenuAction

Referencia

PrimarySelectionContextMenuProvider

Otros recursos

Conceptos de extensibilidad avanzada

Extensibilidad de WPF Designer