This guide will show you some basic ways of programmatically interacting with GoDiagram nodes and links and model data. Throughout this page, we will use the following diagram setup as our starting point:
private void Setup() {
myDiagram.UndoManager.IsEnabled = true;
myDiagram.NodeTemplate =
new Node("Auto")
.Add(
new Shape("Rectangle")
// don't draw any outline
{ Stroke = null }
// the Shape.Fill comes from the Node.Data.Color property
.Bind("Fill", "Color"),
new TextBlock
// leave some space around larger-than-normal text
{ Margin = 6, Font = new Font("Segoe UI", 18) }
// the TextBlock.Text comes from the Node.Data.Text property
.Bind("Text")
);
myDiagram.Model = new MyModel {
NodeDataSource = new List<NodeData> {
new NodeData { Key = 1, Text = "Alpha", Color = "lightblue" },
new NodeData { Key = 2, Text = "Beta", Color = "orange" },
new NodeData { Key = 3, Text = "Gamma", Color = "lightgreen" },
new NodeData { Key = 4, Text = "Delta", Color = "pink" }
}
};
}
...
public class MyModel : Model<NodeData, int, object> { }
public class NodeData : MyModel.NodeData {
public string Color { get; set; }
}
The code produces this Diagram:
You can use Diagram.FindNodeForKey(key)
to get a reference to a
Node in C#. Key values in GoDiagram are specified on the model.
You can then use the Node reference to inspect and manipulate the Node.
var node = myDiagram.FindNodeForKey(1);
// Selects the node programmatically, rather than clicking interactively:
myDiagram.Select(node);
// Outputs an object in the console window.
// This is useful if your data has good ToString()s defined
Console.WriteLine(node.Data);
However FindNodeForKey
may return null
if no node
data uses that key value. Also, it only looks at the model data to find a
node data that uses the given key value, from which it finds the
corresponding Node in the Diagram. It does not look at the text values of
any TextBlocks that are within the Nodes, so it can work even if no text is
shown at all. And it does not look at any programmatically added Nodes that
were not created from model data.
Once you have a Node
, you can get its key either via the
Node.Key
property or by looking at its data:
someNode.Data.Key
, just as you can look at any of the data
properties.
Diagrams have several properties and methods that return collections of Parts.
Both Nodes and Links are kinds of Parts.
Diagram.Nodes
and Diagram.Links
return read only collections
of all Nodes and Links in the Diagram, respectively.
Diagram.Selection
returns a read only collection of selected Parts (both
selected Nodes and selected Links).
There are also more specific methods for common operations, such as
Diagram.FindTreeRoots()
which returns an enumerator of all top-level Nodes that have no parent nodes.
This next example uses Diagram.Nodes
and shows how to iterate
over the collection.
// Calling Diagram.Commit executes the given function between StartTransaction and CommitTransaction
// calls. That automatically updates the display and allows the effects to be undone.
myDiagram.Commit((d) => { // d == myDiagram
// iterate over all nodes in Diagram
foreach (var node in d.Nodes) {
if ((node.Data as NodeData).Text == "Beta") continue; // skip Beta, just to contrast
node.Scale = 0.4; // shrink each node
}
}, "decrease scale");
As a result we have very scaled-down nodes, except for Beta:
Often we want to manipulate a property that belongs to one of the Node's elements, perhaps an element arbitrarily deep in the template. In our example Diagram, each Node has one Shape, and if we want to change the color of this Shape directly we would need a reference to it. To make it possible to find, we can give that Shape a name:
myDiagram.NodeTemplate =
new Node("Auto")
.Add(
new Shape("Rectangle")
{ Stroke = null, Name = "SHAPE" } // added the name property
.Bind("Fill", "Color"),
new TextBlock
{ Margin = 6, Font = new Font("Segoe UI", 18) }
.Bind("Text")
);
Names allow us to easily find GraphObjects inside of Panels (all Nodes are
also Panels) using Panel.FindElement
, which will search the
visual tree of a Panel starting at that panel. So when we have a reference
to a Node, we can call someNode.FindElement("SomeName")
to
search through the node for a GraphObject with that name.
It will return a reference to the named GraphObject if it is found,
or null
otherwise.
Using this, we could make a button that changes the fill of the Shape inside of a selected Node:
private void buttonClick(object sender, RoutedEventArgs e) {
myDiagram.Commit((d) => {
foreach (var node in d.Selection) {
// If there was a GraphObject in the node named SHAPE, then set its fill to red:
if (node.FindElement("SHAPE") is Shape shape) {
shape.Fill = "red";
}
}
}, "change color");
}
Looking again at our Node template, we have the Shape.Fill
property data-bound to the "Color" property of our Node data:
myDiagram.NodeTemplate =
new Node("Auto")
.Add(
new Shape("Rectangle")
{ Stroke = null, Name = "SHAPE" }
.Bind("Fill", "Color"), // note this data binding
new TextBlock
{ Margin = 6, Font = new Font("Segoe UI", 18) }
.Bind("Text")
);
Changing the Shape's Fill
property inside our node will not, as
the Node template currently stands, update the model data.
var node = myDiagram.FindNodeForKey(1);
var shape = node.FindElement("SHAPE") as Shape;
shape.Fill = "red";
// outputs "lightblue" - the model has not changed!
Console.WriteLine((node.Data as NodeData).Color);
This is undesirable in some cases. When we want the change to persist after saving and loading, we will want the model data updated too.
However, in some situations this lack of persistence might be a good thing. For instance if we want the color change for only cosmetic purposes, such as changing the color of a button when hovering over it with the mouse, we would not want to modify the model data that might be saved.
For now, suppose that we do want to update the model. The preferred way to do this is to modify the data in the model and depend on the data binding to automatically update the Shape. However, we cannot modify the data directly by just setting the property.
var node = myDiagram.FindNodeForKey(1);
// DO NOT DO THIS!
// This would update the data, but GoDiagram would not be notified
// that this object has been modified,
// and the associated Node will not be updated appropriately
(node.Data as NodeData).Color = "red";
Instead we should set the data property using the method
Model.Set(data, propertyName, propertyValue)
.
As always, changes should always be performed within a transaction.
There should only be a single transaction for all changes that
need to be made at the same time, from the user's point of view.
A convenient way to perform a transaction is to call Model.Commit
.
var node = myDiagram.FindNodeForKey(1);
// Model.commit executes the given function within a transaction
myDiagram.Model.Commit((m) => { // m == the Model
// This is the safe way to change model data.
// GoDiagram will be notified that the data has changed
// so that it can update the node in the Diagram
// and record the change in the UndoManager.
m.Set(node.Data, "Color", "red");
}, "changed color");
// outputs "red" - the model has changed!
Console.WriteLine((node.Data as NodeData).Color);
// and the user will see the red node
Note that there is no longer any need to name the Shape "SHAPE", because
there is no longer any need to call FindElement
to look for the
particular Shape. Data binding will automatically update properties, so we
do not have to do that ourselves.
myDiagram.NodeTemplate =
new Node("Auto")
.Add(
new Shape("Rectangle")
{ Stroke = null } // removed the Name property
.Bind("Fill", "Color"),
new TextBlock
{ Margin = 6, Font = new Font("Segoe UI", 18) }
.Bind("Text")
);
System.Diagnostics.Trace
— A debugging aid that allows output of messages. See more suggestions for
debugging GoDiagram diagrams at the intro page on Debugging.
You may want to read more tutorials, such as the Learn GoDiagram tutorial. and the Interactivity tutorial.
If you are ready for a comprehensive overview of GoDiagram, have a look at the technical introduction. If you want to explore by example, have a look at the samples to get a feel for what's possible with GoDiagram.