Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

DesignSurface - WPF Designer Extension

0.00/5 (No votes)
13 Oct 2011 2  
Helps you extend WPF designer and design controls right on the designer

Introduction

The other day, I was surfing through the web for third party UIs for WPF. I was amused to see a vendor who claimed to have a complete design time support for their components. Having worked as a UI developer, I know the pain of navigating through the XAML to configure the UIs. Ribbon controls and docking controls are complex enough to cause you headaches. There comes the designer extensions for resolution.

A week later after reading many documentations, walkthroughs and experimenting with some simple little things, it turned out to be simple to bring things right on the designer. So at this moment, I am ready to get things done for our control library, library? Oops!! There are plenty of controls for the simplest need to the complex layouts requirements, are we going to write designer extender for every control? Nope, there must be a better way, and it turned out to be DesignSurface - the theme of this article.

So What is a DesignSurface?

And you know, I am a Windows Forms developer jump started into WPF. In my previous article, I tried to bring animation framework back to Windows Forms. This article brings the concept of ControlDesiger and DesignerVerbs together into DesignSurface to bring your WPF designer alive.

Preview.png

DesignSurface is an adorner capable of auto-generating property editors for the designer verbs collection you have in your designer class. It leaves you with less work to create a designer for your control, no matter what the control type is, it takes care of the value changes and updates the designer for you, all you have to do is derive from the DesignSurface and add the designer verbs of interest, and build it into a separate assembly and deploy with the control library. At this point, it is clear that this article is meant for the control developers / vendors, and not for the consumers of controls. If you are a consumer, don’t forget to ask your vendor for designer support.

Before We Begin, Things To Know

There are guidances in building and deploying a designer extender project. Here is a short glimpse of those rules.

  • Add ‘Design.dll’ suffix to the project name, say my control library is named Creatives.CustomControl, then the designer project should be named as Creatives.CustomControl.Design.
  • Add assembly : ProvideMetaData attribute to your namespace that indicates the designer your assembly provides metadata about the control designer.
  • Deploy your designer DLL with the control library into GAC or at the root folder from which the control library is picked up by the designer.
  • Designer extender is deployed in a different DLL and is valid in the Visual Studio designer context alone, it has no effect on the control’s runtime behavior.

All this information is necessary for the designer to pick up the right DLL and the control designer type, unless the designer detects these information and the DLL, you will see no difference in the designer, so be sure to check against the rules when you encounter nothing on the designer.

How to Get Started ?

That’s enough introduction about the DesignSurface. Let’s take a look at how to begin with the DesignSurface, I assume you already have your own custom control and you are going to make use of DesignSurface to add designer support. For demonstration, I have my DesignerExpander a simple control derived from Expander has nothing else literally, note I named my control library Creatives.CustomControl.

namespace Creatives.CustomControl
{
	public class DeisgnerExpander : Expander
	{
		static DeisgnerExpander() { }
	}
}

And now I need to provide my customers a good design time experience, all I going to do is to define designer verbs and my DesignSurface will take care of the rest of the job. DesignerExpander is actually a Expander control and I need the following properties to be exposed on the designer, two of them define the controls appearance and the other two define control behavior, and I am going to add headers for the categories too.

  • DeisgnerExpander.HeaderProperty – The title text on the control
  • DeisgnerExpander.ContentProperty - The text on the body of the control
  • DeisgnerExpander.IsExpandedProperty – Gets or sets whether the content is expanded or not
  • DeisgnerExpander.VisibilityProperty – Gets or sets the visibility of the control

The first thing is to set up a project for the designer, and I named my designer project Creatives.CustomControl.Desiger therefore I satisfied the first rule, and the second one is to add the attribute to the namespace and this is how the class will look like when you are done.

[assembly: ProvideMetadata(typeof(Creatives.CustomControl.Design.CustomControlDesigner))]
namespace Creatives.CustomControl.Design
{
	public class CustomControlDesigner : DesignSurface
	{
         }
}

And the second thing is to implement IProvideDesignerVerbs used to collect the designerverbs, and here I add the above four properties to the designer verb collection, remember things appear in the order they are added to the collection.

public DesignerVerbCollection GetDesignerVerbs()
{
    DesignerVerbCollection verbs = new DesignerVerbCollection();

    verbs.AddHeader("CustomControl Editor");
 
    verbs.AddHeader("Appearance");
    verbs.AddProperty(DeisgnerExpander.HeaderProperty, "Header");
    verbs.AddProperty(DeisgnerExpander.ContentProperty, "Content");

    verbs.AddHeader("Behavior");
    verbs.AddProperty(DeisgnerExpander.IsExpandedProperty, "IsExpanded");
    verbs.AddProperty(DeisgnerExpander.VisibilityProperty, "Visibility");

    return verbs;
}

Finally, you have two things left to be completed, to register the designer type with the DesignSurface in its constructor, and to provide information on the control type. Here goes the code:

DesignSurface.RegisterDesignerVerbProvider(typeof(DeisgnerExpander), 
	typeof(CustomControlDesigner));

public override Type ControlType { get { return typeof(DeisgnerExpander); } }

That’s it! Build the project and deploy with the controls DLL, believe me you now have the designer support added for your custom control. Go create a test project, drag and drop your custom control, and selecting the custom control will give you the editor right on the designer. How awesome is that to have it!!!

How All These Works?

Yeah, it’s working, and I found it simple and elegant. But how do all these things work? I understand you have this question in your mind, and I did it on purpose. ;) I started writing articles from the day I understood how things works, however my articles received no attentions, even I’ve got comments on my writing too :P It's more than a year and things have gotten better than before. This is an initiative to make a difference and tried things upside down, and I believe it works out for me this time. Don’t forget to leave your comments and suggestions, better or worse they make me improve and I am sure I will help you write lesser code in my future articles.

Back to the Point: How It Works

Let’s make a quick tour on the helpers and wrappers used in the project before we dive into the concepts.

  • DesignerVerb – defines an Item shown in the design time adorner, be it a Header or a PropertyEditor. Header holds a text that will be displayed, and property editor holds a dependency property and its display name.
  • DesignerVerbCollection – defines an IEnumerable, lets you add a property, a header, or a DesignerVerb to the collection.
  • And I have a PropertyGrid and a GridFactory to generate the corresponding editor for the given dependency property type. PropertyGrid does a simple job and consumes the designer verbs and builds the property editors with the help of GridFactory, most of the work done in PropertyGrid is adding the FrameWorkElements to the panel, and setting appropriate values for the grid columns and grid rows.
  • GridFactory – This factory implementation decides the editor type for the given properties and generates the editor. (For instance, textbox for string type. Forgive me, I don’t have editors for brushes and other color types.)
public static PropertyEditor GetEditor(DependencyProperty property)
{
    if (property == null) return null;

    Type valueType = GetValueType(property);
    Type editorType = GetEditorType(valueType);

    FrameworkElement editor = Activator.CreateInstance(editorType) as FrameworkElement;

    if ( editor!=null && editorType == typeof(ComboBox))
        (editor as ComboBox).ItemsSource = Enum.GetValues(valueType);

    return new PropertyEditor(editor, property);
}

You now have the knowledge on how most of the things are worked out, and there is one thing left to be unfolded - the FeatureProvider WPF defines many feature providers, each contributes a specific design time behavior. AdornerProvidres, ContextMenu Provider, DefaultInitializer and more. This article makes use of PrimarySelectionAdornerProvider – provides an adorner in the designer when a selection is made on the designer. When a control is activated, it invokes AdornerProvider.Activate member and here we are going to do all the stuff we need on the designer.

protected override void Activate(Microsoft.Windows.Design.Model.ModelItem item)
{
    ModelItem = item;

    InitSurfacePanel();

    SurfacePanel.PropertyGrid.UpdateLayout();

    base.Activate(item);
}

That’s okay, what is this new ModelItem? Let me explain, ModelItem is an abstraction of the view (the element being selected in the designer), with this reference we are going to sync the value changes in the property editors to the model item.

ModelItem.Properties[property.Name].SetValue(newValue);

What’s Next?

That’s it, my job is done here. Hope you enjoyed it. Don’t forget to leave your comments and suggestions. See you in my next post!.

One last word! This will work for Visual Studio 2010 alone, VS2008 has a different architecture!

Happy coding!!!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here