Procédure pas à pas : implémentation de la modification sur place
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 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 allez effectuer 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é.
Notes
Les boîtes de dialogue et les commandes de menu qui s'affichent peuvent être différentes de celles qui sont décrites dans l'aide, en fonction de vos paramètres actifs ou de l'édition utilisée. Pour modifier vos paramètres, choisissez Importation et exportation de paramètres dans le menu Outils. Pour plus d'informations, consultez Utilisation des paramètres.
Composants requis
Pour exécuter cette procédure pas à pas, vous devez disposer des composants suivants :
- Visual Studio 2010.
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é
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.
Dans l'Explorateur de solutions, remplacez le nom du fichier de code par DemoControl.cs. Si une boîte de message s'affiche pour demander si vous souhaitez renommer toutes les références dans ce projet, cliquez sur Oui.
Dans l'éditeur de code, ouvrez DemoControl.cs.
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 { } }
Le chemin de sortie du projet doit être "bin\".
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 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 plus d'informations, consultez Mise à disposition de métadonnées au moment du design.
Pour créer l'assembly de métadonnées au moment du design
Ajoutez à la solution un nouveau projet de bibliothèque de classes dans Visual C# nommé CustomControlLibrary.VisualStudio.Design.
Affectez au chemin de sortie du projet la valeur \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.
Ajoutez des références aux assemblys WPF suivants.
PresentationCore
PresentationFramework
System.Xaml
WindowsBase
Ajoutez des références aux assemblys du Concepteur WPF suivants.
Microsoft.Windows.Design.Extensibility
Microsoft.Windows.Design.Interaction
Ajoutez une référence au projet CustomControlLibrary.
Dans l'Explorateur de solutions, remplacez le nom du fichier de code Class1 par Metadata.cs.
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; // 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 adorner provider to the design-time metadata. builder.AddCustomAttributes( typeof(DemoControl), new FeatureAttribute(typeof(InplaceButtonAdorners))); return builder.CreateTable(); } } } }
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
Ajoutez une nouvelle classe nommée InplaceButtonAdorners au projet CustomControlLibrary.VisualStudio.Design.
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; //using SampleControls.Designer; using System.Windows.Media; using Microsoft.Windows.Design.Metadata; using System.Globalization; 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; private ModelItem _editedItem; public InplaceButtonAdorners() { adornersPanel = new AdornerPanel(); adornersPanel.IsContentFocusable = true; adornersPanel.Children.Add(ActivateGlyph); Adorners.Add(adornersPanel); } protected override void Activate(ModelItem item) { _editedItem = item; _editedItem.PropertyChanged += new PropertyChangedEventHandler(OnEditedItemPropertyChanged); base.Activate(item); } protected override void Deactivate() { if (_editedItem != null) { _editedItem.PropertyChanged -= new PropertyChangedEventHandler(OnEditedItemPropertyChanged); _editedItem = null; } base.Deactivate(); } private ModelItem EditedItem { get { return _editedItem; } } 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 = 20; glyph.Height = 15; 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. AdornerPanel.SetAdornerHorizontalAlignment(glyph, AdornerHorizontalAlignment.Left); AdornerPanel.SetAdornerVerticalAlignment(glyph, AdornerVerticalAlignment.Top); AdornerPanel.SetAdornerMargin(glyph, new Thickness(5, 5, 0, 0)); // 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 && EditedItem != null) { TextBox glyph = new TextBox(); glyph.Padding = new Thickness(0); glyph.BorderThickness = new Thickness(0); UpdateTextBlockLocation(glyph); // Make the background white to draw over the existing text. glyph.Background = SystemColors.WindowBrush; // Two-way data bind the text box's text property to content. Binding binding = new Binding(); binding.Source = EditedItem; binding.Path = new PropertyPath("Content"); 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; } } private void UpdateTextBlockLocation(TextBox glyph) { Point textBlockLocation = FindTextBlock(); AdornerPanel.SetAdornerMargin(glyph, new Thickness(textBlockLocation.X, textBlockLocation.Y, 0, 0)); } /// <summary> /// iterate through the visual tree and look for TextBlocks to position the glyph /// </summary> /// <returns></returns> private Point FindTextBlock() { // use ModelFactory to figure out what the type of text block is - works for SL and WPF. Type textBlockType = ModelFactory.ResolveType(Context, new TypeIdentifier(typeof(TextBlock).FullName)); ViewItem textBlock = FindTextBlock(textBlockType, EditedItem.View); if (textBlock != null) { // transform the top left of the textblock to the view coordinate system. return textBlock.TransformToView(EditedItem.View).Transform(new Point(0, 0)); } // couldn't find a text block in the visual tree. Return a default position. return new Point(); } private ViewItem FindTextBlock(Type textBlockType, ViewItem view) { if (view == null) { return null; } if (textBlockType.IsAssignableFrom(view.ItemType)) { return view; } else { // walk through the child tree recursively looking for it. foreach (ViewItem child in view.VisualChildren) { return FindTextBlock(textBlockType, child); } } return null; } void OnEditedItemPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "Content") { if (EditGlyph != null) { UpdateTextBlockLocation(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 ModelItem) { return ((ModelItem)value).GetCurrentValue(); } else if (value != null) { return value.ToString(); } return string.Empty; } public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value; } } } }
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
Ajoutez à la solution un nouveau projet d'application WPF dans Visual C# nommé DemoApplication.
MainWindow.xaml s'ouvre dans le Concepteur WPF.
Ajoutez une référence au projet CustomControlLibrary.
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.MainWindow" 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>
Régénérez la solution.
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.
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.
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.
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.
Ajoutez MenuAction à votre contrôle au moment du design personnalisé. Pour plus d'informations, consultez Procédure pas à pas : création d'un fournisseur de menus.
Créez un éditeur de couleurs personnalisé, que vous pouvez utiliser dans la fenêtre Propriétés. Pour plus d'informations, consultez Procédure pas à pas : implémentation d'un éditeur de couleurs.
Créez un ornement personnalisé pour définir l'opacité d'un contrôle. Pour plus d'informations, consultez Procédure pas à pas : création d'un ornement au moment du design.
Voir aussi
Autres ressources
Création d'éditeurs personnalisés