Once upon a time, in a galaxy far away, I wrote a couple of tutorials on how to write extensions for Expression Blend. Today there’s still not much information available on how to write your own extensions. But it is still possible to extend Blend for Visual Studio. The way to do this is slightly changed though.
I recently came across a question at StackOverflow.com that made me decide to update/rewrite some that older tutorials.
Instead of building some dummy extension I’d like to show you how to build an extensions that might be useful for developers of Windows Store apps and Windows Phone apps. The extension is going to display a list of characters of the Segoe UI Symbols font you can use in you apps.
Getting Started
To be able to build directly into the extensions folder of Blend and have Visual Studio debug them you’ll have to start Visual Studio as administrator.
When Visual Studio is started create a new WPF User Control Library project. This will add the necessary references for creating a user control that will be our panel in Blend.
I named the extension SegoeSymbol, but feel free to use anything you like…
Next we’ll have to change some of the configurations of the project to have Blend pick the extension right from it’s extensions folder.
Go to the properties of the the project and change the Assembly Name on the Application tab to “SegoeSymbol.Extension” . This will name the assembly SegoeSymbol.Extension.dll. All extensions need to have the .extension.dll suffix.
Also change the Target framework to .NET Framework 4.5.
Set the Output Path on the Build tab to “c:\Program Files (x86)\Microsoft Visual Studio 11.0\Blend\Extensions\” . If you have installed Visual Studio 2012 in any other location this path might be different. Also, make sure the Extensions folder exists.
The last property to changes is the Start Action on the Debug tab. Set this to the full path to “Blend.exe”. This way Visuals Studio will start Blend when you hit F5 and attaches the debugger to the process enabling you to set breakpoints and such.
Code
Before we can do anything with this extension we need some code. The project we just created doesn’t have a class file yet, so we’ll have to add a one. Name it SegoeSymbol. This class will be the entry point for the extension.
This class has to implement the IPackage interface from Blend. To get this interface you’ll have to reference Microsoft.Expression.Extensibility.dll. You can this assembly in the root folder of Blend, the same folder you’ve found Blend.exe in a bit earlier.
Implementing this interface will give you two methods, Load and Unload. The Load method is called when Blend loads the extension. And, you might have guessed, Unload is called when Blend unloads the extension. Blend will load the the extension when it is started and unload the extension when it is shut down.
Blend for Visual Studio uses the Managed Extensibility Framework, or MEF, to load extensions. MEF is included in the .NET framework. All you have to do is find the reference to System.ComponentModel.Composition. MEF works by decorating a class you want to enable as extensions with the Export attribute and providing that with the type you’d like to export. The other side, Blend in this case, is using an Import based on the IPackage interface to get to the extension. By now your code should look something like this:
namespace SegoeSymbol
{
using System.ComponentModel.Composition;
using Microsoft.Expression.Extensibility;
[Export(typeof (IPackage))]
public class SegoeSymbol : IPackage
{
public void Load(IServices services)
{
}
public void Unload()
{
}
}
}
One last thing before you build and run your code. By default all assemblies referenced that are not in the GAC are copied to the output directory. Because Blend has everything you need, you can set the Copy Local property of all references to False. If you need to have a reference included with your extensions you can leave that one to true.
If you like you can try it out now… If you place breakpoints at the opening brackets of both methods you will see them both hit when opening and closing Blend.
A small thing you might run into when developing extensions is that Blend will lock the extension assemblies when it has them in use. This means you can’t have Visual Studio compile and build you extensions when Blend is running. Even to the point where if you close Blend a process, Microsoft Visual Studio XAML UI Designer (32 bit), will keep the files locked. I have to look a bit more into why and when this process is used. But you often have to go into the process explorer and end the task by hand. If you hit the “stop” button in Visual Studio it will kill Blend most of the time, which unlocks the files. I don’t know if this is a bug or a feature.
I’ll get back to this class in a second. First we should have a look at the…
User Interface
An extension needs a user interface, right? The user interface doesn’t do anything with Blend itself or with the extension so I’ll go over it rather quickly.
Because we started with the WPF User Control Library template we already have one. I renamed this to CharacterSelector.
Lets have a look at the XAML part of the UI. It’s basically just a ListBox with some styling. I changed the ItemsPanelTemplate (on line 27) to contain a WrapPanel instead of a StackPanel so all icons will be placed next to each other until a row is full at which point it will wrap to the next row.
The ItemTemplate, defined on line 14, draw a border around a TextBlock that contains the character in the Segoe UI Symbol font.
The whole thing is combined in a ListBox, line 34. The selection changed event is handled in the code behind.
<UserControl x:Class="SegoeSymbol.CharacterSelector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="322.667"
Width="Auto"
Height="Auto"
MinWidth="325">
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate x:Key="ItemTemplate">
<Border BorderThickness="1"
Width="50"
Height="50"
BorderBrush="White">
<Grid HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock Text="{Binding Character}"
FontFamily="Segoe UI Symbol"
FontSize="24" />
</Grid>
</Border>
</DataTemplate>
<ItemsPanelTemplate x:Key="ItemsPanelTemplate1">
<WrapPanel />
</ItemsPanelTemplate>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<ListBox x:Name="listBox"
Background="{x:Null}"
ItemTemplate="{DynamicResource ItemTemplate}"
ItemsPanel="{DynamicResource ItemsPanelTemplate1}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
SelectionChanged="listBox_SelectionChanged" />
</Grid>
</UserControl>
In the code behind I fill the selection with characters and handle the selection. The list of characters is created with a simple for loop at line 16. This loop instantiates a helper class MetroIcon (see line 57) and fills it with a number and the corresponding character. After the loop the collection of characters is assigned to the ItemsSource of the ListBox (line 27).
When an element of the ListBox is selected, when the extension is running in Blend, the Listbox_SelectionChanged event handler is called (line 27). To keep things simple, the character selected is copied to the Windows clipboard. As text, but formatted in a way xaml-text can handle it.
Here’s the code:
namespace SegoeSymbol
{
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
public partial class CharacterSelector : UserControl
{
public List<MetroIcon> Collection = new List<MetroIcon>();
public CharacterSelector()
{
Collection = new List<MetroIcon>();
for (int i = 0xE100; i < 0xE271; i++)
{
Collection.Add(new MetroIcon()
{
Character = ((char)(i)).ToString(),
Value = i
});
}
InitializeComponent();
listBox.ItemsSource = Collection;
}
private void listBox_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
if (e.AddedItems != null && e.AddedItems.Count>0)
{
var metroIcon = e.AddedItems[0] as MetroIcon;
if (metroIcon != null)
{
string val = string.Format("&#x{0:X};", metroIcon.Value);
Clipboard.SetData(DataFormats.Text,val);
}
listBox.SelectedItem = null;
}
}
}
public class MetroIcon
{
public string Character { get; set; }
public int Value { get; set; }
}
}
Lets have a look at gluing the whole thing together.
Adding the control to Blend
We’re going to make some magic happen in the Load method created earlier in this tutorial. Blend calls the load method and passes it its IServices implementation. You can use this class to get access to various services that Blend exposes. One useful service is the IWindowService. This service is responsible for the panels you see all around Blend. To be able to use the IWindowService interface you need reference Microsoft.Expression.Framework.dll and make sure you set “Copy Local” on false.
To get access to IWindowService you need to call the GetService method on services that was passed as a parameter. When you give this generic method the type IWindowService, it will return an instance of that type that Blend is using.
Next, create an instance of the CharacterSelector control we created in the previous chapter. If you someday need IServices in a user control, just extend the constructor and pass it in.
The last thing to do is register the control as a “Palette”. Palettes are the panels in Blend. To register a new palette call the RegisterPalette method on windowService and give it an identifier, the instance of the control and a title. You can provide a fourth, optional, parameter to set some extended properties on the palette, but we’ll use the defaults for now.
The Load methods should be looking something like this by now:
public void Load(IServices services)
{
IWindowService windowService = services.GetService<IWindowService>();
CharacterSelector characterSelector = new CharacterSelector();
windowService.RegisterPalette("SegoeSymbolExtionsion",
characterSelector,
"SegoeSymbol");
}
At this point you should be able to hit F5 and find the extension in Blend.
After Blend is started you need to create or open a project. If you look in the Window menu, the palette is shown right there.
When the SegoeSymbol panel is visible it behaves like all other panels in Blend. In this case it is docked next to the Resources panel.
What’s next?
I think we’ve got the basics down at this point. There are a lot more things you can do, like creating menus and commands or using documents and dialogs.
Please note that anything you just read is based on my own findings. There is no documentation available on writing these extension,
no support and no guarantee it will even work in a next version of Blend for Visual Studio.