Categories: .Net, C#, WPF
Posted by
mheydt on
3/16/2008 4:41 PM |
Comments (0)
During the process of learning how to do "real things" with WPF, one of the major issues that I came across as I think it is not documented fairly well is how to create a user control that displays multiple items (and without being a ItemsListControl) that have a default look, but which allows the user to completely change the way in which those items render themselves.
The example I will use is of a force directed layout control that I have been developing. For the unititiated, a force directed layout control using springs takes a network of items and repositions them in an order that is visually aesthetic and where each node is positioned as though it is "forced" away from neighbor nodes by "springs."
As an example, here is the control I am building in action. This picture is the network with nodes randomly place in space prior to layout (nodes are positioned randomly in code behind):

In this app, pressing a key causes the force layout engine to reposition the nodes to look as follows:

The process of force directed layout is not the topic of this post, but how to write your control to allow the user to change the look of controls used by your control without changing code.
To start this explanation, let me exhibit the XAML used by this control:

There isn't a whole lot to this control as the actual creation of nodes and lines is done in the code behind. The important part is that the templete for the node and lines between the nodes are defined as ControlTemplate's and stored as a resource in the control. The code behind will then dynamically load the control template when needed and use it to create an actual WPF control that is set to be a Child element of the Canvas.
To create an actual WPF control from a ControlTemplate stored as a resource and add it as a Child of the canvas is fairly easy as it only requires a few lines of code:

So, whenever a node is needed in the graph, this code will create one of the teal balls and place it on the canvas.
Now, say that I have packaged this control as a DLL and redistribute it, but I want to allow the user of the control to be able to use a different element for a node instead of the default teal ball. What needs to be done to support this is to add a DependencyProperty to the control that allows the person using the control to specify the key of their own control template to use, and to also change the code snippet above to look for the name of the control through the propery instead of being hard coded.
The DependencyProperty is coded as such:

The declaration of this property allows a user of the control to define a new control template to use for the nodes instead of the default by binding to this property in the XAML of the control client.
For example, this XAML reuses the control and also specifies that the teal balls be replaced with blue rectangles with a black border (and also that the lines be replaced by orange lines):

And the visual result of this is:

Notice that I used data binding to set the NodeControlTemplate property. Because of using data binding (which I highly recommend), the property must be defined as a DependencyProperty instead of a CLR property.
Lessons learned:
If you are compositing a controls visual representation with sub controls that you want to allow users to override the visual representation of those controls:
- Declare those controls as ControlTemplates
- Declare a DependecyProperty of type ControlTemplate to allow the user to override the default ControlTemplate
- Use the ControlTemplate.LoadContent method to create the control and add it as a Child of your canvas.
ca810fba-78f9-43ba-aae2-eb82b1fdce45|0|.0