Export (0) Print
Expand All

Walkthrough: Debugging WPF Custom Controls at Design Time

[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]

This walkthrough shows how to debug a design-time adorner for a Windows Presentation Foundation (WPF) custom control. The adorner is a check box, which provides a simple auto-size feature.

In this walkthrough, you perform the following tasks:

  • Create a WPF custom control library project.

  • Create a separate assembly for design-time metadata.

  • Implement the adorner provider.

  • Test the control at design time.

  • Set up the project for design-time debugging.

  • Debug the control at design time.

When you are finished, you will know how to debug an adorner for a custom control.

Note Note

The dialog boxes and menu commands you see might differ from those described in Help depending on your active settings or edition. To change your settings, choose Import and Export Settings on the Tools menu. For more information, see Visual Studio Settings.

You need the following components to complete this walkthrough:

  • Visual Studio 2012 RC.

The first step is to create the project for the custom control. The control is a button with small amount of design-time code, which uses the GetIsInDesignMode method to implement a design-time behavior.

To create the custom control

  1. Create a new WPF Custom Control Library project in Visual Basic or Visual C# named AutoSizeButtonLibrary.

    The code for CustomControl1 opens in the Code Editor.

  2. In Solution Explorer, change the name of the code file to AutoSizeButton.cs or AutoSizeButton.vb. If a message box appears that asks if you want to perform a rename for all references in this project, click Yes.

  3. Open AutoSizeButton.cs or AutoSizeButton.vb in the Code Editor.

  4. Replace the automatically generated code with the following code. This code inherits from Button and displays the text "Design mode active" when the button appears in the designer.

    
                      
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.ComponentModel;
    
    namespace AutoSizeButtonLibrary
    {
        // The AutoSizeButton control implements a button
        // with a custom design-time experience. 
        public class AutoSizeButton : Button
        {
            public AutoSizeButton()
            {
                // The following code enables custom design-mode logic.
                // The GetIsInDesignMode check and the following design-time 
                // code are optional and shown only for demonstration.
                if (DesignerProperties.GetIsInDesignMode(this))
                {
                    Content = "Design mode active";
                }
            }
        }
    }
    
    
                    
    
  5. Set the project's output path to "bin\".

  6. Build the solution.

Design-time code is deployed in special metadata assemblies. For this walkthrough, the custom adorner is deployed in an assembly named AutoSizeButtonLibrary.VisualStudio.Design. For more information, see Providing Design-time Metadata.

To create the design-time metadata assembly

  1. Add a new Class Library project in Visual Basic or Visual C# named AutoSizeButtonLibrary.VisualStudio.Design to the solution.

  2. Set the project's output path to "..\AutoSizeButtonLibrary\bin\". This keeps the control's assembly and the metadata assembly in the same folder, which enables metadata discovery for designers.

  3. Add references to the following WPF assemblies.

    • PresentationCore

    • PresentationFramework

    • System.Xaml

    • WindowsBase

  4. Add references to the following WPF Designer assemblies.

    • Microsoft.Windows.Design.Extensibility

    • Microsoft.Windows.Design.Interaction

  5. Add a reference to the AutoSizeButtonLibrary project.

  6. In Solution Explorer, change the name of the Class1 code file to Metadata.cs or Metadata.vb. If a message box appears that asks if you want to perform a rename for all references in this project, click Yes.

  7. Replace the automatically generated code with the following code. This code creates an AttributeTable which attaches the custom design-time implementation to the AutoSizeButton 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 AutoSizeButtonLibrary;
    using Microsoft.Windows.Design.Features;
    using Microsoft.Windows.Design.Metadata;
    using AutoSizeButtonLibrary.VisualStudio.Design;
    
    // The ProvideMetadata assembly-level attribute indicates to designers
    // that this assembly contains a class that provides an attribute table. 
    [assembly: ProvideMetadata(typeof(AutoSizeButtonLibrary.VisualStudio.Design.Metadata))]
    namespace AutoSizeButtonLibrary.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();
    
                    builder.AddCustomAttributes(
                        typeof(AutoSizeButton),
                        new FeatureAttribute(typeof(AutoSizeAdornerProvider)));
    
                    return builder.CreateTable();
                }
            }
        }
    }
    
    
                    
    
  8. Save the solution.

The adorner provider is implemented in a type named AutoSizeAdornerProvider. This adorner FeatureProvider enables setting the control's Height and Width properties at design time.

To implement the adorner provider

  1. Add a new class named AutoSizeAdornerProvider to the AutoSizeButtonLibrary.VisualStudio.Design project.

  2. In the Code Editor for AutoSizeAdornerProvider, replace the automatically generated code with the following code. This code implements a PrimarySelectionAdornerProvider which provides an adorner based on a CheckBox control.

    
                      
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows.Input;
    using System.Windows;
    using System.Windows.Automation;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Shapes;
    using Microsoft.Windows.Design.Interaction;
    using Microsoft.Windows.Design.Model;
    
    namespace AutoSizeButtonLibrary.VisualStudio.Design
    {
        // The following class implements an adorner provider for the 
        // AutoSizeButton control. The adorner is a CheckBox control, which 
        // changes the Height and Width of the AutoSizeButton to "Auto",
        // which is represented by double.NaN.
        public class AutoSizeAdornerProvider : PrimarySelectionAdornerProvider
        {
            bool settingProperties;
            private ModelItem adornedControlModel;
            CheckBox autoSizeCheckBox;
            AdornerPanel autoSizeAdornerPanel;
    
            // The constructor sets up the adorner control. 
            public AutoSizeAdornerProvider()
            {
                autoSizeCheckBox = new CheckBox();
                autoSizeCheckBox.Content = "AutoSize";
                autoSizeCheckBox.IsChecked = true;
                autoSizeCheckBox.FontFamily = AdornerFonts.FontFamily;
                autoSizeCheckBox.FontSize = AdornerFonts.FontSize;
                autoSizeCheckBox.Background = AdornerResources.FindResource(
                    AdornerColors.RailFillBrushKey) as Brush;
            }
    
            // The following method is called when the adorner is activated.
            // It creates the adorner control, sets up the adorner panel,
            // and attaches a ModelItem to the AutoSizeButton.
            protected override void Activate(ModelItem item)
            {
                // Save the ModelItem and hook into when it changes.
                // This enables updating the slider position when 
                // a new background value is set.
                adornedControlModel = item;
                adornedControlModel.PropertyChanged += 
                    new System.ComponentModel.PropertyChangedEventHandler(
                        AdornedControlModel_PropertyChanged);
    
                // All adorners are placed in an AdornerPanel
                // for sizing and layout support.
                AdornerPanel panel = this.Panel;
    
                // Set up the adorner's placement.
                AdornerPanel.SetAdornerHorizontalAlignment(autoSizeCheckBox, AdornerHorizontalAlignment.OutsideLeft);
                AdornerPanel.SetAdornerVerticalAlignment(autoSizeCheckBox, AdornerVerticalAlignment.OutsideTop);
    
                // Listen for changes to the checked state.
                autoSizeCheckBox.Checked += new RoutedEventHandler(autoSizeCheckBox_Checked);
                autoSizeCheckBox.Unchecked += new RoutedEventHandler(autoSizeCheckBox_Unchecked);
    
                // Run the base implementation.
                base.Activate(item);
            }
    
            // The Panel utility property demand-creates the 
            // adorner panel and adds it to the provider's 
            // Adorners collection.
            private AdornerPanel Panel
            {
                get
                {
                    if (this.autoSizeAdornerPanel == null)
                    {
                        autoSizeAdornerPanel = new AdornerPanel();
    
                        // Add the adorner to the adorner panel.
                        autoSizeAdornerPanel.Children.Add(autoSizeCheckBox);
    
                        // Add the panel to the Adorners collection.
                        Adorners.Add(autoSizeAdornerPanel);
                    }
    
                    return this.autoSizeAdornerPanel;
                }
            }
    
            // The following code handles the Checked event.
            // It autosizes the adorned control's Height and Width.
            void autoSizeCheckBox_Checked(object sender, RoutedEventArgs e)
            {
                this.SetHeightAndWidth(true);
            }
    
            // The following code handles the Unchecked event.
            // It sets the adorned control's Height and Width to a hard-coded value.
            void autoSizeCheckBox_Unchecked(object sender, RoutedEventArgs e)
            {
                this.SetHeightAndWidth(false);
            }
    
            // The SetHeightAndWidth utility method sets the Height and Width
            // properties through the model and commits the change.
            private void SetHeightAndWidth(bool autoSize)
            {
                settingProperties = true;
    
                try
                {
                using (ModelEditingScope batchedChange = adornedControlModel.BeginEdit())
                {
                    ModelProperty widthProperty =
                        adornedControlModel.Properties["Width"];
    
                    ModelProperty heightProperty =
                        adornedControlModel.Properties["Height"];
    
                    if (autoSize)
                    {
                        widthProperty.ClearValue();
                        heightProperty.ClearValue();
                    }
                    else
                    {
                        widthProperty.SetValue(20d);
                        heightProperty.SetValue(20d);
                    }
    
                    batchedChange.Complete();
                }
                }
                finally { settingProperties = false; }
            }
    
            // The following method deactivates the adorner.
            protected override void Deactivate()
            {
                adornedControlModel.PropertyChanged -= 
                    new System.ComponentModel.PropertyChangedEventHandler(
                        AdornedControlModel_PropertyChanged);
    
                base.Deactivate();
            }
    
            // The following method handles the PropertyChanged event.
            void AdornedControlModel_PropertyChanged(
                object sender, 
                System.ComponentModel.PropertyChangedEventArgs e)
            {
                if (settingProperties)
                {
                    return;
                }
    
                if (e.PropertyName == "Height" || e.PropertyName == "Width")
                {
                    double h = (double)adornedControlModel.Properties["Height"].ComputedValue;
                    double w = (double)adornedControlModel.Properties["Width"].ComputedValue;
    
                    autoSizeCheckBox.IsChecked = (h == double.NaN && w == double.NaN) ? true : false;
                }
            }
        }
    }
    
    
                    
    
  3. Build the solution.

You can use the AutoSizeButton control as you would use any other WPF control. The WPF Designer handles the creation of all design-time objects.

To test the design-time implementation

  1. Add a new WPF Application project named DemoApplication to the solution.

    MainWindow.xaml opens in the WPF Designer.

  2. Add a reference to the AutoSizeButtonLibrary project.

  3. In XAML view, replace the automatically generated code with the following code. This XAML adds a reference to the AutoSizeButtonLibrary namespace and adds the AutoSizeButton custom control. The button appears in Design view with the text "Design mode active", indicating that it is in design mode. If the button does not appear, you might have to click the Information bar at the top of the designer to reload the view.

    
    <Window x:Class="DemoApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ab="clr-namespace:AutoSizeButtonLibrary;assembly=AutoSizeButtonLibrary"
        Title="Window1" Height="300" Width="300">
        <Grid>
            <ab:AutoSizeButton Height="Auto" Width="Auto" />
        </Grid>
    </Window>
    
    
    
  4. In Design view, click the AutoSizeButton control to select it.

    A CheckBox control appears above the AutoSizeButton control.

  5. Uncheck the check box adorner.

    The control reduces in size. The check box adorner moves to maintain its position relative to the adorned control.

At this point, you have completed the design-time implementation. Now you can use Visual Studio to set breakpoints and step into your design-time code. To debug your design-time implementation, you attach another instance of Visual Studio to your current Visual Studio session.

To set up project for design-time debugging

  1. In Solution Explorer, right-click the DemoApplication project and select Set as StartUp Project.

  2. In Solution Explorer, right-click the DemoApplication project and select Properties.

  3. In the DemoApplication Project Designer, click the Debug tab.

  4. In the Start Action section, select Start external program. You will be debugging a separate instance of Visual Studio.

  5. Click the ellipsis (VisualStudioEllipsesButton screenshot) button to open the Select File dialog box.

  6. Browse for Visual Studio. The name of the executable file is devenv.exe, and if you installed Visual Studio in the default location, its path is "%programfiles%\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe".

  7. Click Open to select devenv.exe.

Now you are ready to debug your custom control as it runs in design mode. When you start the debugging session, a new instance of Visual Studio will be created, and you will use it to load the AutoSizeButtonLibrary solution. When you open MainWindow.xaml in the WPF Designer, an instance of your custom control will be created and will start to run.

To debug your custom control at design time

  1. Open the AutoSizeButton code file in the Code Editor and add a breakpoint in the constructor.

  2. Open Metadata.cs or Metadata.vb in the Code Editor and add a breakpoint in the AttributeTable property.

  3. Open AutoSizeAdornerProvider.cs or AutoSizeAdornerProvider.vb in the Code Editor and add a breakpoint in the constructor.

  4. Add breakpoints in the remaining methods for the AutoSizeAdornerProvider class.

  5. Press F5 to start the debugging session.

    A second instance of Visual Studio is created. You can distinguish between the debugging instance and the second instance in two ways:

    • The debugging instance has the word Running in its title bar.

    • The debugging instance has the Start button on its Debug toolbar disabled.

    Your breakpoint is set in the debugging instance.

  6. In the second instance of Visual Studio, open the AutoSizeButtonLibrary solution.

  7. Open MainWindow.xaml in the WPF Designer.

    The debugging instance of Visual Studio acquires focus and execution stops at the breakpoint in the AttributeTable property.

  8. Press F5 to continue debugging.

    Execution stops at the breakpoint in the AutoSizeButton constructor.

  9. Press F5 to continue debugging.

    The second instance of Visual Studio acquires focus and the WPF Designer appears.

  10. In Design view, click the AutoSizeButton control to select it.

    The debugging instance of Visual Studio acquires focus and execution stops at the breakpoint in the AutoSizeAdornerProvider constructor.

  11. Press F5 to continue debugging.

    Execution stops at the breakpoint in the Activate method.

  12. Press F5 to continue debugging.

    The second instance of Visual Studio acquires focus and the WPF Designer appears. The check box adorner appears above and to the left of the AutoSizeButton.

  13. When you are finished, you can stop your debugging session by closing the second instance of Visual Studio or by clicking the Stop Debugging button in the debugging instance.

You can add more custom design-time features to your custom controls.

Show:
© 2014 Microsoft