One of the most impressive technologies knocked out by Microsoft was Windows Communication Foundation. WCF unified various communication capabilities that exists in .NET 2.0 into a single, common, general framework. I think that it is the great achievement!
Recently I found Visual Studio 2010 training kit which includes the set of the hand-on-labs. One of the labs is called "Introduction To Managed Extensibility Framework". I read the instructions and it made me excited. Managed Extensibility Framework unifies the application extensibility capabilities! Actually .NET doesn't provide any "standard" API's for the applications extensibility. There is the plug-in model which should be implemented from scratch by every application that wants to support it. WPF provides the ways for look-and-feel extensibility by using styles, templates, skins and themes. But until now there was no the technology that unifies the extensibility like WCF unifies the communication. Managed Extensibility Framework is unifier guy for the extensibility. In this post I will try to describe my experience with MEF and provide the real-world code sample.
MEF architecture
The MEF architecture is shown here. It is very simple and the extensibility implementation can be described in the following steps:
- The application provides the set of extensibility points using MEF API.
- The application hosts the extensibility parts container using MEF API.
- The plug-in provides the set of extensibility parts using MEF API.
- The application imports the extensibility parts using MEF API.
The real-world sample.
Recently I was thinking about providing one more extensibility facilities for Data Dynamics Reports. Briefly - it should provide the capability to use the custom UI editors of some entities. For the real-world sample I selected the similar task:
WPF application provides the editor of the color of something. For example the application for facial composite construction uses the editor for eyes color. The application should provide the built-in color editor and end-user should be able to specify the custom color editor.
For example : the application provides built-in color editor that allows editing of the Red, Green and Blue color components. End-user can select the custom color editor that allows editing the Hue, Saturation and Brightness color components. The application should provide the visual feedback whenever the selected color is changed.
Implementation of the built-in color editor.
So, the built-in color editor allows editing of the Red, Green and Blue color components. WPF doesn't provide the standard color editor and we need to implement it. The implementation of the RGB color editor is straightforward task. We should:
- Define the dependency properties for the Red, Green and Blue components.
- Define the dependency property for the currently selected color.
- Provide the way which can be used by the hosting application to intercept the event when the selected color is changed.
interface IColorChanged
{
event ColorChangedEventHandler ColorChanged;
}
delegate void ColorChangedEventHandler(object sender, ColorChangedEventArgs args);
class ColorChangedEventArgs : EventArgs
{
public Color OldValue{ get; set;}
public Color NewValue{ get; set;}
}
class RGBColorEditor : Control, IColorChanged
{
private Color _color = Colors.Black;
public event ColorChangedEventHandler ColorChanged;
public ColorEditor()
{
SetValue(RProperty, _color.R);
SetValue(SelectedColorProperty, _color);
}
public static readonly DependencyProperty RProperty =
DependencyProperty.Register("R", typeof (byte), typeof (RGBColorEditor),
new PropertyMetadata((byte)255, OnRChanged));
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register("SelectedColor", typeof(Color), typeof(RGBColorEditor), new PropertyMetadata(Colors.Black, OnSelectedColorChanged));
public byte R
{
set { SetValue(RProperty, value); }
get { return (byte)GetValue(RProperty); }
}
private void OnRChanged(byte newValue)
{
_color.R = newValue;
SetValue(SelectedColorProperty, _color);
}
private static void OnRChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as ColorEditor).OnRChanged((byte)e.NewValue);
}
private void OnSelectedColorChanged(DependencyPropertyChangedEventArgs e)
{
if (ColorChanged != null)
ColorChanged(this, new ColorChangedEventArgs { NewValue = (Color)e.NewValue, OldValue = (Color)e.OldValue });
}
private static void OnSelectedColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as ColorEditor).OnSelectedColorChanged(e);
}
}
Custom color editor sample.
So, our custom editor control will allow editing of the Hue, Saturation and Brightness color components. The implementation would be very similar to RGBColorEditor. We implement IColorChanged interface, define the dependency properties for Hue, Saturation, Brightness and SelectedColor properties. Here is the snippet of the code:
class HSBColorEditor : Control, IColorChanged
{
private static Color ConvertHsbToRgb(double h, double s, double b)
{
//return new color created from h,s,b values;
}
public static readonly DependencyProperty RProperty =
DependencyProperty.Register("H", typeof(double), typeof(HSBColorEditor),
new PropertyMetadata(0, OnHChanged));
void OnHChanged(double newValue)
{
Color newColor = ConvertHsbToRgb(newValue, S, B);
SetValue(SelectedColorProperty, newColor);
}
private static void OnHChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as HSBColorEditor).OnHChanged((double)e.NewValue);
}
}
Extensibility without MEF.
I think that the most suitable way to provide the extensibility facility without MEF is using Provider Model Design Pattern. The application configuration file would have the list of the available color editors, the application would provide end-user with UI that shows this list and allows selecting the appropriate editor. Then the application would create the selected control instance and add it to the corresponding place.
MEF world.
For our application purpose we will use the color editor selector dialog which is responsible for collecting the custom color editor implementations and allowing end-user to select the appropriate editor. The color editor selector will try collecting the color editors from the assemblies located in the Plugin folder of the the directory of the executing application. Also, the built-in color editor should be added in the collection. So, the first thing that we should do if defining the extensibility points in the color editor selector. MEF allows to do that by using different ways, but the key is System.ComponentModel.Composition.ImportAttribute. Look at the code:
class ColorEditorSelector : Window
{
[Import(typeof(IColorChanged))]
private ExportCollection<Control> EditorCollection { get; set; }
public Control SelectedEditor { get; set; }
}
private void Compose()
{
var catalog = new AggregateCatalog();
var catalog1 = new AssemblyCatalog(typeof(ColorEditorSelector).Assembly);
catalog.Catalogs.Add(catalog1);
var catalog2 = new DirectoryCatalog("Plugins", true);
catalog.Catalogs.Add(catalog2);
var container = new CompositionContainer(catalog);
var batch = new CompositionBatch();
batch.AddPart(this);
container.Compose(batch);
}
public ColorEditorSelector()
{
Compose();
var list = new ListBox();
foreach(var colorEditor in EditorCollection)
{
var control = colorEditor.GetExportedObject();
list.Items.Add(control.GetType().Name);
}
}
[Export(typeof(IColorChanged))]
internal sealed class RGBColorEditor : Control, IColorChanged
[Export(typeof(IColorChanged))]
internal sealed class HSBColorEditor : Control, IColorChanged
In conclusion.
MEF is included in .NET Framework 4.0 CTP and I think it is really new standard for the application extensibility. Microsoft did the great job in design and implementation of MEF library and I will definitely will use it!
Great post Sergey! I'm a fan of the IServiceProvider interface myself. Looks like MEF is the new kid in town. I'll have to give this a try. Your post breaks it down very nicely. Well done indeed sir. :) Thanks!
ReplyDelete