GoXam - Data Binding & Templates

GoXam is the first diagram control to be designed from the ground up to be a natural extension of the Microsoft XAML language used in Windows Presentation Foundation (WPF) and Silverlight. Essential to this environment are Data Binding and Data Templates. GoXam integrates these concepts cleanly and coherently into a powerful diagramming extension to XAML.

Diagram Models and Data Binding

One of the principal features of XAML-defined presentation is the use of data binding. A diagram control, however, must support more complex features than the typical control.

There are at least two kinds of relationships that a diagram can support between data items:

  • Relationships forming a graph of nodes and links (or in similar terminology: nodes and arcs, or entities and relationships, or vertices and edges)
  • Grouping relationships, where a group contains members; perhaps for part/sub-part containment, or for the nesting of subgraphs

GoXam makes use of a model to discover, maintain, navigate, and modify these relationships based on the data to which the diagram is bound. Not all data behind graphs has the same complexity, so we provide three primary model classes to give you the right blend of ease of use, performance and power.

The TreeModel is the simplest model. It is suitable for applications where the data forms a graph that is a tree structure.

The GraphModel is used when each node has a list of nodes connecting to or from that node. The GraphModel also supports simple grouping.

The third model is the GraphLinksModel, where your data includes a source for nodes and also a source for the links that connect them. GraphLinksModel also supports link information that allows different link connection points on each node. It also supports labels on links.

Once a model is created, and the model's data is initialized and assigned to a Diagram, you have created an automatic link between the model and the diagram. Changes to the model update the diagram, and changes to the diagram (typically by the user) update the model.

If you look at our online Silverlight demos, you'll be amazed at how little code you need to write to visualize and update your data.

Data Templates for Nodes

The appearance of any node or link is determined not only by the data to which it is bound but also the DataTemplate used to define the elements of its visual tree. A data template is a reusable piece of XAML that defines how to display your bound data. So the appearance of your diagrams is separate from the code. Simply editing the XAML that defines a node or link can change a diagram's appearance.

Since nodes and links are defined by XAML, it is easy to incorporate all of the power of WPF and SIlverlight graphics (rectangles, text, paths, gradients, images, even video) into your diagrams, including the use of animation, storyboards and effects like Blur and Drop Shadow.

Nodes can be simple using this NodeTemplate

simple GoXam diagram


    <DataTemplate>
      <TextBlock Text="{Binding Path=Data}" />
    </DataTemplate>
    

But it is also possible to define nodes that are as complex as your needs require (click image to see XAML)

Click to Show DataTemplate XAML code
Click to Show DataTemplate XAML code
Click to Show DataTemplate XAML code
DataTemplate for Purple node Alpha:

      <DataTemplate x:Key="NodeTemplate4">
        <!-- a NodePanel shows a background shape and places the other panel children inside the shape -->
        <go:NodePanel go:Node.SelectionAdorned="True"
                      go:Node.ToSpot="LeftSide" go:Node.FromSpot="RightSide" >
          <!-- in Silverlight, use a Path instead of a go:NodeShape -->
          <go:NodeShape go:NodePanel.Figure="Database" Stroke="Black" StrokeThickness="1">
            <Shape.Fill>
              <!-- use a fancier brush than a simple solid color -->
              <LinearGradientBrush StartPoint="0.0 0.0" EndPoint="1.0 0.0">
                <LinearGradientBrush.GradientStops>
                  <GradientStop Color="{Binding Path=Data.Color,
                          Converter={StaticResource theStringColorConverter}}" Offset="0.0" />
                  <GradientStop Color="White" Offset="0.5" />
                  <GradientStop Color="{Binding Path=Data.Color,
                          Converter={StaticResource theStringColorConverter}}" Offset="1.0" />
                </LinearGradientBrush.GradientStops>
              </LinearGradientBrush>
            </Shape.Fill>
          </go:NodeShape>
          <!-- this TextBlock element is arranged inside the NodePanel’s shape -->
          <TextBlock Text="{Binding Path=Data.Key}" TextAlignment="Center"
                     HorizontalAlignment="Center" VerticalAlignment="Center" />
        </go:NodePanel>
      </DataTemplate>
      
DataTemplate for light blue OrgChart node:


    <DataTemplate x:Key="NodeTemplate" >
      <go:NodePanel go:Node.SelectionAdorned="True" >
        <Border Background="Azure" BorderBrush="Black" BorderThickness="1" MaxWidth="200" >
          <Grid MaxWidth="200">
            <Grid.RowDefinitions>
              <RowDefinition Height="Auto" />
              <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid Grid.Row="0" >
              <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
              </Grid.ColumnDefinitions>
              <TextBlock Grid.Column="0" Text="{Binding Path=Data.Name}" FontWeight="Bold"
                         TextAlignment="Left" TextWrapping="Wrap" Margin="4 4 4 2" />
              <Border Grid.Column="1" BorderBrush="Black" BorderThickness="2"
                      Height="34" Margin="2"
                      Width="{Binding Path=Data.Flag,
                              Converter={StaticResource theImageSizeConverter}}">
                <Image Stretch="Fill" Source="{Binding Path=Data.Flag}" />
              </Border>
            </Grid>
            <TextBlock Grid.Row="1" Text="{Binding Path=Data.PersonData}"
                       TextAlignment="Left" TextWrapping="Wrap" Margin="4 4 4 2" />
          </Grid>
        </Border>
      </go:NodePanel>
    </DataTemplate>
  
DataTemplate for Entity Relationship node:

  <DataTemplate x:Key="NodeTemplate">
    <Border Background="Gray" BorderBrush="Gray" BorderThickness="2" CornerRadius="3"
            go:Part.SelectionAdorned="True" go:Part.Resizable="True"
            go:Node.FromSpot="AllSides" go:Node.ToSpot="AllSides"
            go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}">
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto" />
          <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
          </Grid.ColumnDefinitions>
          <TextBlock Grid.Column="0" HorizontalAlignment="Center"
                     Text="{Binding Path=Data.Key}" FontWeight="Bold" />
          <Button Grid.Column="1" Content="*" Click="Button_Click" />
        </Grid>
        <ListView Grid.Row="1" Background="White" HorizontalAlignment="Stretch"
                  ItemsSource="{Binding Path=Data.Items}">
          <ListView.View>
            <GridView>
              <GridView.Columns>
                <GridViewColumn>
                  <GridViewColumn.CellTemplate>
                    <DataTemplate>
                      <go:NodeShape
                          go:NodePanel.Figure="{Binding Path=Figure}"
                          Width="10" Height="10"
                          Fill="{Binding Path=Color,
                                 Converter={StaticResource theStringBrushConverter}}"
                          Stroke="Black" StrokeThickness="1" />
                    </DataTemplate>
                  </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="Name"
                                DisplayMemberBinding="{Binding Path=Name}" />
              </GridView.Columns>
            </GridView>
          </ListView.View>
        </ListView>
      </Grid>
    </Border>
  </DataTemplate>
  

Data Templates for Links

GoXam also supports DataTemplate for links, along with links features like Orthogonal, Bezier, rounded corners, jump overs and avoids-nodes routing. (click image to see XAML)

Click to Show DataTemplate XAML code

Normal Routing

Click to Show DataTemplate XAML code

Orthogonal

Click to Show DataTemplate XAML code

Bezier

Click to Show DataTemplate XAML code

with curviness

Click to Show DataTemplate XAML code

Round Corners

DataTemplate for simple links with an arrowhead

    <DataTemplate x:Key="LinkTemplate2">
      <go:LinkPanel go:Part.SelectionElementName="Path"
                    go:Part.SelectionAdorned="True" >
        <!-- in Silverlight substitute Path for go:LinkShape -->
        <go:LinkShape x:Name="Path" go:LinkPanel.IsLinkShape="True"
                      Stroke="Black" StrokeThickness="1" />
        <!-- the arrowhead -->
        <Polygon Fill="Black" Points="8 4  0 8  2 4  0 0"
                 go:LinkPanel.Alignment="1 0.5" go:LinkPanel.Index="-1"
                 go:LinkPanel.Orientation="Along" />
      </go:LinkPanel>
    </DataTemplate>
    
DataTemplate for orthogonal links with an arrowhead

  <DataTemplate x:Key="LinkTemplate4">
    <go:LinkPanel go:Part.SelectionElementName="Path"
                  go:Part.SelectionAdorned="True">
      <go:Link.Route>
        <go:Route Routing="Orthogonal" />
      </go:Link.Route>
      <!-- in Silverlight substitute Path for go:LinkShape -->
      <go:LinkShape x:Name="Path" go:LinkPanel.IsLinkShape="True"
                    Stroke="Black" StrokeThickness="1" />
      <Polygon Fill="Black" Points="8 4  0 8  2 4  0 0"
               go:LinkPanel.Alignment="1 0.5" go:LinkPanel.Index="-1"
               go:LinkPanel.Orientation="Along" />
    </go:LinkPanel>
  </DataTemplate>
  
Option to enable Bezier curves. You can control the amount of curvature by setting the Route.Curviness property. With varying numbers of links between the same pair of nodes it will automatically compute values for Curviness unless you assign it explicitly.

    <go:Link.Route>
      <go:Route Curve="Bezier" />
    </go:Link.Route>
    

Click to Show DataTemplate XAML code

with AvoidsNodes

Click to Show DataTemplate XAML code

Rounded with Jumpovers

Option to enable AvoidsNodes link routing

    <go:Link.Route>
      <go:Route Routing="AvoidsNodes" />
    </go:Link.Route>
    
Orthogonal Routing with rounded corners

    <go:Link.Route>
      <go:Route Routing="Orthogonal" Curve="JumpOver" Corner="10" />
    </go:Link.Route>
    

Links with Annotations

It is common to add annotations or decorations to links, particularly text. You can easily add any elements you want to a LinkPanel. (click image to see XAML)

Links with labels

Links support labels

that follow the path of the link

that follow the path of the link

Label on the From and To ends of link

      <DataTemplate x:Key="LinkTemplate5">
        <go:LinkPanel>
          <go:LinkShape Stroke="Black" StrokeThickness="1" />
          <Polygon Fill="Black" Points="8 4  0 8  2 4  0 0" go:LinkPanel.Index="-1"
                   go:LinkPanel.Alignment="1 0.5" go:LinkPanel.Orientation="Along" />
          <TextBlock Text="From" go:LinkPanel.Index="0"
                   go:LinkPanel.Offset="NaN NaN" go:LinkPanel.Orientation="Upright" />
          <TextBlock Text="To" go:LinkPanel.Index="-1"
                   go:LinkPanel.Offset="NaN NaN" go:LinkPanel.Orientation="Upright" />
        </go:LinkPanel>
      </DataTemplate>
      

even curvy paths

even curvy paths

link labels can be any element

link labels can be any element

link labels can be any element - here a red 8 pointed star and a XAML Button control are used

      <!-- LinkPanel labels in Silverlight -->
      <go:NodePanel go:LinkPanel.Index="0" go:LinkPanel.Offset="5 5" >
        <Path go:NodePanel.Figure="EightPointedStar" Fill="Red"
              Width="10" Height="10" />
      </go:NodePanel>
      <Button Content="?" Click="Button_Click" />

      <!-- LinkPanel labels in WPF -->
      <go:NodeShape go:LinkPanel.Index="0" go:LinkPanel.Offset="5 5"
                    go:NodePanel.Figure="EightPointedStar" Fill="Red"
                    Width="10" Height="10" />
      <Button Content="?" Click="Button_Click" />
      

Link Connection Points

By default, links will connect around the edge of a node. (click image to see XAML)

links connect to edge of any shape

links connect to edge of any shape

connect to spot

or to a specific spot

or to a one or more sides

Setting the Node.FromSpot and Node.ToSpot attached properties

      <DataTemplate x:Key="NodeTemplate2">
        <TextBlock Text="{Binding Path=Data.Key}" go:Node.SelectionAdorned="True"
                   go:Node.ToSpot="MiddleLeft" go:Node.FromSpot="MiddleRight" />
      </DataTemplate>
      
You can also specify that the links go into a node not at a single spot but spread out along one side. Note we've added a border to the node here as well.

      <DataTemplate x:Key="NodeTemplate3">
        <Border BorderBrush="Black" BorderThickness="1" Padding="3"
                go:Node.SelectionAdorned="True"
                go:Node.ToSpot="LeftSide" go:Node.FromSpot="RightSide" >
          <TextBlock Text="{Binding Path=Data.Key}" />
        </Border>
      </DataTemplate>
      

There are times when you want to have different logical and graphical places at which links should connect. GoXam allows you to create a link connection spot out of any element. The elements to which a link may connect are called ports. There may be any number of ports in a node. (click image to see XAML)

nodes with ports

Nodes with ports

nodes with ports

logic diagram with input/output ports

Gray node with 2 inputs ports and an output port

      <DataTemplate x:Key="NodeTemplate4">
        <Border BorderBrush="Black" BorderThickness="1"
                go:Node.SelectionAdorned="True">
          <Grid Background="LightGray">
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto" />
              <ColumnDefinition Width="*" />
              <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
              <RowDefinition Height="Auto" />
              <RowDefinition Height="*" />
              <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <TextBlock Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3"
                       Text="{Binding Path=Data.Key}" TextAlignment="Center"
                       FontWeight="Bold" TextWrapping="Wrap" Margin="4,4,4,2" />
            <StackPanel Grid.Column="0" Grid.Row="1" Orientation="Horizontal">
              <!-- this Rectangle is a port, identified with the string "A";
                   links only come into it at the middle of the left side -->
              <Rectangle Width="6" Height="6" Fill="Black"
                         go:Node.PortId="A" go:Node.ToSpot="MiddleLeft" />
              <TextBlock Text="A" />
            </StackPanel>
            <StackPanel Grid.Column="0" Grid.Row="2" Orientation="Horizontal">
              <!-- this Rectangle is another input port, named "B" -->
              <Rectangle Width="6" Height="6" Fill="Black"
                         go:Node.PortId="B" go:Node.ToSpot="MiddleLeft" />
              <TextBlock Text="B" />
            </StackPanel>
            <StackPanel Grid.Column="2" Grid.Row="1" Grid.RowSpan="2"
                        Orientation="Horizontal" VerticalAlignment="Center">
              <TextBlock Text="Out" />
              <!-- this Rectangle is another port, identified with the string "Out";
                   links only go out of it at the middle of the right side -->
              <Rectangle Width="6" Height="6" Fill="Black"
                         go:Node.PortId="Out" go:Node.FromSpot="MiddleRight" />
            </StackPanel>
          </Grid>
        </Border>
      </DataTemplate>