Procédure pas à pas : implémentation de la modification sur place

Mise à jour : novembre 2007

Cette procédure pas à pas indique comment implémenter la modification sur place pour un contrôle personnalisé WPF (Windows Presentation Foundation). Vous pouvez utiliser cette fonctionnalité au moment du design dans le Concepteur Windows Presentation Foundation (WPF) pour Visual Studio pour définir la valeur de la propriété Content pour un contrôle bouton personnalisé. Pour cette procédure pas à pas, le contrôle est un bouton simple et l'ornement est une zone de texte qui permet de modifier le contenu du bouton.

Dans cette procédure pas à pas, vous exécutez les tâches suivantes :

  • créer un projet de bibliothèque de contrôles personnalisés WPF ;

  • créer un assembly distinct pour les métadonnées au moment du design ;

  • implémenter le fournisseur d'ornements pour la modification sur place ;

  • tester le contrôle au moment du design.

Lorsque vous aurez terminé, vous saurez comment créer un ornement pour un contrôle personnalisé.

Remarque :

Selon vos paramètres actifs ou votre édition, les boîtes de dialogue et les commandes de menu que vous voyez peuvent différer de celles qui sont décrites dans l'aide. Pour modifier vos paramètres, choisissez Importation et exportation de paramètres dans le menu Outils. Pour plus d'informations, consultez Paramètres Visual Studio.

Composants requis

Vous avez besoin des composants suivants pour accomplir cette procédure pas à pas :

  • Visual Studio 2008.

Création du contrôle personnalisé

La première étape consiste à créer le projet pour le contrôle personnalisé. Le contrôle est un bouton simple avec une petite portion de code au moment du design, qui utilise la méthode GetIsInDesignMode pour implémenter un comportement au moment du design.

Pour créer le contrôle personnalisé

  1. Créez un projet de bibliothèque de contrôles personnalisés WPF dans Visual C# nommé CustomControlLibrary.

    Le code pour CustomControl1 s'ouvre dans l'éditeur de code.

  2. Dans l'Explorateur de solutions, remplacez le nom du fichier de code par DemoControl.cs. Si un message s'affiche demandant si vous souhaitez renommer toutes les références dans ce projet, cliquez sur Oui.

  3. Dans l'Explorateur de solutions, développez le dossier Thèmes.

  4. Double-cliquez sur Generic.xaml.

    Generic.xaml s'ouvre dans le Concepteur WPF.

  5. En mode XAML, remplacez toutes les occurrences de "CustomControl1" par "DemoControl".

  6. Dans l'éditeur de code, ouvrez DemoControl.cs.

  7. Remplacez le code généré automatiquement par le code suivant. Le contrôle personnalisé DemoControl hérite de Button

    using System;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace CustomControlLibrary
    {
        public class DemoControl : Button
        {   
        }
    }
    
  8. Le chemin de sortie du projet doit être "bin\".

  9. Générez la solution.

Création de l'assembly de métadonnées au moment du design

Le code au moment du design est déployé dans des assemblys de métadonnées spéciaux. Pour plus d'informations, consultez Comment : utiliser le magasin de métadonnées. Pour cette procédure pas à pas, l'ornement personnalisé est pris en charge par Visual Studio uniquement et est déployé dans un assembly nommé CustomControlLibrary.VisualStudio.Design.

Pour créer l'assembly de métadonnées au moment du design

  1. Ajoutez à la solution un nouveau projet de bibliothèque de classes dans Visual C# nommé CustomControlLibrary.VisualStudio.Design.

  2. Le chemin de sortie du projet doit être "..\CustomControlLibrary\bin\". Cela permet de garder l'assembly du contrôle et l'assembly de métadonnées dans le même dossier, ce qui active la découverte de métadonnées pour les concepteurs.

  3. Ajoutez des références aux assemblys WPF suivants.

    • PresentationCore

    • PresentationFramework

    • WindowsBase

  4. Ajoutez des références aux assemblys du Concepteur WPF suivants.

    • Microsoft.Windows.Design

    • Microsoft.Windows.Design.Extensibility

    • Microsoft.Windows.Design.Interaction

  5. Ajoutez une référence au projet CustomControlLibrary.

  6. Dans l'Explorateur de solutions, remplacez le nom du fichier de code Class1 par Metadata.cs.

  7. Remplacez le code généré automatiquement par le code suivant. Ce code crée un AttributeTable qui attache l'implémentation au moment du design personnalisée à la classe 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());
    
            }
        }
    }
    
  8. Enregistrez la solution.

Implémentation du fournisseur d'ornements

Le fournisseur d'ornements est implémenté dans un type nommé InplaceButtonAdorners. Grâce à ce fournisseur d'ornements, l'utilisateur peut définir la propriété Content du contrôle au moment du design.

Pour implémenter le fournisseur d'ornements

  1. Ajoutez une nouvelle classe nommée InplaceButtonAdorners au projet CustomControlLibrary.VisualStudio.Design.

  2. Dans l'éditeur de code pour InplaceButtonAdorners, remplacez le code généré automatiquement par le code suivant. Ce code implémente un PrimarySelectionAdornerProvider qui fournit un ornement basé sur un contrôle 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;
                }
            }
        }
    }
    
  3. Générez la solution.

Test de l'implémentation au moment du design

Vous pouvez utiliser la classe DemoControl comme vous utiliseriez tout autre contrôle WPF. Le Concepteur WPF gère la création de tous les objets au moment du design.

Pour tester l'implémentation au moment du design

  1. Ajoutez à la solution un nouveau projet d'application WPF dans Visual C# nommé DemoApplication.

    Window1.xaml s'ouvre dans le Concepteur WPF.

  2. Ajoutez une référence au projet CustomControlLibrary.

  3. En mode XAML, remplacez le code XAML généré automatiquement par le code XAML suivant. Ce code XAML ajoute une référence à l'espace de noms CustomControlLibrary ainsi que le contrôle personnalisé DemoControl. Si le contrôle ne s'affiche pas, il peut s'avérer nécessaire de cliquer sur la barre d'informations, située dans la partie supérieure du concepteur, pour recharger la vue.

    <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>
    
  4. Régénérez la solution.

  5. En mode Design, cliquez sur le contrôle DemoControl pour le sélectionner.

    Un petit glyphe Rectangle apparaît dans le coin supérieur gauche du contrôle DemoControl.

  6. Cliquez sur le glyphe Rectangle pour activer l'édition sur place.

    Une zone de texte contenant le Content de DemoControl s'affiche. Comme le contenu est actuellement vide, seul un curseur s'affiche au centre du bouton.

  7. Tapez une nouvelle valeur pour le contenu de texte, puis appuyez sur la touche ENTRÉE.

    En mode XAML, la propriété Content prend la valeur de texte que vous avez tapée en mode Design.

  8. Définissez le projet DemoApplication en tant que projet de démarrage, puis exécutez la solution.

    Au moment de l'exécution, la valeur de texte du bouton correspond à celle définie avec l'ornement.

Étapes suivantes

Vous pouvez ajouter d'autres fonctionnalités personnalisées au moment du design à vos contrôles personnalisés.

Voir aussi

Autres ressources

Création d'éditeurs personnalisés

Extensibilité du concepteur WPF