Click to Rate and Give Feedback
MSDN
MSDN Library
Visual Studio 2008
Visual Studio
 Walkthrough: Creating a Category Ed...
Collapse All/Expand All Collapse All
This page is specific to
Microsoft Visual Studio 2008/.NET Framework 3.5

Other versions are also available for the following:
Windows Presentation Foundation Designer Extensibility for Visual Studio
Walkthrough: Creating a Category Editor

Updated: January 2010

The extensibility model for the Windows Presentation Foundation (WPF) Designer for Visual Studio allows you to create custom editors for categories of properties known as category editors. Category editors allow you to provide a custom user interface that allows users to edit related properties that belong to a single category, such as text-related properties. In this walkthrough, you will build a category editor that allows users to edit the text-related properties of a control.

In this walkthrough, you perform the following tasks:

  • Create a WPF custom control project.

  • Create a category editor that can be used to edit the text-related properties of that control.

  • Create a class that inherits from CategoryEditor that represents the category editor for the control.

  • Create a class that inherits from IRegisterMetadata to register your new extended editor.

  • Test the category editor at design time.

You need the following components to complete this walkthrough:

  • Visual Studio 2008.

The first step is to create the project for the custom control. The control is a simple 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 C# named CustomControlLibrary.

    The code for CustomControl1 opens in the Code Editor.

  2. Add a reference to the following WPF Designer assembly.

    • Microsoft.Windows.Design

  3. In the Code Editor for CustomControl1, replace the code in the CustomControlLibrary namespace with the following code:

    C#
    public class CustomControl1 : Button
    {
        public CustomControl1()
        {
            if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
            {
                Content = "I'm in design mode";
            }
        }
    }
  4. Set the project's output path to "bin\".

  5. Build the solution.

The category editor that you will create requires some information about fonts and associated properties, so you will create a class that encapsulates that information. This class will be used as a data source by your category editor.

To create a class that encapsulates font property information

  1. Add a new WPF Custom Control Library project in Visual C# named CustomControlLibrary.Design to the solution.

    The code for CustomControl1 opens in the Code Editor.

  2. In Solution Explorer, delete the CustomControl1 file from the CustomControlLibrary.Design project.

  3. In Solution Explorer, delete the Themes folder from the CustomControlLibrary.Design project.

  4. Add a reference to the following WPF Designer assembly.

    • Microsoft.Windows.Design

  5. Add a reference to the CustomControlLibrary project.

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

  7. Add a new class named FontList to the CustomControlLibrary.Design project.

  8. In the Code Editor for FontList, replace the automatically generated code with the following code.

    C#
    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows.Media;
    using System.Collections.ObjectModel;
    using System.Windows;
    using System.Windows.Data;
    using System.Globalization;
    
    namespace CustomControlLibrary.Design
    {
        public class FontList : ObservableCollection<FontFamily>
        {
            public FontList()
            {
                foreach (FontFamily ff in Fonts.SystemFontFamilies)
                {
                    Add(ff);
                }
            }
        }
    
        public class FontSizeList : ObservableCollection<double>
        {
            public FontSizeList()
            {
                Add(8);
                Add(9);
                Add(10);
                Add(11);
                Add(12);
                Add(14);
                Add(16);
                Add(18);
                Add(20);
            }
        }
    
        public class FontStyleConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                FontStyle fs = (FontStyle)value;
                return fs == FontStyles.Italic;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                if (value != null)
                {
                    bool isSet = (bool)value;
    
                    if (isSet)
                    {
                        return FontStyles.Italic;
                    }
                }
    
                return FontStyles.Normal;
            }
        }
    
        public class FontWeightConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                FontWeight fs = (FontWeight)value;
                return fs == FontWeights.Bold;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                if (value != null)
                {
                    bool isSet = (bool)value;
    
                    if (isSet)
                    {
                        return FontWeights.Bold;
                    }
                }
    
                return FontWeights.Normal;
            }
        }
    
    }

The category editor will be created by using a XAML data template. This will be a simple user interface that binds to several text-related properties.

To create the template for the category editor

  1. Add a new class named EditorResources to the CustomControlLibrary.Design project.

  2. In the Code Editor for EditorResources, replace the automatically generated code with the following code.

    C#
    namespace CustomControlLibrary.Design
    {
        using System;
        using System.Collections.Generic;
        using System.Text;
        using System.Windows;
        public partial class EditorResources : ResourceDictionary {
            public EditorResources()
                : base()
            {
                InitializeComponent();
            }
        }
    }
  3. From the Project menu, click Add Resource Dictionary.

  4. Name the file EditorResources.xaml and click Add.

  5. In XAML view for EditorResources, replace the automatically generated XAML with the following XAML.

    <ResourceDictionary
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:PropertyEditing="clr-namespace:Microsoft.Windows.Design.PropertyEditing;assembly=Microsoft.Windows.Design"
        xmlns:Local="clr-namespace:CustomControlLibrary.Design"
        xmlns:Media="clr-namespace:System.Windows.Media;assembly=PresentationCore"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        x:Class="CustomControlLibrary.Design.EditorResources">
        <Local:FontList x:Key="FontFamilyList"/>
        <Local:FontSizeList x:Key="FontSizeList"/>
        <Local:FontStyleConverter x:Key="FontStyleConverter"/>
        <Local:FontWeightConverter x:Key="FontWeightConverter"/>
        <DataTemplate x:Key="TextCategoryEditorTemplate">
            <StackPanel Margin="5">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="50"/>
                    </Grid.ColumnDefinitions>
                    <ComboBox 
                        Grid.Column="0"
                        Margin="2"
                        ItemsSource="{Binding Source={StaticResource FontFamilyList}}" 
                        SelectedItem="{Binding [FontFamily].PropertyValue.Value}"/>
                    <ComboBox 
                        Grid.Column="1"
                        Margin="2"
                        ItemsSource="{Binding Source={StaticResource FontSizeList}}"
                        SelectedItem="{Binding [FontSize].PropertyValue.Value}"/>
                </Grid>
                <StackPanel Orientation="Horizontal">
                    <CheckBox 
                        Margin="2"
                        Content="Bold"
                        IsChecked="{Binding Path=[FontWeight].PropertyValue.Value, Converter={StaticResource FontWeightConverter}}"/>
                    <CheckBox 
                        Margin="2"
                        Content="Italic"
                        IsChecked="{Binding Path=[FontStyle].PropertyValue.Value, Converter={StaticResource FontStyleConverter}}"/>
                </StackPanel>
            </StackPanel>
        </DataTemplate>
    </ResourceDictionary>
  6. Build the solution.

Now that you have created the template for your category editor, you must create a class that inherits CategoryEditor to use the template as a custom editor, and you must register the new category editor.

To encapsulate and register your category editor

  1. Add a new class named TextCategoryEditor to the CustomControlLibrary.Design project.

  2. In the Code Editor for TextCategoryEditor, replace the automatically generated code with the following code.

    C#
    namespace CustomControlLibrary.Design {
        using System;
        using System.Collections.Generic;
        using System.Text;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Data;
        using Microsoft.Windows.Design.PropertyEditing;
    
        public class TextCategoryEditor : CategoryEditor {
    
            private EditorResources res = new EditorResources();
            public TextCategoryEditor()
            {
            }
    
            public override bool ConsumesProperty(PropertyEntry property)
            {
                return true;
            }
    
            public override DataTemplate EditorTemplate
            {
                get {
                    return res["TextCategoryEditorTemplate"] as DataTemplate;
                }
            }
    
            public override object GetImage(Size desiredSize)
            {
                return null;
            }
    
            public override string TargetCategory
            {
                get { return "Text"; }
            }
        }
    }
  3. Add a new class named Metadata to the CustomControlLibrary.Design project.

  4. In the Code Editor for Metadata, replace the automatically generated code with the following code.

    C#
    namespace CustomControlLibrary.Design
    {
        using System;
        using System.Collections.Generic;
        using System.Text;
        using Microsoft.Windows.Design.Metadata;
        using System.ComponentModel;
        using Microsoft.Windows.Design.PropertyEditing;
        using System.Windows.Media;
        using System.Windows.Controls;
        using System.Windows;
        using CustomControlLibrary;
    
        // Container for any general design-time metadata that we want to initialize.
        // Designers will look for a type in the design-time assembly that implements IRegisterMetadata.
        // If found, they will instantiate it and call its Register() method automatically.
        internal class Metadata : IRegisterMetadata
        {
    
            // Called by the WPF Designer to register any design-time metadata
            public void Register()
            {
                AttributeTableBuilder builder = new AttributeTableBuilder();
                builder.AddCustomAttributes
                        (typeof(CustomControl1),
                        new EditorAttribute(
                            typeof(TextCategoryEditor), 
                            typeof(TextCategoryEditor)));
                MetadataStore.AddAttributeTable(builder.CreateTable());
            }
        }
    }
  5. Build the solution.

Your category editor is now complete and ready to use. All that remains is to test it. To test the category editor, you will add a WPF application to your project, add the custom control to your WPF application, and view the category editor in action.

To test the category editor

  1. Add a new WPF Application project in Visual C# named DemoApplication to the solution.

    Window1.xaml opens in the WPF Designer.

  2. Add a reference to the CustomControlLibrary project.

  3. In XAML view for Window1.xaml, replace the automatically generated XAML with the following XAML. This XAML adds a reference to the CustomControlLibrary namespace and adds the CustomControl1 custom control. The button appears in Design view with text 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.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300" xmlns:my="clr-namespace:CustomControlLibrary;assembly=CustomControlLibrary">
        <Grid>
            <my:CustomControl1 Margin="30,30,30,30" Name="customControl11"></my:CustomControl1>
        </Grid>
    </Window>
  4. In Design view, select the control.

  5. In the Properties window, find the Text category.

    You should see a user interface for specifying Text properties that is different than other controls. You can select a font name and a font size from drop-down lists. You can specify bold and italic by selecting check boxes.

  6. Make changes to the properties represented in this category. Note that they changes are reflected in the control.

Date

History

Reason

January 2010

Updated editor template and metadata.

Customer feedback.

Tags What's this?: Add a tag
Community Content   What is Community Content?
Add new content RSS  Annotations
Local variable for FontWeightConverter.Convert method      Denis Vuyka   |   Edit   |   Show History
It seems that the code for FontWeightConverter.Convert was copy-pasted from FontStyleConverter.Convert before modifying it. It would be better naming local variable "fw" instead of "fs" to keep the source intuitive. If FontStyle is shortened to "fs" than FontWeight should be shortened to "fw".
The code for Metadata is incorrect.      opellegr   |   Edit   |   Show History
You'd think the "Enter Text" placeholder would have clued QA in ;)

The editor does not work as-is; the EditorAttribute shouldn't be applied to any particular property on the class, so that overload is inappropriate. The Register() method should look more like this:

public void Register()
{
AttributeTableBuilder builder = new AttributeTableBuilder();
builder.AddCustomAttributes
(typeof(CustomControl1),
new EditorAttribute(typeof(TextCategoryEditor), typeof(TextCategoryEditor)));
MetadataStore.AddAttributeTable(builder.CreateTable());
}
Processing
© 2012 Microsoft. All rights reserved. Terms of Use | Trademarks | Privacy Statement
Page view tracker