Make your WPF controls fully extensible using WCF IExtensibleObject pattern (.net 3.0)


 

Two days ago while reading MSDN documentation details on WCF’s NetDataContractSerializer I found a reference to the IExtensibleObject interface from "System.ServiceModel.dll". There were no details concerning what for the sample serializable object was exposing it as it was out of the sample scope. So I tried to figure out the purpose of this interface and how it can be reused in my applications when not dealing with Communication Foundation. Of course as you might have noticed the 2008 MSDN library contains almost no developer-readable information and the situation gets worse and worse ;), the Googling also gave me nothing except various annoying reprints of MSDN articles. It took me two days of investigations to figure out how much power this pattern can bring to common applications.

IExtensibleObject<T>

Exposing this interface "Enable an object to participate in custom behavior, such as registering for events, or watching state transitions […] For example, if you are implementing a TreeNode class and want to allow external code to add annotations and methods to some instances of TreeNode, you can have TreeNode implement IExtensibleObject<TreeNode>. This enables code that uses TreeNode to add objects that implement IExtensibleObject<TreeNode> to the Extensions collection." (MSDN). And four confusing samples from Windows Communication foundation… 😉

So starting implementation of our extensible class…

public class Person: IExtensibleObject<Person>
{
  public string Name { get; set; }

  public Person()
  {
    Extensions = new ExtensionCollection<Person>(this);     
    Name = "Default Name";     
  }

  #region IExtensibleObject<Person> Members

  public IExtensionCollection<Person> Extensions { get; private set; }
  #endregion
}

Actually I like those Persons and Dogs samples all over the MSDN so continued the tradition 🙂

IExtensibleObject<Person> requires you implementing only the strong-typed "Extensions" collection. We don’t require any extreme logic so using the default out-of-box ExtensionCollection. For testing and debugging purposes I also define the name of our person to something meaningful 🙂

IExtensionCollection<T>

Won’t be rewriting the obvious information, guess you should better take a look the original article. It is supposed to hold the extensions for the extensible object. Default ExtensionCollection class provides you the possibility of fetching the result sets from it entries directly according the type you are acquiring. For more details see Find<E> and FindAll<E> details for the collection.

IExtension<T>

This is what your "Extensions" collection expects you to provide 😉 Each your extension should expose this interface having the same <T> as the object being extended. Guess it might be more easy to get the idea from the sample below

public class PersonExtension : IExtension<Person>
  {
    #region IExtension<Person> Members

    public void Attach(Person owner)
    {
      Console.WriteLine("Attaching to owner called: " + owner.Name);
    }

    public void Detach(Person owner)
    {
      Console.WriteLine("Detaching from owner called: " + owner.Name);
    }

    #endregion
  }

IExtension<T> exposing needs two methods to be implemented by the author of the extension class: Attach and Detach. Very easy and the main things needed to extend the owner. Before each extension is added to the Extensions collection the Attach method is called to provide extension with possibility to initialize itself (cache the parent instance, attach events, change properties, whatever you need). Each time the extension is removed from the collection the Detach method is called so the extension class finalize itself, release resources, detach event handlers, etc. Each time for both steps extension gets it’s owner in order.

In this sample I won’t be doing anything except sending a notification with a current owner name just to be sure the code is correct.

Putting it all together

Person p = new Person() { Name = "Denis Vuyka" };
p.Extensions.Add(new PersonExtension());

You create an instance of the Person class, set the name property and add a new instance of the PersonExtension to it. Upon adding the extension you will see the "Attaching to owner…" message in your VS Output window. So the code is working.

 

Extending WPF UI elements

Another good sample would be showing some UI element being extended. I’ve chosen a Canvas but you are free to choose anything you need actually.

public class ExtensibleCanvas : Canvas, IExtensibleObject<ExtensibleCanvas>
{
  public ExtensibleCanvas()
    : base()
  {
    this.Background = Brushes.Transparent;
    this.Extensions = new ExtensionCollection<ExtensibleCanvas>(this);
  }
  
  #region IExtensibleObject<ExtensibleCanvas> Members

  public IExtensionCollection<ExtensibleCanvas> Extensions { get; private set; }

  #endregion
}

Again as in the previous sample we create our custom class inheriting IExtensibleObject and a Canvas control. As I will be playing with Mouse events I set the "Background" property of the canvas to "Transparent" to have a valid mouse responses. Also again we initialize our Extensions collection providing ourselves as an owner of the collection.

As I want just simply react to the MouseLeftButtonDown event of the ExtensibleCanvas the code sample for the extension will be the following

public class CanvasExtension : IExtension<ExtensibleCanvas>
{    
  #region IExtension<ExtensibleCanvas> Members

  public void Attach(ExtensibleCanvas owner)
  {
    owner.MouseLeftButtonDown += new MouseButtonEventHandler(owner_MouseLeftButtonDown);
  }

  void owner_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  {
    MessageBox.Show("Extension report: MouseLeftButtonDown event hooked!");
  }

  public void Detach(ExtensibleCanvas owner)
  {
    owner.MouseLeftButtonDown -= owner_MouseLeftButtonDown;
  }

  #endregion
}

During attach phase we wire up the MouseLeftButtonDown event with our own extension handler and release it during the detach phase. Each time the left button of the mouse if down we simply display a message box.

Putting it all together

Here’s the source for our sample window

<Window x:Class="CanvasExtensions.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ext="clr-namespace:CanvasExtensions"
    Title="Window1" Height="300" Width="300">
    <ext:ExtensibleCanvas x:Name="myExtensibleCanvas">
        
    </ext:ExtensibleCanvas>
</Window>

Here we just simply registered our local namespace and declared the ExtensibleCanvas instance called "myExtensibleCanvas".

Here’s the source code for our sample window

using System;
using System.Windows;

namespace CanvasExtensions
{
  /// <summary>
  /// Interaction logic for Window1.xaml
  /// </summary>
  public partial class Window1 : Window
  {
    public Window1()
    {
      InitializeComponent();

      // Testing extending the UI (Canvas extensions)
      myExtensibleCanvas.Extensions.Add(new CanvasExtension());
    }
  }
}

Just add a new instance of Canvas extension, run application and click somewhere on the canvas. You’ll get the message box with some text in it.

 

Extending dependency objects

As I immediately started to fill the power of this pattern (thanks to Microsoft guys who implemented it 🙂 ) I’ve decided to gain more control over the dependency objects that they can give me within their public properties and events. I’ve decided that there’s a cases when "wise" extension could have the need integrating deeper into the property system of the owner object thus I won’t have the possibility or time extending the owner to support "OnChanged" events.

So this example will show you how to extend the dependency objects (so read "all the UI controls present in WPF also" 🙂 ) that don’t want to tell anyone of their properties changes.

Let’s extend our Person class to be dependency object having "Name" property as a dependency one:

public class DoPerson : DependencyObject, IExtensibleObject<DoPerson>
{
  public string Name
  {
    get { return (string)GetValue(NameProperty); }
    set { SetValue(NameProperty, value); }
  }

  // Using a DependencyProperty as the backing store for Name.  This enables animation, styling, binding, etc...
  public static readonly DependencyProperty NameProperty =
      DependencyProperty.Register("Name", typeof(string), typeof(DoPerson), new UIPropertyMetadata("Default Name"));
      
  public DoPerson()
  {
    Extensions = new ExtensionCollection<DoPerson>(this);
  }

  #region IExtensibleObject<DoPerson> Members

  public IExtensionCollection<DoPerson> Extensions { get; private set; }

  #endregion
}

Now we have DependencyObject Person or DoPerson for short. Let’s imagine someone else had written this class and I have no possibility of tracking whether the name of DoPerson object has changed indeed. Thanks for WPF dependency system this is also possible via the "DependencyPropertyDescriptor". Let’s imagine your extension wants to know when the Name property of it’s owner is changed and want to slightly modify resulting value according to some internal rules.

Here’s how will look like the updated version of extension called DoPersonExtension. For educational purposes and code clearance I didn’t implement any checks and safe casting, hope you’ll understand that right

public class DoPersonExtension : IExtension<DoPerson>
{    
  DependencyPropertyDescriptor dpDes;
  bool NamePropertyLock = false;

  #region IExtension<DoPerson> Members

  public void Attach(DoPerson owner)
  {
    dpDes = DependencyPropertyDescriptor.FromProperty(DoPerson.NameProperty, typeof(DoPerson));
    dpDes.AddValueChanged(owner, HookOwnerNameChanged);
  }

  public void Detach(DoPerson owner)
  {
    dpDes.RemoveValueChanged(owner, HookOwnerNameChanged);
  }

  #endregion

  private void HookOwnerNameChanged(object sender, EventArgs e)
  {
    // avoid entering endless loop here
    if (NamePropertyLock) return;
          
    NamePropertyLock = true;

    ((DoPerson)sender).Name += "(hooked by extension)";

    NamePropertyLock = false;
  }
}

What a hell the extension is doing here… 🙂

It extracts the DependencyPropertyDescriptor from the DoPersonClass for his dependency property Name and attaches it’s own handler for value changed event that is raised regardless the DoPerson notifies about or not 😉 As now both the extension and owner classes are dealing with the same property it is obvious that some locking should be implemented not to bring application into endless loop when changing the Name and so avoiding overflow exceptions.

Upon detachment from the owner extensions collections DoPersonExtension clears it’s own garbage by removing the event handler for the property descriptor.

The testing lines for the sample above could be similar to following

// Testing extending of DependencyObject hooking the change of properties
// when no change events are supported withing the object
DoPerson doPerson = new DoPerson();
doPerson.Extensions.Add(new DoPersonExtension());

Console.WriteLine("Setting name: Denis Vuyka");
doPerson.Name = "Denis Vuyka";

Console.WriteLine("Current name: " + doPerson.Name);

I create a DoPerson instance and add the appropriate extension to it. Then I set the value of the "Name" property to my own name 🙂 and instantly put it in the Output window. As our extension hooked the Name setter we have the following message as a result:

"Current name: Denis Vuyka (hooked by extension)"

This is the simplest sample and the rest depends on your imagination.

Finalizing my article I’d like to admit that this pattern perfectly fits into MVC pattern as you can simply change the control flow vice versa. The extended object itself can iterate through the extension collection and call appropriate methods and do something he needs to do. So it means the Controller can also get the power unleashed 🙂

Have a nice coding…

 

Upcoming WPF Diagramming framework


 

As Francois Vanderseypen has disclosed some info in his post Mindmapping layout in WPF I’ve also decided to make a small announcement towards that 🙂

We joined our forces on the way implementing full "WPF replacement" for Netron 3 due to a small mindmapping application that resides on the top of the it. For the last couple of months it takes all my spare time but I must admit it really brings a lot of pleasure working with WPF and .net 3.5 in this way 🙂 We try to get the most of MVC and MS service buses approaches to perform scalable and highly extensible diagramming framework.

While you can see the layout details and screenshots here I’ll single out some additional features that might be of interest to developer audience.

Stress on lightweight MS service bus architecture close to MS designer re-hosting approaches so to be very intuitive for those guys familiar with Windows Forms and Workflow designers re-hosting. Some common services like unlimited Undo/Redo, Zooming/Panning, Drag/Drag to scroll, Single and Multiselection, Movement/Resizing, exporting content to different formats including images and XPS. Variety of Line connectors and different types of Shapes.

Stress on maximizing the extensibility of the core designer functionality. Nearly each part of the functionality can be plugged in/changed/disabled by the single developer according to the needs. Flexible service model.

Different drawing surfaces for diagram types like trees, mindmaps, flowcharts etc.

Even writing this very small list of features requires me a lot of investigations towards the native WPF features and possibilities of implementation this or that feature. Due to the lack of documentation and good samples for WPF (especially in diagramming and drawing) I have to surf with Reflector and google a lot 🙂 This also gives me some additional and rare material worth placing into separate blog posts concerning WPF itself. I’ll try to find some free hours in the nearest future for that 🙂

Of course we are only on the way preparing some world-ready app demos but the progress is very stable 😉

Meanwhile I would advice the people really interested in diagramming and WPF solutions stay on the line for other news within "The Orbifold" and my blog. Thanks for your attention.