It is common to make a Node (or a part of a Node or a Link) stand out by "highlighting" it in some way. This happens with selection when a selection Adornment is shown. However one frequently wants to highlight Parts independently of selection. This can be done by changing the fill or stroke of a Shape, replacing a Picture source with another source, adding or removing a shadow, and so on.
The most general kind of highlighting is to change appearance when an action occurs, such as mousing over a node. This can draw attention to interactive Nodes or Links or really any GraphObject, such as buttons. This is why predefined buttons in GoDiagram highlight on mouse-over.
To achieve this effect you just need to define GraphObject.MouseEnter and GraphObject.MouseLeave event handlers.
void mouseEnter(InputEvent e, GraphObject obj, GraphObject _) {
var node = obj as Node;
var shape = node.FindElement("SHAPE") as Shape;
shape.Fill = "#6DAB80";
shape.Stroke = "#A6E6A1";
var text = node.FindElement("TEXT") as TextBlock;
text.Stroke = "white";
}
void mouseLeave(InputEvent e, GraphObject obj, GraphObject _) {
var node = obj as Node;
var shape = node.FindElement("SHAPE") as Shape;
// Return the Shape's fill and stroke to the defaults
shape.Fill = (node.Data as NodeData).Color;
shape.Stroke = null;
// Return the TextBlock's stroke to its default
var text = node.FindElement("TEXT") as TextBlock;
text.Stroke = "black";
}
diagram.NodeTemplate =
new Node("Auto") {
MouseEnter = mouseEnter,
MouseLeave = mouseLeave
}
.Add(
new Shape { Name = "SHAPE", StrokeWidth = 2, Stroke = null }
.Bind("Fill", "Color"),
new TextBlock { Name = "TEXT", Margin = 10, Font = new Font("Verdana", 18, FontWeight.Bold) }
.Bind("Text", "Key")
);
diagram.Model =
new MyModel {
NodeDataSource = new List<NodeData> {
new NodeData { Key = "Alpha", Color = "#96D6D9" },
new NodeData { Key = "Beta", Color = "#96D6D9" },
new NodeData { Key = "Gamma", Color = "#EFEBCA" },
new NodeData { Key = "Delta", Color = "#EFEBCA" }
},
LinkDataSource = new List<LinkData> {
new LinkData { From = "Alpha", To = "Beta" },
new LinkData { From = "Alpha", To = "Gamma" },
new LinkData { From = "Beta", To = "Beta" },
new LinkData { From = "Gamma", To = "Delta" },
new LinkData { From = "Delta", To = "Alpha" }
}
};
Nodes are highlighted when the mouse is over them.
It is also commonplace to perform highlighting of stationary Parts during a drag, which is a different case of "mouse over". This can be implemented in a manner similar to the MouseEnter/MouseLeave events by implementing GraphObject.MouseDragEnter and GraphObject.MouseDragLeave event handlers. Several samples demonstrate this: Org Chart Editor, Planogram, Regrouping, and Seating Chart.
It is common to want to show Nodes or Links that are related to a particular Node. Unlike the mouse-over scenarios, one may want to maintain the highlighting for many Parts independent of any mouse state or selection state.
Here is an example of highlighting all of the nodes and links that come out of a node that the user clicks. This example uses the Part.IsHighlighted property and data binding of visual properties to that Part.IsHighlighted property.
diagram.NodeTemplate =
new Node("Auto") {
// when the user clicks on a Node, highlight all Links coming out of the node
// and all of the Nodes at the other ends of those Links.
Click = (e, obj) => {
var node = obj as Node;
// highlight all Links and Nodes coming out of a given Node
node.Diagram.Commit(d => {
// remove any previous highlighting
d.ClearHighlighteds();
// for each Link coming out of the Node, set Link.IsHighlighted
foreach (var l in node.FindLinksOutOf()) {
l.IsHighlighted = true;
}
// for each Node destination for the Node, set Node.IsHighlighted
foreach (var n in node.FindNodesOutOf()) {
n.IsHighlighted = true;
}
}, "highlight");
}
}
.Add(
new Shape { StrokeWidth = 2, Stroke = null }
.Bind("Fill", "Color")
// the Shape.Stroke color depends on whether Node.IsHighlighted is true
.Bind(new Binding("Stroke", "IsHighlighted", h => (bool)h ? "red" : "black").OfElement()),
new TextBlock { Margin = 10, Font = new Font("Verdana", 18, FontWeight.Bold) }
.Bind("Text", "Key")
);
diagram.LinkTemplate =
new Link { ToShortLength = 4 }
.Add(
new Shape()
// the Shape.Stroke color depends on whether Link.IsHighlighted is true
.Bind(new Binding("Stroke", "IsHighlighted", h => (bool)h ? "red" : "black").OfElement())
// the Shape.StrokeWidth depends on whether Link.IsHighlighted is true
.Bind(new Binding("StrokeWidth", "IsHighlighted", h => (bool)h ? 3 : 1).OfElement()),
new Shape { ToArrow = "Standard", StrokeWidth = 0 }
// the Shape.Fill color depends on whether Link.IsHighlighted is true
.Bind(new Binding("Fill", "IsHighlighted", h => (bool)h ? "red" : "black").OfElement())
);
// when the user clicks on the background of the Diagram, remove all highlighting
diagram.Click = e => {
e.Diagram.Commit(d => d.ClearHighlighteds(), "no highlighteds");
};
diagram.Model =
new MyModel {
NodeDataSource = new List<NodeData> {
new NodeData { Key = "Alpha", Color = "#96D6D9" },
new NodeData { Key = "Beta", Color = "#96D6D9" },
new NodeData { Key = "Gamma", Color = "#EFEBCA" },
new NodeData { Key = "Delta", Color = "#EFEBCA" }
},
LinkDataSource = new List<LinkData> {
new LinkData { From = "Alpha", To = "Beta" },
new LinkData { From = "Alpha", To = "Gamma" },
new LinkData { From = "Beta", To = "Beta" },
new LinkData { From = "Gamma", To = "Delta" },
new LinkData { From = "Delta", To = "Alpha" }
}
};
Clicking on a node highlights outbound connected links and nodes. Clicking in the diagram background removes all highlights. Note that the highlighting is independent of selection.
The use of data binding to modify the Shape properties allows you to avoid specifying names for each Shape and writing code to find the Shape and modify its properties.
You may want to increase the size of a node or of an element in a node in order to highlight it. For example you could have a Binding on GraphObject.Scale or Shape.StrokeWidth:
new Node() { ... }
.Add(
new Shape() { ... }
.Bind("StrokeWidth", "IsHighlighted", h => (bool)h ? 5 : 1),
...
);
However, doing so will change the size of the object. That is likely to invalidate the route of any links that are connected with that node. That might not matter in many apps, but in some cases the routes of some links may have been reshaped by the user. Any recomputation of the route due to a connected node moving or changing size might lose that route.
If that is a consideration in your app, you might consider instead having each node hold an additional Shape that would provide the highlighting when shown and that would be unseen otherwise. But do not toggle the GraphObject.Visible property, because that would cause the node to change size. Instead toggle the GraphObject.Opacity property between 0.0 and 1.0.
diagram.NodeTemplate =
new Node("Auto") {
LocationSpot = Spot.Center,
// when the user clicks on a Node, highlight all Links coming out of the node
// and all of the Nodes at the other ends of those Links.
Click = (e, obj) => {
var node = obj as Node;
node.Diagram.Commit(d => {
d.ClearHighlighteds();
foreach (var l in node.FindLinksOutOf()) {
l.IsHighlighted = true;
}
foreach (var n in node.FindNodesOutOf()) {
n.IsHighlighted = true;
}
}, "highlight");
}
}
.Add(
new Panel("Auto")
.Add(
new Shape("Ellipse") { StrokeWidth = 2, PortId = "" }
.Bind("Fill", "Color"),
new TextBlock { Margin = 5, Font = new Font("Verdana", 18, FontWeight.Bold) }
.Bind("Text", "Key")
),
// the highlight shape, which is always a thick red ellipse
new Shape("Ellipse") {
// this shape is the "border" of the Auto Panel, but is drawn in front of the
// regular Auto Panel holding the black-bordered ellipse and text
IsPanelMain = true, Spot1 = Spot.TopLeft, Spot2 = Spot.BottomRight,
StrokeWidth = 6, Stroke = "red", Fill = null
}
// only show this ellipse when Part.IsHighlighted is true
.Bind(new Binding("Opacity", "IsHighlighted", h => (bool)h ? 1.0 : 0.0).OfElement())
);
diagram.LinkTemplate =
new Link { ToShortLength = 4, Reshapable = true, Resegmentable = true }
.Add(
new Shape()
/// when highlighted, draw as a thick red line
.Bind(new Binding("Stroke", "IsHighlighted", h => (bool)h ? "red" : "black").OfElement())
.Bind(new Binding("StrokeWidth", "IsHighlighted", h => (bool)h ? 3 : 1).OfElement()),
new Shape { ToArrow = "Standard", StrokeWidth = 0 }
.Bind(new Binding("Fill", "IsHighlighted", h => (bool)h ? "red" : "black").OfElement())
);
// when the user clicks on the background of the Diagram, remove all highlighting
diagram.Click = e => {
e.Diagram.Commit(d => d.ClearHighlighteds(), "no highlighteds");
};
diagram.Model =
new MyModel {
NodeDataSource = new List<NodeData> {
new NodeData { Key = "Alpha", Color = "#96D6D9" },
new NodeData { Key = "Beta", Color = "#96D6D9" },
new NodeData { Key = "Gamma", Color = "#EFEBCA" },
new NodeData { Key = "Delta", Color = "#EFEBCA" }
},
LinkDataSource = new List<LinkData> {
new LinkData { From = "Alpha", To = "Beta" },
new LinkData { From = "Alpha", To = "Gamma" },
new LinkData { From = "Beta", To = "Beta" },
new LinkData { From = "Gamma", To = "Delta" },
new LinkData { From = "Delta", To = "Alpha" }
}
};
The highlight Shape is the outer ellipse that always has a thick red stroke. It is normally hidden by having zero opacity, but the Binding will change its opacity to one when Part.IsHighlighted is true.
That highlight Shape is always shown in front of the panel of the colored ellipse and text by putting it afterwards in the list of the panel's child elements. However since the "Auto" Panel assumes the first element acts as the border, we need to set GraphObject.IsPanelMain to true on the highlight Shape so that it is the border for the inner panel.