This sample allows the user to create models of circuits using gates and wires. It also automatically simulates its operation by displaying values: green for true and red for false.  It continually updates itself with a DispatcherTimer that calls an UpdateValues method so that the values throughout the circuit remain correct when changing the wiring or when changing the Input values by double-clicking on Input nodes.

To support the creation and editing of circuit diagrams, we create a GraphLinksModel in code, set its Modifiable property to true, set the NodesSource and LinksSource properties to empty collections, and set HasUndoManager to true.

  var diagramModel = new GraphLinksModel<MyGateData, String, String, MyWireData>();
  diagramModel.Modifiable = true;
  diagramModel.NodeKeyPath = "Key";
  diagramModel.NodeCategoryPath = "GateType";
  diagramModel.NodesSource = new ObservableCollection<MyGateData>();
  diagramModel.LinksSource = new ObservableCollection<MyWireData>();
  diagramModel.HasUndoManager = true;
  myDiagram.Model = diagramModel;

To support multiple DataTemplates for Nodes, we add a GateType property to our MyGateData class to choose the data template to use for rendering that node data.  The model needs to know how to choose the template, so we also have to set the NodeCategoryPath to the name of that property.

public class MyGateData : GraphLinksModelNodeData<String> {
  // the type of gate that this node represents
  public String GateType { get; set; }

  // the shape of the node figure is bound to this property
  public NodeFigure Figure { get; set; }

  // the current value of this gate;
  // the node color is bound to its Data's Value property and must update with it
  public Boolean Value {
    get { return _Value; }
    set {
      Boolean old = _Value;
      if (old != value) {
        _Value = value;
        RaisePropertyChanged("Value", old, value);
      }
    }
  }
  private Boolean _Value;
}

Although there are potentially many different kinds of gates, we can simplify their implementation by using only four categories:

The corresponding four DataTemplates are inserted into a DataTemplateDictionary (for WPF in XAML, for Silverlight in code).

The current value of each Input or Output gate or wire is shown by its color.  This is achieved by data-binding its brush to the Value property of the data:

   <go:BooleanBrushConverter x:Key="theValueConverter" FalseBrush="Red" TrueBrush="Green" />

  ...
     <go:NodeShape Fill="{Binding Path=Data.Value, Converter={StaticResource theValueConverter}}" ...>

Note how ports are implemented using Rectangles.  For example, consider the typical output port:

 <Rectangle go:SpotPanel.Spot="MiddleRight" Width="8" Height="8" Fill="Black"
           go:Node.PortId="OneOutOf" go:Node.LinkableSelfNode="True"
           go:Node.LinkableFrom="True" go:Node.FromSpot="MiddleRight" Cursor="Hand" />

The go:SpotPanel.Spot attached property tells the SpotPanel where to position the Rectangle.  The go:Node.PortId attached property both declares that this Rectangle acts as a port and gives it a name if you want to find it again by calling Node.FindPort.  The two go:Node.Linkable... attached properties restrict the interactive linking abilities of the user.  The goNode.FromSpot attached property declares that links coming out of this port connect at a particular spot on the Rectangle's bounds.  Of course you could use other elements instead of Rectangles if you want them to appear differently.

To support drag-and-drop from the palette of gates shown on the left, we also have to set Diagram.AllowDrop to true.

  <go:Diagram x:Name=myDiagram AllowDrop="True" ...>

The Palette control extends the Diagram control, so it inherits much the same behavior. However, by default it has a GridLayout, which results in the Nodes in a Palette arranged in a grid, filling each row from left to right in the available space.

To create a Palette, simply declare it in XAML as you would a Diagram, so that it's placed wherever in the window you want it. Since the default behavior of a Palette allows one to drag nodes out of it, we needn't set any attributes to enable our desired behavior. The implementation is as shown:

    <go:Palette x:Key="myPalette" ...>

In code, we create a model for our Palette exactly as we would for a Diagram.  Since we want our palette to include Nodes, but not Links, we set our model's LinksSource property to an empty collection:

    var paletteModel = new GraphLinksModel<MyGateData, String, String, MyWireData>();
    paletteModel.NodeKeyPath = "Key";
    paletteModel.NodeCategoryPath = "GateType";
    paletteModel.NodesSource = new List<MyGateData>() {
      new MyGateData() { Figure = NodeFigure.Circle, GateType = "Input", Key = "Input"},
      new MyGateData() { Figure = NodeFigure.Rectangle, GateType = "Output", Key = "Output", Value=true},
      new MyGateData() { Figure = NodeFigure.AndGate, GateType = "TwoInOneOut", Key = "And"},
      new MyGateData() { Figure = NodeFigure.OrGate, GateType = "TwoInOneOut", Key = "Or"},
      new MyGateData() { Figure = NodeFigure.XorGate, GateType = "TwoInOneOut", Key = "Xor"},
      new MyGateData() { Figure = NodeFigure.Inverter, GateType = "OneInOneOut", Key = "Not"},
      new MyGateData() { Figure = NodeFigure.NandGate, GateType = "TwoInOneOut", Key = "Nand"},
      new MyGateData() { Figure = NodeFigure.NorGate, GateType = "TwoInOneOut", Key = "Nor"},
      new MyGateData() { Figure = NodeFigure.XnorGate, GateType = "TwoInOneOut", Key = "Xnor"},
    };
    paletteModel.LinksSource = new List<MyWireData>();
    myPalette.Model = paletteModel;

Another feature this sample makes use of is the UndoManager. The undo manager enables the user to use the standard undo and redo commands (Ctrl-Z and Ctrl-Y) to revert to an earlier or later state of your diagram.  The DiagramModel supports all of the standard operations, such as adding a new node either by drag-and-drop or by copying, drawing a new link or reconnecting an existing link, or deleting a node or a link.

However, models do not know about the position of nodes.  If you want to support undo/redo for the interactive movement (dragging) of nodes, you need to add the following binding to your Node's DataTemplate's root visual element:

      go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}"

This assumes that you have used a Node data type with a Location property, which all of the predefined node data classes do have.