Share via


Walkthrough: Implementing a Color Editor

The extensibility model for the Windows Presentation Foundation (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 inherits from IRegisterMetadata 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 2008.

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

    public class CustomControl1 : Button
    {
        public CustomControl1()
        {
            if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
            {
                Background = Brushes.Red;
            }
        }
    }
    
  4. Set the project's output path to "bin\".

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

    • Microsoft.Windows.Design
  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 ColorsListNamespace
    {
        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="ColorsListNamespace.ColorsListControl"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Local="clr-namespace:ColorsListNamespace" 
        xmlns:PropertyEditing="clr-namespace:Microsoft.Windows.Design.PropertyEditing;assembly=Microsoft.Windows.Design"
        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 ColorsListNamespace
    {
        public partial class ColorsListControl : System.Windows.Controls.UserControl
        {
            public static readonly RoutedEvent ClosePopupEvent = EventManager.RegisterRoutedEvent("ClosePopupEvent", RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(ColorsListControl));
    
            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); }
            }
    
            public event RoutedEventHandler ClosePopup
            {
                add { AddHandler(ClosePopupEvent, value); }
                remove { RemoveHandler(ClosePopupEvent, value); }
            }
    
            protected void RaiseClosePopupEvent()
            {
                RoutedEventArgs newEventArgs = new RoutedEventArgs(ColorsListControl.ClosePopupEvent);
                RaiseEvent(newEventArgs);
            }
    
            private void ItemsControl_Click(object sender, RoutedEventArgs e)
            {
                SelectedColor = (Color)((Button)sender).Tag;
                SelectedBrush = new SolidColorBrush(SelectedColor);
                RaiseClosePopupEvent();
            }
        }
    }
    
  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"
        xmlns:Local="clr-namespace:ColorsListNamespace"
        xmlns:Media="clr-namespace:System.Windows.Media;assembly=PresentationCore"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        x:Class="ExtendedEditorNamespace.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.

    namespace ExtendedEditorNamespace
    {
        using System;
        using System.Collections.Generic;
        using System.Text;
        using Microsoft.Windows.Design.PropertyEditing;
        using System.Windows;
        using ExtendedEditorNamespace;
    
        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.

    namespace ExtendedEditorNamespace
    {
        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 Cider to register any design-time metadata
            public void Register()
            {
                AttributeTableBuilder builder = new AttributeTableBuilder();
                builder.AddCustomAttributes
                    (typeof(CustomControl1), 
                    Control.BackgroundProperty,
                    PropertyValueEditor.CreateEditorAttribute(
                        typeof(BrushExtendedEditor)));
                MetadataStore.AddAttributeTable(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.

    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 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.Window1"
        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">Button</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