Search
Tutorial 3: Custom composite node

In this tutorial we will create a custom CompositeNode class which will be able to display information about individuals in an organization - such as name, description and image. The node will be customizable through various exposed properties.

1. Declaring the custom class

To create a custom composite node simply derive from the CompositeNode class.

C#  Copy Code

public class OrgChartNode : CompositeNode
{
}

Visual Basic  Copy Code

Public Class OrgChartNode
    Inherits CompositeNode

End Class

Creating a custom node is applicable in various scenarios. For example, if you want to define the contents of the node in XML and want to declare event handlers for various components. Or if you want to apply custom component arrangement logic or custom rendering.

2. Specifying the component content

Add a single constructor to the newly created node class with the following contents:

C#  Copy Code

...
public OrgChartNode(Diagram diagram) : base(diagram)
{
    string content = @"
      <SimplePanel>

        <Shape Name=""Shape"" Shape=""Rectangle"" />

          <Border Padding=""2"">
            <GridPanel>
              <GridPanel.Columns>
              <GridColumn Width=""30"" />
              <GridColumn />
            </GridPanel.Columns>
            <GridPanel.Rows>
              <GridRow />
            </GridPanel.Rows>

            <Image Name=""Image"" ImageAlign=""Fit"" />

            <StackPanel Orientation=""Vertical"" GridColumn=""1"">
              <Text Name=""Title"" Font=""Arial, 12pt, style=Bold"" TextAlignment=""Near"" />
              <Text Name=""FullName"" TextColor=""Blue"" TextAlignment=""Near"" />
              <Text Name=""Text"" Font=""Arial, 8pt"" TextAlignment=""Near"" />
            </StackPanel>
          </GridPanel>
        </Border>

      </SimplePanel>";

    Components.Add(XmlLoader.Load(content, null, null));
}
...

Visual Basic  Copy Code

...
Public Sub New(ByVal diagram As Diagram)

    MyBase.new(diagram)

    Dim content As String = _
        "<SimplePanel>" & _
        "" & _
        "  <Shape Name=""Shape"" Shape=""Rectangle"" />" & _
        "" & _
        "  <Border Padding=""2"">" & _
        "    <GridPanel>" & _
        "      <GridPanel.Columns>" & _
        "        <GridColumn Width=""30"" />" & _
        "        <GridColumn />" & _
        "      </GridPanel.Columns>" & _
        "      <GridPanel.Rows>" & _
        "        <GridRow />" & _
        "      </GridPanel.Rows>" & _
        "" & _
        "      <Image Name=""Image"" ImageAlign=""Fit"" />" & _
        "" & _
        "      <StackPanel Orientation=""Vertical"" GridColumn=""1"">" & _
        "        <Text Name=""Title"" Font=""Arial, 12pt, style=Bold"" TextAlignment=""Near"" />" & _
        "        <Text Name=""FullName"" TextColor=""Blue"" TextAlignment=""Near"" />" & _
        "        <Text Name=""Text"" Font=""Arial, 8pt"" TextAlignment=""Near"" />" & _
        "      </StackPanel>" & _
        "    </GridPanel>" & _
        "  </Border>" & _
        "" & _
        "</SimplePanel>"

    Components.Add(XmlLoader.Load(content, Nothing, Nothing))

End Sub
...

3. Declaring component fields

Now that the content of the node is defined and loaded, we will add several fields to our class referencing different components of interest. Add the following fields at the end of the class:

C#  Copy Code

private ShapeComponent shape;
private ImageComponent image;
private TextComponent title;
private TextComponent fullName;
private TextComponent text;

Visual Basic  Copy Code

Private _shape As ShapeComponent
Private _image As ImageComponent
Private _title As TextComponent
Private _fullName As TextComponent
Private _text As TextComponent

To initialize the fields to reference the respective components, you can use the FindComponent method of the CompositeNode class. This method will operate successfully after the components are added to the node's Components collection. Add the following code in the constructor, after the components are connected to the composite node:

C#  Copy Code

...
Components.Add(XmlLoader.Load(content, null, null));

shape = FindComponent("Shape") as ShapeComponent;
image = FindComponent("Image") as ImageComponent;
title = FindComponent("Title") as TextComponent;
fullName = FindComponent("FullName") as TextComponent;
text = FindComponent("Text") as TextComponent;
...

Visual Basic  Copy Code

...
Components.Add(XmlLoader.Load(content, Nothing, Nothing))

_shape = CType(FindComponent("Shape"), ShapeComponent)
_image = CType(FindComponent("Image"), ImageComponent)
_title = CType(FindComponent("Title"), TextComponent)
_fullName = CType(FindComponent("FullName"), TextComponent)
_text = CType(FindComponent("Text"), TextComponent)
...

4. Defining node properties

To be able to customize the node, we need to expose several properties. In this case the properties will act merely as wrappers of properties of the underlying components. For example, the Stroke property will wrap the Pen property of the underlying ShapeComponent used as a background. This way we can allow the user to customize the node without exposing its internal component structure directly.

Add the following properties after the constructor.

C#  Copy Code

public MindFusion.Drawing.Pen Stroke
{
    get { return shape.Pen; }
    set { shape.Pen = value; }
}

public MindFusion.Drawing.Brush Fill
{
    get { return shape.Brush; }
    set { shape.Brush = value; }
}

public Image Image
{
    get { return image.Image; }
    set { image.Image = value; }
}

public string Title
{
    get { return title.Text; }
    set { title.Text = value; }
}

public string FullName
{
    get { return fullName.Text; }
    set { fullName.Text = value; }
}

public string Text
{
    get { return text.Text; }
    set { text.Text = value; }
}

Visual Basic  Copy Code

Public Property Stroke() As MindFusion.Drawing.Pen

    Get
        Return _shape.Pen
    End Get
    Set(ByVal value As MindFusion.Drawing.Pen)
        _shape.Pen = value
    End Set

End Property

Public Property Fill() As MindFusion.Drawing.Brush

    Get
        Return _shape.Brush
    End Get
    Set(ByVal value As MindFusion.Drawing.Brush)
        _shape.Brush = value
    End Set

End Property

Public Property Image() As Image

    Get
        Return _image.Image
    End Get
    Set(ByVal value As Image)
        _image.Image = value
    End Set

End Property

Public Property Title() As String

    Get
        Return _title.Text
    End Get
    Set(ByVal value As String)
        _title.Text = value
    End Set

End Property

Public Property FullName() As String

    Get
        Return _fullName.Text
    End Get
    Set(ByVal value As String)
        _fullName.Text = value
    End Set

End Property

Public Property Text() As String

    Get
        Return _text.Text
    End Get
    Set(ByVal value As String)
        _text.Text = value
    End Set

End Property

5. Specifying default values

The default values can be provided in the constructor, after the component content have been loaded and the reference to the components of interest have been obtained. The following code sets White background and Black border as default values of new nodes.

C#  Copy Code

Stroke = new MindFusion.Drawing.Pen(Color.Black, 0);
Fill = new MindFusion.Drawing.SolidBrush(Color.White);

Visual Basic  Copy Code

Stroke = New MindFusion.Drawing.Pen(Color.Black, 0)
Fill = New MindFusion.Drawing.SolidBrush(Color.White)

This concludes the implementation of our custom node class.

6. Using the custom node class

Assuming that we have a ready WinForms application containing a Diagram variable named diagram1, and that two images have been added as resources to this application - named respectively Image1 and Image2, the following code creates two new instances of the OrgChartNode class and connects them with a link:

C#  Copy Code

OrgChartNode node1 = new OrgChartNode(diagram1);
node1.Bounds = new RectangleF(20, 10, 80, 40);
node1.Title = "CEO";
node1.FullName = "John Smith";
node1.Text = "Our beloved leader. \r\n" +
    "The CEO of this great corporation.";
node1.Image = Properties.Resources.Image1;
diagram1.Nodes.Add(node1);

OrgChartNode node2 = new OrgChartNode(diagram1);
node2.Bounds = new RectangleF(60, 60, 80, 40);
node2.Title = "CIO";
node2.FullName = "Bob Smith";
node2.Text = "The CIO of this great corporation.";
node2.Image = Properties.Resources.Image2;
diagram1.Nodes.Add(node2);

diagram1.Factory.CreateDiagramLink(node1, node2);

Visual Basic  Copy Code

Dim node1 As New OrgChartNode(diagram1)
node1.Bounds = New RectangleF(20, 10, 80, 40)
node1.Title = "CEO"
node1.FullName = "John Smith"
node1.Text = "Our beloved leader. \r\n" & _
    "The CEO of this great corporation."
node1.Image = My.Resources.Image1
diagram1.Nodes.Add(node1)

Dim node2 = New OrgChartNode(diagram1)
node2.Bounds = New RectangleF(60, 60, 80, 40)
node2.Title = "CIO"
node2.FullName = "Bob Smith"
node2.Text = "The CIO of this great corporation."
node2.Image = My.Resources.Image2
diagram1.Nodes.Add(node2)

diagram1.Factory.CreateDiagramLink(node1, node2)

Running the application will give results similar to the image below: