Procedura dettagliata: implementazione della modifica sul posto
In questa procedura dettagliata viene illustrato come implementare modifiche sul posto per un controllo personalizzato WPF (Windows Presentation Foundation). Questa funzionalità Design-Time può essere utilizzata in WPF Designer per Visual Studio per impostare il valore della proprietà Content su un controllo pulsante personalizzato. Per questa procedura dettagliata, il controllo è un semplice pulsante e lo strumento decorativo visuale è una casella di testo che consente di modificare il contenuto del pulsante.
Questa procedura dettagliata prevede l'esecuzione delle attività seguenti:
Creare un progetto di libreria di controlli personalizzati WPF.
Creare un assembly distinto per i metadati Design-Time.
Implementare il provider dello strumento decorativo visuale per la modifica sul posto.
Verificare il controllo in fase di progettazione.
Al termine, si sarà in grado di creare un provider di strumenti decorativi visuali per un controllo personalizzato.
Nota
È possibile che le finestre di dialogo e i comandi di menu visualizzati siano diversi da quelli descritti nella Guida a seconda delle impostazioni attive o dell'edizione del programma. Per modificare le impostazioni, scegliere Importa/Esporta impostazioni dal menu Strumenti. Per ulteriori informazioni, vedere Gestione delle impostazioni.
Prerequisiti
Per completare la procedura dettagliata, è necessario disporre dei componenti seguenti:
- Visual Studio 2010.
Creazione del controllo personalizzato
Il primo passaggio consiste nella creazione del progetto per il controllo personalizzato. Il controllo è un semplice pulsante con una piccola quantità di codice Design-Time che utilizza il metodo GetIsInDesignMode per implementare un comportamento in fase di progettazione.
Per creare il controllo personalizzato
In Visual C# creare un nuovo progetto di libreria di controlli personalizzati WPF denominato CustomControlLibrary.
Il codice per CustomControl1 verrà aperto nell'editor del codice.
In Esplora soluzioni modificare il nome del file di codice in DemoControl.cs. Se viene visualizzata una finestra di messaggio in cui si richiede se eseguire un'operazione di ridenominazione per tutti i riferimenti del progetto, fare clic su Sì.
Aprire il file DemoControl.cs nell'editor del codice.
Sostituire il codice generato automaticamente con il codice seguente. Il controllo DemoControl personalizzato eredita da Button
using System; using System.Windows; using System.Windows.Controls; namespace CustomControlLibrary { public class DemoControl : Button { } }
Impostare il percorso di output del progetto su "bin\".
Compilare la soluzione.
Creazione dell'assembly di metadati Design-Time
Il codice Design-Time viene distribuito in speciali assembly di metadati. Per questa procedura dettagliata, lo strumento decorativo visuale personalizzato è supportato solo da Visual Studio ed è distribuito in un assembly denominato CustomControlLibrary.VisualStudio.Design. Per ulteriori informazioni, vedere Aggiunta di metadati della fase di progettazione.
Per creare l'assembly di metadati Design-Time
In Visual C# aggiungere alla soluzione un nuovo progetto di libreria di classi denominato CustomControlLibrary.VisualStudio.Design.
Impostare il percorso di output del progetto su ".. \CustomControlLibrary\bin\". In questo modo l'assembly del controllo e l'assembly dei metadati verranno mantenuti nella stessa cartella, consentendo l'individuazione di metadati per le finestre di progettazione.
Aggiungere riferimenti agli assembly WPF riportati di seguito.
PresentationCore
PresentationFramework
System.Xaml
WindowsBase
Aggiungere riferimenti agli assembly WPF Designer riportati di seguito.
Microsoft.Windows.Design.Extensibility
Microsoft.Windows.Design.Interaction
Aggiungere un riferimento al progetto CustomControlLibrary.
In Esplora soluzioni modificare il nome del file di codice Class1 in Metadata.cs.
Sostituire il codice generato automaticamente con il codice seguente. Con questo codice viene creato un oggetto AttributeTable che connette l'implementazione Design-Time personalizzata alla 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(); } } } }
Salvare la soluzione.
Implementazione del provider di strumenti decorativi visuali
Il provider di strumenti decorativi visuali viene implementato in un tipo denominato InplaceButtonAdorners. Questo provider di strumenti decorativi visuali consente all'utente di impostare in fase di progettazione la proprietà Content del controllo.
Per implementare il provider di strumenti decorativi visuali
Aggiungere una nuova classe denominata InplaceButtonAdorners al progetto CustomControlLibrary.VisualStudio.Design.
Nell'editor del codice per InplaceButtonAdorners sostituire il codice generato automaticamente con il codice seguente. Questo codice implementa un PrimarySelectionAdornerProvider che fornisce uno strumento decorativo visuale basato su un controllo 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; } } } }
Compilare la soluzione.
Test dell'implementazione Design-Time
È possibile utilizzare la classe DemoControl come qualsiasi altro controllo WPF. In WPF Designer viene gestita la creazione di tutti gli oggetti in fase di progettazione.
Per eseguire il test dell'implementazione in fase di progettazione
In Visual C# aggiungere alla soluzione un nuovo progetto di applicazione WPF denominato DemoApplication.
MainWindow.xaml viene aperto in WPF Designer.
Aggiungere un riferimento al progetto CustomControlLibrary.
In visualizzazione XAML sostituire il codice XAML generato automaticamente con il seguente codice XAML. Con questo codice XAML viene aggiunto un riferimento allo spazio dei nomi CustomControlLibrary e viene aggiunto il controllo personalizzato DemoControl. Se il controllo non viene visualizzato, può essere necessario fare clic sulla barra informazioni nella parte superiore della finestra di progettazione per ricaricare la visualizzazione.
<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>
Ricompilare la soluzione.
In visualizzazione Progettazione fare clic sul controllo DemoControl per selezionarlo.
Nell'angolo superiore sinistro del controllo DemoControl viene visualizzato un piccolo glifo Rectangle.
Fare clic sul glifo Rectangle per attivare la modifica sul posto.
Viene visualizzata una casella di testo in cui viene mostrato l'oggetto Content dell'oggetto DemoControl. Dal momento che il contenuto è attualmente vuoto, viene visualizzato soltanto un cursore al centro del pulsante.
Digitare un nuovo valore per il contenuto di testo, quindi premere il tasto INVIO.
In visualizzazione XAML, la proprietà Content è impostata sul valore del testo digitato in visualizzazione Progettazione.
Impostare il progetto DemoApplication come progetto di avvio ed eseguire la soluzione.
In fase di esecuzione, il pulsante presenta il valore del testo impostato con lo strumento decorativo visuale.
Passaggi successivi
È possibile aggiungere altre funzionalità Design-Time ai controlli personalizzati.
Aggiungere un controllo MenuAction alla fase di progettazione personalizzata. Per ulteriori informazioni, vedere Procedura dettagliata: creazione di un provider di menu.
Creare un editor di colori personalizzato da utilizzare nella finestra Proprietà. Per ulteriori informazioni, vedere Procedura dettagliata: implementazione di un editor di colori.
Creare uno strumento decorativo visuale personalizzato per l'impostazione dell'opacità di un controllo. Per ulteriori informazioni, vedere Procedura dettagliata: creazione di uno strumento decorativo visuale in fase di progettazione.
Vedere anche
Altre risorse
Creazione di editor personalizzati