Mar 31, 2009

How to Write Silverlight Design Time for All Designers: Visual Studio 2008, Blend 2; Blend 3, and Visual Studio 2010

Introduction

This is part of the series on design time implementation changes in Silverlight Toolkit March 2009 Release. This post focuses on the breaking changes in designer extensibility framework introduced in Silverlight 3/Blend 3/Visual Studio 2010, and how to develop Silverlight design time to support all designers: Visual Studio 2008/Blend 2, and Visual Studio 2010/Blend 3.

 

Silverlight 2 Design Time for Visual Studio 2008 and Blend 2

Visual Studio and Blend share the same designer extensibility framework, and use it for both WPF and Silverlight. WPF Designer Extensibility on MSDN is probably the best reference available for the designer extensibility framework. The December 2008 Release of Silverlight Toolkit is based on Silverlight 2 (aka SL2), and has designer support for Visual Studio 2008 (aka VS9) and Blend 2. It is a good real world example in demonstrating how to develop Silverlight 2 design time features for VS9 and Blend 2. My blog post Design Time Feature Implementation in Silverlight Toolkit explains in detail how it is done, and the implementation framework readers can use in their own design time implementation.

To recap, the Silverlight 2 design time framework for Visual Studio 2008 and Blend 2 in a nutshell:

  • MWD: the design time extensibility framework is exposed through a set of assemblies, most notably Microsoft.Windows.Design.dll, Microsoft.Windows.Design.Extensibility.dll, and Microsoft.Windows.Design.Interaction.dll. Collectively those assemblies are called MWD. They are installed under %DevEnvDir%\PublicAssemblies (C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies in 32bit OS, or  C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies in 64bit OS). They are also GAC-ed. Most design time projects need to reference those MWD dlls. Visual Studio 2008 and Blend 2 use MWD version 3.5.
    MWD in GAC
  • Naming Convention: Visual Studio and Blend load design time assemblies using a naming convention: for control assembly Foo.dll, VS and Blend will find its design time assemblies with the name Foo.Design.dll, Foo.VisualStudio.Design.dll, and Foo.Expression.Design.dll, in the same directory where Foo.dll is, or the Design sub directory. Visual Studio will load Foo.Design.dll and Foo.VisualStudio.Design.dll, while Blend will load Foo.Design.dll and Foo.Expression.Design.dll. So it is advised to put design time features shared across all designers in .Design.dll, and put Visual Studio and Blend specific design time features in .VisualStudio.Design.dll and .Expression.Design.dll respectively.
  • Entry Point: when Visual Studio or Blend loads a design time assembly, it uses reflection to find all classes that implement IRegisterMetadata, and calls its only method Register. Silverlight Toolkit December 2008 Release has a design time implementation framework detailed in previous blog post Design Time Feature Implementation in Silverlight Toolkit.

Below screenshots of Silverlight.Controls.Design.sln in Silverlight 2 Toolkit source in March 2009 Release demonstrates implementation of SL2 design time for VS9 and Blend 2 discussed above:
SL2 design projects

 

What Changed In Silverlight 3, Blend 3 and Visual Studio 2010

As stated in WPF Designer Extensibility Architecture on MSDN:

The WPF Designer supports the full extensibility framework. Expression Blend supports only property editors, metadata loading, and licensing. Blend does not support menu actions and adorners.

Blend 2 has above limited support for Silverlight design time too, but Visual Studio 2008 has little support for Silverlight design time (just icons, metadata loading for few attributes like ToolboxBrowsableAttribute): it is more a viewer than a designer. To have WPF parity for Silverlight design time support, Blend 3 and Visual Studio 2010 (aka VS10) have to introduce breaking changes to the designer extensibility framework (i.e. MWD), most notably:

  • MWD: most of the designer extensibility framework API stay the same, but their physical packaging changes: Microsoft.Windows.Design.dll is gone, and the version of all MWD assemblies changes from 3.5 to 4.0. Before Visual Studio 2010 Beta1, the only way to get the new MWD is via Blend 3 Preview, and its version is 3.7 instead of 4.0 for now:
    MWD in Blend 3
  • Entry Point: instead of reflect through IRegisterMetadata implementations, Blend 3 and Visual Studio 2010 use a new assembly attribute ProvideMetadataAttribute, and the metadataProviderType must implement IProvideAttributeTable:
    ProvideMetadataAttribute
  • Naming Convention: because of the breaking changes like MWD version and entry point, existing design time assemblies developed against 3.5 MWD will not load in Blend 3 and Visual Studio 2010, you have to write new design time against the new 4.0 MWD for Blend 3 and Visual Studio 2010. While this is OK for Blend (since you can only use Blend 3 with Silverlight 3 and Blend 2 with Silverlight 2), this creates a challenge for Visual Studio, since you can use Visual Studio 2008 for Silverlight 3 development, as well as Visual Studio 2010 for Silverlight 2 development.

Below table lists all supported scenarios and which version of MWD is used to load design time assemblies:

SL\Designer VS9 VS10 Blend2 Blend3
SL2 MWD3.5 MWD4.0 MWD3.5  
SL3 MWD3.5 MWD4.0   MWD4.0

We can’t build one design time assembly against two incompatible MWDs, so we have to create two separate design time assemblies, one against each MWD. Design time assemblies are loaded by name, and we can’t have two assemblies with the same name in the same location, so the design time assembly loading mechanism (i.e., the naming convention) has to change, to allow multiple versions of the same design time assembly coexist.

While waiting for the new naming convention to come out with Blend 3 and Visual Studio 2010 RTM, we can do something clever now to support both VS9 and VS10. Remember current naming convention allows Visual Studio and Blend each to load two design assemblies:

DLL\Designer Visual Studio Blend
Foo.Design.dll X X
Foo.VisualStudio.Design.dll X  
Foo.Expression.Design.dll   X

so we can build the two assemblies against different versions of MWD:

  • For Silverlight 3, we can have the shared .Design.dll build against MWD 4.0, so it can be loaded by both VS10 and Blend 3. We then have .VisualStudio.Design.dll build against MWD 3.5 and duplicate everything from shared .Design.dll, so it can be loaded by VS9. There is one minor issue though: we don’t have a place to put Visual Studio specific design time features for VS10, like using ToolboxBrowsableAttribute(false) to hide some controls from the increasingly crowded Visual Studio toolbox, since VS10 can’t load the .VisualStudio.Design.dll build against MWD 3.5. This will be solved with the new naming convention once Visual Studio 2010 RTM.
DLL\MWD\Designer MWD VS9 VS10 Blend3
Foo.Design.dll 4.0   X X
Foo.VisualStudio.Design.dll 3.5 X ?  
Foo.Expression.Design.dll 4.0     X
  • For Silverlight 2, follow the same approach: have the shared .Design.dll build against MWD 4.0, but have both .VisualStudio.Design.dll and .Expression.Design.dll build against MWD 3.5 and duplicate everything in shared .Design.dll. This way, both VS9 and Blend 2 will work just fine, but with the  same issue that there is no place to put Visual Studio specific features for VS10. Again, that will be solved with the new naming convention in VS10.
DLL\MWD\Designer MWD VS9 VS10 Blend2
Foo.Design.dll 4.0   X X
Foo.VisualStudio.Design.dll 3.5 X ?  
Foo.Expression.Design.dll 3.5     X

 

Design Time Feature Implementation in Silverlight 3 Toolkit

MWD

The design projects in Silverlight 3 Toolkit in March 2009 Release followed above mentioned scheme to support both Visual Studio 2008 and Visual Studio 2010, as well as Blend 3.

Download the March 2009 Release, unzip the source code, load Silverlight.Controls.Design.sln into Visual Studio 2008. Use the simpler Control.Input.Design project as example:

Controls.Input.Design.csproj in Silverlight 3 Toolkit Source

  • Design projects for VS9/Blend3 need to reference the new 4.0 MWD. Here we use pre-build event to invoke CopySystemWindows.bat to locate Blend 3 and copy the two  MWD dlls to Binaries directory.
  • Please note the minor changes to CopySystemWindows.bat file: we need to quote “%THIS_DIR%\Binaries\Blend3” (lines in yellow) to handle cases where there is a space in the path to the directory the source code is unzipped to.

If you don’t like the fragile CopySystemWindows.bat file or using build events, you can set Reference Paths to help Visual Studio find where the new MWD is, as shown below. The only caveat is that you need specify both %programfiles% and %programfiles(x86)% to make it build on both x86 and x64 machines.

Controls.Input.Design.csproj in Silverlight 3 Toolkit Source

The reference paths setting is persisted into the .csproj.user file:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <ReferencePath>c:\Program Files\Microsoft Expression\Blend 3 Preview\;c:\Program Files (x86)\Microsoft Expression\Blend 3 Preview\</ReferencePath>
  </PropertyGroup>
</Project>

Metadata.cs

I use the same implementation framework as described in Design Time Feature Implementation in Silverlight Toolkit, with some minor changes:

Metadata.cs in Silverlight 3 Toolkit Source

  • I moved links to shared files Extensions.cs and MetadataBase.cs from Controls.Design.Common folder to under a solution folder with the same name;
  • Metadata.cs in .Design and .Expression.Design projects use the new entry point: use ProvideMetadata assembly attribute, and implement IProvideAttributeTable interface;
  • add note that everything in AddAttributes() method needs to be duplicated in corresponding .VisualStudio.Design project.

The .VisualStudio.Design projects still use the old IRegisterMetadata interface in MWD 3.5, but:
image

  • under Controls.Design folder are links to all *Metadata.cs files in corresponding .Design project;
  • AddAttributes() method duplicates the content of AddAttributes() method in corresponding .Design project.

Controls.Input.Expression.Design.csproj in Silverlight 3 Toolkit Source

 

Conclusion

While the breaking changes in MWD may cause some inconvenience, it is necessary to implement as rich a design time experience for Silverlight as for WPF. This post describes a way to handle the breaking changes and still support all existing designers: Visual Studio 2008, Blend 2, and Blend 3. Once Visual Studio 2010 is publicly available, I will blog about the new naming convention, and how Toolkit design time work in Visual Studio 2010. Stay tuned!