Tutorial: Implementar la edición en contexto
Actualización: noviembre 2007
En este tutorial se muestra cómo implementar la edición en contexto para un control personalizado de Windows Presentation Foundation (WPF). Puede utilizar esta característica en tiempo de diseño en Windows Presentation Foundation (WPF) Designer for Visual Studio para establecer el valor de la propiedad Content en un control de botón personalizado. En este tutorial, el control es un botón simple y el adorno es un cuadro de texto que le permite cambiar el contenido del botón.
En este tutorial realizará las siguientes tareas:
Crear un proyecto de biblioteca de controles personalizados de WPF.
Crear un ensamblado independiente para los metadatos de tiempo de diseño.
Implementar el proveedor de adornos para la edición en contexto.
Probar el control en tiempo de diseño.
Cuando haya finalizado, sabrá crear un proveedor de adornos para un control personalizado.
Nota: |
---|
Los cuadros de diálogo y comandos de menú que se ven pueden diferenciarse 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 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
Cree un nuevo proyecto de biblioteca de controles personalizados de WPF en 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 DemoControl.cs. 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í.
En el Explorador de soluciones, expanda la carpeta Temas.
Haga doble clic en Generic.xaml.
Generic.xaml se abrirá en WPF Designer.
En la vista XAML, reemplace todas las apariciones de "CustomControl1" por "DemoControl".
Abra DemoControl.cs en el editor de código.
Reemplace el código generado automáticamente por el código siguiente. El control personalizado DemoControl hereda de Button
using System; using System.Windows; using System.Windows.Controls; namespace CustomControlLibrary { public class DemoControl : Button { } }
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 de tiempo de diseño se implementa en ensamblados de metadatos especiales. Para obtener más información, vea Cómo: Utilizar el almacén de metadatos. En este tutorial, el adorno personalizado sólo lo admite Visual Studio y se implementa en un ensamblado denominado CustomControlLibrary.VisualStudio.Design.
Para crear el ensamblado de metadatos en tiempo de diseño
Agregue a la solución un nuevo proyecto de biblioteca de clases en Visual C# denominado CustomControlLibrary.VisualStudio.Design.
Establezca la ruta de acceso de los resultados 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
WindowsBase
Agregue referencias a los siguientes ensamblados de WPF Designer.
Microsoft.Windows.Design
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.
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 DemoControl.
using System; using Microsoft.Windows.Design.Features; using Microsoft.Windows.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 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 adorner provider to the design-time metadata. builder.AddCustomAttributes( typeof(DemoControl), new FeatureAttribute(typeof(InplaceButtonAdorners))); MetadataStore.AddAttributeTable(builder.CreateTable()); } } }
Guarde la solución.
Implementar el proveedor de adornos
El proveedor de adornos se implementa en un tipo denominado InplaceButtonAdorners. Este proveedor de adornos permite al usuario establecer la propiedad Content del control en tiempo de diseño.
Para implementar el proveedor de adornos
Agregue una nueva clase denominada InplaceButtonAdorners al proyecto CustomControlLibrary.VisualStudio.Design.
En el editor de código para InplaceButtonAdorners, reemplace el código generado automáticamente por el código siguiente. Este código implementa un PrimarySelectionAdornerProvider que proporciona un adorno basado en un control TextBox.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Shapes; using Microsoft.Windows.Design.Interaction; using System.Windows.Data; using System.Windows.Input; using System.ComponentModel; using Microsoft.Windows.Design.Model; namespace CustomControlLibrary.VisualStudio.Design { // The InplaceButtonAdorners class provides two adorners: // an activate glyph that, when clicked, activates in-place // editing, and an in-place edit control, which is a text box. internal class InplaceButtonAdorners : PrimarySelectionAdornerProvider { private Rectangle activateGlyph; private TextBox editGlyph; private AdornerPanel adornersPanel; public InplaceButtonAdorners() { adornersPanel = new AdornerPanel(); adornersPanel.IsContentFocusable = true; adornersPanel.Children.Add(ActivateGlyph); Adorners.Add(adornersPanel); } private UIElement ActivateGlyph { get { if (activateGlyph == null) { // The following code specifies the shape of the activate // glyph. This can also be implemented by using a XAML template. Rectangle glyph = new Rectangle(); glyph.Fill = AdornerColors.HandleFillBrush; glyph.Stroke = AdornerColors.HandleBorderBrush; glyph.RadiusX = glyph.RadiusY = 2; glyph.Width = 10; glyph.Height = 5; glyph.Cursor = Cursors.Hand; ToolTipService.SetToolTip( glyph, "Click to edit the text of the button. " + "Enter to commit, ESC to cancel."); // Position the glyph to the upper left of the DemoControl, // and slightly inside. AdornerPlacementCollection placement = new AdornerPlacementCollection(); placement.PositionRelativeToContentHeight(0, 10); placement.PositionRelativeToContentWidth(0, 5); placement.SizeRelativeToAdornerDesiredHeight(1, 0); placement.SizeRelativeToAdornerDesiredWidth(1, 0); AdornerPanel.SetPlacements(glyph, placement); // Add interaction to the glyph. A click starts in-place editing. ToolCommand command = new ToolCommand("ActivateEdit"); Task task = new Task(); task.InputBindings.Add(new InputBinding(command, new ToolGesture(ToolAction.Click))); task.ToolCommandBindings.Add(new ToolCommandBinding(command, OnActivateEdit)); AdornerProperties.SetTask(glyph, task); activateGlyph = glyph; } return activateGlyph; } } // When in-place editing is activated, a text box is placed // over the control and focus is set to its input task. // Its task commits itself when the user presses enter or clicks // outside the control. private void OnActivateEdit(object sender, ExecutedToolEventArgs args) { adornersPanel.Children.Remove(ActivateGlyph); adornersPanel.Children.Add(EditGlyph); // Once added, the databindings activate. // All the text can now be selected. EditGlyph.SelectAll(); EditGlyph.Focus(); GestureData data = GestureData.FromEventArgs(args); Task task = AdornerProperties.GetTask(EditGlyph); task.Description = "Edit text"; task.BeginFocus(data); } // The EditGlyph utility property creates a TextBox to use as // the in-place editing control. This property centers the TextBox // inside the target control and sets up data bindings between // the TextBox and the target control. private TextBox EditGlyph { get { if (editGlyph == null) { TextBox glyph = new TextBox(); glyph.BorderThickness = new Thickness(0); glyph.Margin = new Thickness(4); AdornerPlacementCollection placement = new AdornerPlacementCollection(); placement.PositionRelativeToContentWidth(0, 0); placement.PositionRelativeToContentHeight(0, 0); placement.SizeRelativeToContentHeight(1, 0); placement.SizeRelativeToContentWidth(1, 0); AdornerPanel.SetPlacements(glyph, placement); // Data bind the glyph's vertical and horizontal alignment // to the target control's alignment properties. Binding binding = new Binding(); binding.Source = glyph; binding.Path = new PropertyPath( "(0).(1)", AdornerProperties.ActualViewProperty, Button.HorizontalContentAlignmentProperty); glyph.SetBinding(TextBox.HorizontalContentAlignmentProperty, binding); binding = new Binding(); binding.Source = glyph; binding.Path = new PropertyPath( "(0).(1)", AdornerProperties.ActualViewProperty, Button.VerticalContentAlignmentProperty); glyph.SetBinding(TextBox.VerticalContentAlignmentProperty, binding); // Make the glyph's background match the control's background. binding = new Binding(); binding.Source = glyph; binding.Path = new PropertyPath( "(0).(1)", AdornerProperties.ActualViewProperty, Button.BackgroundProperty); glyph.SetBinding(TextBox.BackgroundProperty, binding); // Two-way data bind the text box's text property to content. binding = new Binding(); binding.Source = glyph; binding.Path = new PropertyPath("(0).(1)[Content].(2)", AdornerProperties.ActualModelProperty, TypeDescriptor.GetProperties( typeof(ModelItem))["Properties"], TypeDescriptor.GetProperties( typeof(ModelProperty))["ComputedValue"]); binding.Mode = BindingMode.TwoWay; binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; binding.Converter = new ContentConverter(); glyph.SetBinding(TextBox.TextProperty, binding); // Create a task that describes the UI interaction. ToolCommand commitCommand = new ToolCommand("Commit Edit"); Task task = new Task(); task.InputBindings.Add( new InputBinding( commitCommand, new KeyGesture(Key.Enter))); task.ToolCommandBindings.Add( new ToolCommandBinding(commitCommand, delegate { task.Complete(); })); task.FocusDeactivated += delegate { adornersPanel.Children.Remove(EditGlyph); adornersPanel.Children.Add(ActivateGlyph); }; AdornerProperties.SetTask(glyph, task); editGlyph = glyph; } return editGlyph; } } // The ContentConverter class ensures that only strings // are assigned to the Text property of EditGlyph. private class ContentConverter : IValueConverter { public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is string) { return value; } return string.Empty; } public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value; } } } }
Genere la solución.
Probar la implementación en tiempo de diseño
Puede utilizar la clase DemoControl como utilizaría 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 a la solución un nuevo proyecto de aplicación de WPF en Visual C# denominado DemoApplication.
Window1.xaml se abrirá en el 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 DemoControl. Si el control no aparece, es posible que tenga que 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:ccl="clr-namespace:CustomControlLibrary;assembly=CustomControlLibrary" Title="Window1" Height="300" Width="300"> <Grid> <ccl:DemoControl></ccl:DemoControl> </Grid> </Window>
Vuelva a generar la solución.
En la vista Diseño, haga clic en el control DemoControl para seleccionarlo.
Aparece un pequeño glifo de Rectangle en la esquina superior izquierda del control DemoControl.
Haga clic en el glifo de Rectangle para activar la edición en contexto.
Aparecerá un cuadro de texto con la propiedad Content de DemoControl. Puesto que su contenido está vacío, únicamente se ve un cursor en el centro del botón.
Escriba un valor nuevo para el contenido de texto y presione la tecla ENTRAR.
En la vista XAML, la propiedad Content se establece en el valor de texto que ha escrito en la vista Diseño.
Establezca el proyecto DemoApplication como proyecto de inicio y ejecute la solución.
En tiempo de ejecución, el botón tiene el valor de texto establecido con la etiqueta contextual.
Pasos siguientes
Puede agregar más características personalizadas en tiempo de diseño a los controles personalizados.
Agregue un objeto MenuAction al tiempo de diseño personalizado. Para obtener más información, vea Tutorial: Crear MenuAction.
Cree un editor de colores personalizado que se pueda utilizar en la ventana Propiedades. Para obtener más información, vea Tutorial: Implementar un editor de colores.
Cree un adorno personalizado para establecer la opacidad de un control. Para obtener más información, vea Tutorial: Crear un adorno en tiempo de diseño.