Just wondering if it’s possible to draw a link which is not attached to anything by just clicking and dragging? Obviously by default that behavior is for selection, but I would like to be able to draw unconnected links this way rather than dragging a predefined link onto the diagram and then moving and resizing that link (as in the draggable link demo).
public override void DoDeactivate() {
base.DoDeactivate();
this.Diagram.Cursor = null;
ReleaseMouse();
StopTransaction();
this.CurrentData = null;
this.CurrentLink = null;
}
MyLinkData CurrentData { get; set; }
Link CurrentLink { get; set; }
String IntendedLayerName { get; set; }
private void AddPoint(Point p) {
// make sure there's a new MyLinkData in the model
MyLinkData d = this.CurrentData;
if (d == null) {
// create and initialize the MyLinkData
d = new MyLinkData();
d.Points = new List<Point>();
this.CurrentData = d;
// add the MyLinkData to the model to produce a Link
((MyModel)this.Diagram.Model).AddLink(d);
this.CurrentLink = this.Diagram.PartManager.FindLinkForData(d, this.Diagram.Model);
// temporarily put into the temporary "Tool" layer, so it's in front of anything else
this.IntendedLayerName = this.CurrentLink.LayerName;
this.CurrentLink.LayerName = "Tool";
}
// add the Point to the data and to the PointCollection
var pts = d.Points.ToList();
pts.Add(p);
d.Points = pts;
this.CurrentLink.Route.Points = pts;
}
private void MoveLastPoint(Point p) {
MyLinkData d = this.CurrentData;
if (d == null) return;
if (((List<Point>)d.Points).Count > 0) {
// replace the last point in the data and in the PointCollection
((List<Point>)d.Points)[((List<Point>)d.Points).Count-1] = p;
}
}
private void RemoveLastPoint() {
MyLinkData d = this.CurrentData;
if (d == null) return;
if (((List<Point>)d.Points).Count > 0) {
// replace the last point in the data and in the PointCollection
((List<Point>)d.Points).RemoveAt(((List<Point>)d.Points).Count-1);
}
}
private void FinishShape() {
MyLinkData d = this.CurrentData;
if (d == null) return;
// if there aren't enough points, discard it by cancelling
if (((List<Point>)d.Points).Count < 2) {
DoCancel();
} else {
// put into desired layer and update the diagram bounds
this.CurrentLink.LayerName = this.IntendedLayerName;
this.CurrentLink.Route.Points = (List<Point>)d.Points;
this.Diagram.Panel.UpdateDiagramBounds();
// select the new Link
this.CurrentLink.IsSelected = true;
// and tell the tool to commit the transaction
this.TransactionResult = "New Routed Link";
StopTool();
}
}
public override void DoMouseDown() {
if (!this.Active) {
DoActivate();
// the first point
AddPoint(this.Diagram.LastMousePointInModel);
}
if (IsLeftButtonDown()) {
// a temporary end point to be replaced by DoMouseMove
AddPoint(this.Diagram.LastMousePointInModel);
} else { // e.g. right mouse down
FinishShape();
}
}
public override void DoMouseMove() {
if (this.Active) {
// the temporary last point follows the mouse
MoveLastPoint(this.Diagram.LastMousePointInModel);
}
}
public override void DoMouseUp() {
// don't stop this tool (the default behavior is to call StopTool)
}
public override void DoKeyDown(KeyEventArgs e) {
if (e.Key == Key.Enter) { // accept
// remove the temporary point, which is last
RemoveLastPoint();
FinishShape();
} else if (e.Key == Key.Z) { // undo
// remove a point, and treat the last one as a temporary one
RemoveLastPoint();
MoveLastPoint(this.Diagram.LastMousePointInModel);
} else {
base.DoKeyDown(e);
}
}
public override void DoCancel() {
if (this.CurrentData != null) {
((MyModel)this.Diagram.Model).RemoveLink(this.CurrentData);
}
base.DoCancel();
}
}[/code]
Use this as a modal tool by setting Diagram.CurrentTool, such as:
myDiagram.CurrentTool = new RoutingLinkTool() { Diagram = myDiagram };
Works great, thanks… For anyone interested I did make the lines orthogonal, made the drawing a click-drag-release instead of multiple clicks, and added a temporary line to show where the link was going. Here’s what I changed:
private void FinishShape()
{
PipeData d = this.CurrentData;
if (d == null) return;
// if there aren't enough points, discard it by cancelling
if (((List<Point>)d.Points).Count < 2)
{
DoCancel();
}
else
{
Point start = d.Points.First();
Point end = d.Points.Last();
d.Points = makeOrthogonal(start, end);
Brush pipeColor = ((Document)((FrameworkElement)this.Diagram.Parent).Parent).PipeColor;
this.CurrentLink.Path.Stroke = pipeColor;
this.CurrentLink.Path.StrokeThickness = 3.0;
this.CurrentLink.DropOntoBehavior = DropOntoBehavior.SplicesIntoLink;
this.CurrentLink.RelinkableTo = true;
this.CurrentLink.RelinkableFrom = true;
d.PipeColor = pipeColor;
// put into desired layer and update the diagram bounds
this.CurrentLink.LayerName = this.IntendedLayerName;
this.CurrentLink.Route.Points = (List<Point>)d.Points;
this.CurrentLink.Route.Routing = LinkRouting.AvoidsNodes;
this.CurrentLink.Route.Curve = LinkCurve.JumpOver;
this.CurrentLink.Route.Corner = 5;
this.Diagram.Panel.UpdateDiagramBounds();
// select the new Link
this.CurrentLink.IsSelected = true;
// and tell the tool to commit the transaction
this.TransactionResult = "New Routed Link";
StopTool();
}
}
private List<Point> makeOrthogonal(Point start, Point end)
{
double midpoint1X;
double midpoint2X;
double midpoint1Y;
double midpoint2Y;
if (start.X < end.X)
midpoint1X = start.X + 20;
else
midpoint1X = start.X - 20;
midpoint2X = midpoint1X;
midpoint1Y = start.Y;
midpoint2Y = end.Y;
List<Point> points = new List<Point>();
points.Add(start);
points.Add(new Point(midpoint1X, midpoint1Y));
points.Add(new Point(midpoint2X, midpoint2Y));
points.Add(end);
return points;
}
public override void DoMouseDown()
{
if (!this.Active)
{
DoActivate();
// the first point
AddPoint(this.Diagram.LastMousePointInModel);
AddPoint(this.Diagram.LastMousePointInModel);
this.CurrentLink.Path.Stroke = Brushes.Blue;
this.CurrentLink.Path.StrokeThickness = 1.0;
}
}
public override void DoMouseMove()
{
if (this.Active)
{
// the temporary last point follows the mouse
MoveLastPoint(this.Diagram.LastMousePointInModel);
this.CurrentLink.Route.Points = (List<Point>)this.CurrentData.Points;
}
}
public override void DoMouseUp()
{
FinishShape();
}
How can I keep the link drawing tool active after completing a link? I would like to be able to draw multiple lines without having to click the button each time to re-set the current tool.
You’ll need to implement DoMouseUp completely, actually creating the appropriate link, because the default implementation calls StopTool.
Alternatively you could let it call StopTool and then explicitly restart the LinkingTool again by setting its LinkingTool.StartPort property and then setting Diagram.CurrentTool. That might be easier to implement.