Adding Design Time Metadata to Cider and Blend

The extensibility points in Cider are all metadata based.  That means that you use attributes to attach design time features to your custom controls.  This was also true with the extensibility in Windows Forms however there is a significant difference. In Windows Forms, you might've done something like the following:

[Designer(“CoolDesigner, CustomControl.Design")]
public class CoolControl {
  [Category(“Layout”)] 
  [Browsable(true)]
  public string CoolTitle { … }
 …  }

Notice how the attribute is added directly to the control.  This means that the assembly in which that attribute is defined needs to be present whenever that control is loaded.  In Windows Forms, this was ok since the design time attributes shipped with the .Net Framework redistributable.  In the Cider case, this is not true, Cider assemblies are shipped only with Visual Studio 2008 and cannot be redistributed by 3rd parties.

Additionally, changing the metadata requires that the control be re-built.  Since WPF shipped their controls with Vista and Cider is shipping with Visual Studio 2008, we needed to be able to add metadata to existing controls.

The solution is that at design time, lookups for metadata are made to a "MetadataStore".  That is, if you add attributes for a type to the MetadataStore, it will be as if you had added them declaratively -- but only at design time.

 The question then becomes: how do I add my 3rd party metadata to the MetadataStore?  We use a naming convention to load 3rd party "metadata assemblies".  A Metadata Assembly is simply an assembly that contains an implementation of Microsoft.Windows.Design.Metadata.IRegisterMetadata which is defined in Microsoft.Windows.Design.dll.

 When an assembly named "CustomAssembly" is loaded in Cider or Blend, the designer will look for an assembly named CustomAssembly.Design.dll.  If found, it will load it, instantiate any types that implement IRegisterMetadata and call the Register() method.  This is your opportunity to add metadata to the metadata store. 

 Cider will also look for an assembly named CustomAssembly.VisualStudio.Design.dll and load it (after loading CustomAssembly.Design.dll) for Cider specific metadata.  Likewise Blend will do the same for assemblies named CustomAssembly.Expression.design.dll.  This is important because if Cider only extensibility features such as Adorners and MenuActions were added in in a CustomAssembly.Design.dll, this would cause Blend to fail to load that assembly on machines that only have Blend installed as it will have references to Cider only assemblies such as Microsoft.Windows.Design.Interaction.dll or Microsoft.Windows.Design.Extensibility.dll -- only Microsoft.Windows.Design.Dll is shared between Cider and Blend.

The implementation of the IRegisterMetadata.Register() method will look like this:

internal class Metadata : IRegisterMetadata {
    public void Register() {
        AttributeTableBuilder builder = new AttributeTableBuilder();

  // To add an attribute such as this using the MetadataStore API
  // [Feature(typeof(OpacitySliderAdornerProvider))]
  // public class ButtonWithDesignTime { }

   builder.AddCustomAttributes(typeof(ButtonWithDesignTime), new FeatureAttribute(typeof(OpacitySliderAdornerProvider)));
   MetadataStore.AddAttributeTable(builder.CreateTable());  
   }
}

There is also an override that allows you to specify the property to add the attribute to.

Note that you can still use attributes that are defined in the .Net runtime assemblies such as BrowsableAttribute and the like declaratively as you did in Windows Forms however you lose the ability to change your design time metadata independently of your control.

This functionality is available for you to play with in Visual Studio 2008 beta 2 and later.

For an example of a project with extensibility features that are wired up using the MetadataStore and Metadata Assemblies, please see https://blogs.msdn.com/jnak/archive/2007/08/10/recording-of-my-teched-talk-on-cider-wpf-designer-design-time-extensibility.aspx