Share via


Walkthrough: Implementing a Color Editor

The extensibility model for the WPF Designer for Visual Studio allows you to create custom value editors for properties in the Properties window at design time. Editors can be either inline editors, which allow you edit values directly in the Properties window, or extended editors, which allow you to provide an additional UI for editing a property outside of the Properties window. To demonstrate how to create an extended editor, this walkthrough provides step-by-step procedures for creating an color editor for the Background property of a control. This extended editor is opened from an inline editor in the Properties window.

In this walkthrough, you perform the following tasks:

  • Create a WPF custom control project.

  • Create a user control that will act as the extended editor.

  • Create an inline editor that can be used to edit the property value in the Properties window and open the extended editor.

  • Create a class that inherits from ExtendedPropertyValueEditor that is used to connect the editors to the class you want to provide custom editing for.

  • Create a class that implements the IProvideAttributeTable interface to register your new extended editor.

  • Test the extended editor at design time.

Prerequisites

You need the following components to complete this walkthrough:

  • Visual Studio 2010.

Creating the Custom Control

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. In the Code Editor for CustomControl1, replace the code in the CustomControlLibrary namespace with the following code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace CustomControlLibrary
    {
        public class CustomControl1 : Button
        {
            public CustomControl1()
            {
                if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
                {
                    Background = Brushes.Red;
                }
            }
        }
    }
    
  3. Set the project's output path to "bin\".

  4. Build the solution.

Creating the User Control for the Extended Editor

The control that you created in the previous procedure is the control that you will attach your custom color editor to. In this procedure, you will create another user control that will act as the extended editor.

To create the user control that will act as the extended editor

  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. Add a reference to the following WPF Designer assemblies.

    • Microsoft.Windows.Design.Extensibility

    • Microsoft.Windows.Design.Interaction

  4. Add a reference to the CustomControlLibrary project.

  5. 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.

  6. Add a new class named ColorsList to the CustomControlLibrary.Design project.

  7. In the Code Editor for ColorsList, replace the automatically generated code with the following code.

    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows.Media;
    using System.Reflection;
    using System.Collections.ObjectModel;
    
    namespace CustomControlLibrary.Design
    {
        public class ColorsList : ObservableCollection<Color>
        {
            public ColorsList()
            {
                Type type = typeof(Colors);
                foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.Static))
                {
                    if (propertyInfo.PropertyType == typeof(Color))
                    {
                        Add((Color)propertyInfo.GetValue(null, null));
                    }
                }
            }
        }
    }
    
  8. Add a new User Control (WPF) named ColorsListControl to the CustomControlLibrary.Design project.

    The code for ColorsListControl.xaml opens in the designer.

  9. In XAML view for ColorsListControl.xaml, replace the automatically generated XAML with the following XAML.

        <UserControl x:Class="CustomControlLibrary.Design.ColorsListControl"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Local="clr-namespace:CustomControlLibrary.Design" 
        xmlns:PropertyEditing="clr-namespace:Microsoft.Windows.Design.PropertyEditing;assembly=Microsoft.Windows.Design.Interaction"
        Height="184" Width="260" Background="White">
        <UserControl.Resources>
            <Local:ColorsList x:Key="colors"/>
            <Style TargetType="{x:Type Button}">
                <EventSetter Event="Click" Handler="ItemsControl_Click"/>
            </Style>
        </UserControl.Resources>
    
        <ItemsControl 
            ItemsSource="{Binding Source={StaticResource colors}}" 
            HorizontalContentAlignment="Stretch" 
            VerticalContentAlignment="Stretch" 
            HorizontalAlignment="Stretch" 
            VerticalAlignment="Stretch">
            <ItemsControl.Template>
                <ControlTemplate TargetType="ItemsControl">
                    <Border CornerRadius="5" >
                        <WrapPanel Orientation="Horizontal"
                                   VerticalAlignment="Center"
                                   HorizontalAlignment="Center">
                            <ScrollViewer>
                                <ItemsPresenter/>
                            </ScrollViewer>
                        </WrapPanel>
                    </Border>
                </ControlTemplate>
            </ItemsControl.Template>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button Tag="{Binding}" Command="{x:Static PropertyEditing:PropertyValueEditorCommands.ShowInlineEditor}">
                        <Button.Template>
                            <ControlTemplate>
                                <Border Width="30" Height="30" BorderBrush="Black" BorderThickness="1" CornerRadius="5">
                                    <Rectangle Width="22" Height="22" ToolTip="{Binding}">
                                        <Rectangle.Fill>
                                            <SolidColorBrush Color="{Binding}"/>
                                        </Rectangle.Fill>
                                    </Rectangle>
                                </Border>
                            </ControlTemplate>
                        </Button.Template>
                    </Button>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </UserControl>
    
  10. In Solution Explorer, expand ColorsListControl.xaml and open ColorsListControl.xaml.cs.

  11. In the Code Editor for ColorsListControl, replace the automatically generated code with the following code.

    using System;
    using System.Linq;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace CustomControlLibrary.Design
    {
        public partial class ColorsListControl : System.Windows.Controls.UserControl
        {
            public ColorsListControl()
            {
                InitializeComponent();
            }
    
            public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register("SelectedColor", typeof(Color), typeof(ColorsListControl), new FrameworkPropertyMetadata(null));
            public Color SelectedColor
            {
                get { return (Color)base.GetValue(SelectedColorProperty); }
                set { base.SetValue(SelectedColorProperty, value); }
            }
    
            public static readonly DependencyProperty SelectedBrushProperty = DependencyProperty.Register("SelectedBrush", typeof(SolidColorBrush), typeof(ColorsListControl), new FrameworkPropertyMetadata(null));
            public SolidColorBrush SelectedBrush
            {
                get { return (SolidColorBrush)base.GetValue(SelectedBrushProperty); }
                set { base.SetValue(SelectedBrushProperty, value); }
            }
    
            private void ItemsControl_Click(object sender, RoutedEventArgs e)
            {
                SelectedColor = (Color)((Button)sender).Tag;
                SelectedBrush = new SolidColorBrush(SelectedColor);
            }
        }
    }
    
  12. Build the solution.

  13. Reload ColorsListControl.xaml in the designer and you should see the extended editor UI appear in Design view.

Creating the Templates for the Color Editor

The inline editor for your color editor is less complex than the extended editor and can be created with a XAML data template. You will also create a data template for the extended editor that specifies to use the user control you created in the previous procedure.

To create the template for the color 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.

    namespace ExtendedEditorNamespace
    {
        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.

    The code for EditorResources.xaml opens in the designer.

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

        <ResourceDictionary
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:PropertyEditing="clr-namespace:Microsoft.Windows.Design.PropertyEditing;assembly=Microsoft.Windows.Design.Interaction"
        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">
    
        <DataTemplate x:Key="BrushExtendedEditorTemplate">
            <Local:ColorsListControl SelectedBrush="{Binding Value, Mode=TwoWay}"/>
        </DataTemplate>
    
    
        <DataTemplate x:Key="BrushInlineEditorTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="1*"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <TextBox Grid.Column="0" Text="{Binding StringValue}"/>
                <PropertyEditing:EditModeSwitchButton Grid.Column="1"/>
            </Grid>
        </DataTemplate>
    
    </ResourceDictionary>
    
  6. Build the solution.

Encapsulating the Templates and Registering the Editors

The hard work is finished. Now that you have created the extended and inline editor templates for your color editor, you can create a class that encapsulates them and then registers them with your custom control.

To encapsulate and register the editors

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

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

    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.Windows.Design.PropertyEditing;
    using System.Windows;
    
    namespace CustomControlLibrary.Design
    {
        public class BrushExtendedEditor : ExtendedPropertyValueEditor
        {
            private EditorResources res = new EditorResources();
    
            public BrushExtendedEditor()
            {
                this.ExtendedEditorTemplate = res["BrushExtendedEditorTemplate"] as DataTemplate;
                this.InlineEditorTemplate = res["BrushInlineEditorTemplate"] as DataTemplate;
            }
        }
    }
    
  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.

    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;
    
    // The ProvideMetadata assembly-level attribute indicates to designers
    // that this assembly contains a class that provides an attribute table. 
    [assembly: ProvideMetadata(typeof(CustomControlLibrary.Design.Metadata))]
    
    namespace CustomControlLibrary.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(CustomControl1),
                        "Background",
                        PropertyValueEditor.CreateEditorAttribute(
                            typeof(BrushExtendedEditor)));
    
                    return builder.CreateTable();
                }
            }
        }
    }
    
  5. Build the solution.

Testing the Color Editor

You have just completed creating your color editor. All that remains now is to test it. To test your editor you will add a WPF application project to your solution and add your custom control. You will then change the Background in the Properties window and see your new editor in action.

To test the color editor

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

    MainWindow.xaml opens in the WPF Designer.

  2. Add a reference to the CustomControlLibrary project.

  3. In XAML view for MainWindow.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 a red background, indicating that the control 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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://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 Designer view, select the control.

  5. In the Properties window, click the drop-down button next to the Background property. A visual color editor is displayed instead of the default list of colors.

  6. Select a color from the editor. The background of your custom control changes to that color.

See Also

Tasks

Walkthrough: Implementing an Inline Value Editor

How to: Create a Value Editor

Other Resources

Creating Custom Editors

WPF Designer Extensibility