This example shows how databinding and styles can be used for displaying a set of connected nodes. To better illustrate this, I've added some basic interactivity: the nodes can be dragged using the mouse and databinding will update automatically the positions of the lines.

For simplicity, I will use a set of predefined nodes and a set of predefined lines connecting those nodes (but, the functionality could be easily exteded to create them dynamically).

I've used mainly Microsoft Expression Interactive Designer for creating this sample, but because of some limitations in the current CTP, I had to tweak a few times the code manually. The project can be opened and edited by Microsoft Expression Interactive Designer without any problems.

This time I won't create a step by step tutorial; I will explain shortly each part of the code and how everything works. So feel free to download the project, open it and Visual Studio or Microsoft Expression Interactive Designer, run it and look through the code.

Canvas as the Document Root

We will use a Canvas as the DocumentRoot. Canvas is a simple layout container which allows positioning children by specifying X-Y coordinates.

By default, Microsoft Expression Interactive Designer creates the DocumentRoot a Grid. In order to create a new scene with a Canvas, you need to uncheck the option "New Scene Creates a Grid" from the "Tools" menu. The scenes created after that will have a Canvas as the DocumentRoot

Defining a Style for a node

Styles represent a great way to reuse properties and events. This is what our style does:

  • Sets the Stroke to black
  • Sets the Fill to red
  • Sets the Width and Height to 50
  • Sets a RenderTransform which translates to left and up with 25 px. This will cause the ellipse to be centered around its top-left position.
  • Defines event handlers for MouseUp and MouseDown
	<Canvas.Resources>
		<Style x:Key="NodeStyle" TargetType="{x:Type Ellipse}">
			<Setter Property="Stroke" Value="#FF000000" />
			<Setter Property="Fill" Value="sc#1, 1, 0, 0"/>
			<Setter Property="Width" Value="50" />
			<Setter Property="Height" Value="50"/>
			<Setter Property="RenderTransform">
				<Setter.Value>
					<TransformGroup>
						<TranslateTransform X="0" Y="0"/>
						<ScaleTransform ScaleX="1" ScaleY="1"/>
						<SkewTransform AngleX="0" AngleY="0"/>
						<RotateTransform Angle="0"/>
						<TranslateTransform X="0" Y="0"/>
						<TranslateTransform X="-25" Y="-25"/>
					</TransformGroup>
				</Setter.Value>
			</Setter>
			
			<EventSetter Event="MouseDown" Handler="OnMouseDown" />
			<EventSetter Event="MouseUp" Handler="OnMouseUp" />
		</Style>
	</Canvas.Resources>

Adding a few nodes

Now, when we create a new node, we just need to specify its style and its position, because all the other properties and the event handlers are defined inside the style.

	<Ellipse x:Name="Ellipse" Style="{DynamicResource NodeStyle}" Canvas.Left="448" Canvas.Top="133" />

You can also add new nodes using Microsoft Expression Interactive Designer:

  • Draw a new ellipse
  • Right click on it Edit Style - Apply Resource - NodeStyle; this will apply the existing style to the ellipse
  • Microsoft Expression Interactive Designer creates by default some attributes; the problem is that, in this case they override the values defined by the style; so we need to clear those attributes (in the Properties palette, click on the property and select "Clear/Default"):
    • Stroke
    • Fill
    • Width
    • Height

Defining the lines

We will define a set of predefined lines, and databind the coordinates of the two points to the coordinates of the nodes (the attached properties Canvas.Left and Canvas.Top). Unfortunately, the only way to do this step manually, because Microsoft Expression Interactive Designer has currently some limitations (we know about this issues and we hope that we will improve them in the next releases):

  • it doesn't allow to create a Line object.
  • it doesn't support databinding to an attached property.

A Line object has two pairs of values representing the coordinates for the two points: X1, Y1 and X2, Y2. Here is how we can bind one of the ends to the coordinates of one of the ellipses:

	<Line Stroke="#FF000000"
		X1="{Binding ElementName=Ellipse, Path=(Canvas.Left), Mode=Default}"
		Y1="{Binding ElementName=Ellipse, Path=(Canvas.Top), Mode=Default}" 
		X2="50" Y2="150" />

The logic for moving the nodes

First of all we will add an event handler for the MouseMove event of the DocumentRoot element. We will name it OnMouseMove. This step can be done using Microsoft Expression Interactive Designer by selecting the DocumentRoot and using the Events palette to add the event.

We also need to add the events handler OnMouseUp and OnMouseDown defined in the style. Unfortunately this step needs to be done manually at this point.

The logic is simple: when we get a MouseDown notification, we store in a private field the sender (the node) of that notification; in the MouseMove handler we will update the position of that node. This is the final code:

	public partial class Scene1
	{
		
		public Scene1()
		{
			// This assumes that you are navigating to this scene.
			// If you will normally instantiate it via code and display it
			// manually, you either have to call InitializeComponent by hand or
			// uncomment the following line.
			// this.InitializeComponent();

			// Insert code required on object creation below this point.
		}

		private FrameworkElement movingObject = null;

		private void OnMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
		{
			if (this.movingObject != null)
			{
				Point mousePosition = Mouse.GetPosition(this.DocumentRoot);

				this.movingObject.SetValue(Canvas.LeftProperty, mousePosition.X);
				this.movingObject.SetValue(Canvas.TopProperty, mousePosition.Y);
			}
		}


		private void OnMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			this.movingObject = sender as FrameworkElement;
		}


		private void OnMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			this.movingObject = null;
		}
	}

This implementation could be improved (for example, if you click on the edge of an ellipse and move the mouse just a little bit, you will notice that the ellipse will "jump" so that it will be centered on the mouse pointer.

Running the application

Once you've got all the code in place, you just need to build and run the application. You can click and move different nodes and the lines will just update automatically.

Notes:

  1. The attached project requires the Feb CTP of WinFX Runtime Components Feb CTP. The corresponding version of Microsoft Expression Interactive Designer is the March CTP (which should be released soon)
  2. This sample shows a very simplistic implementation for the drag-drop behavior. A better approach is presented by Unni in his article - "Panel Like" system.