Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / WF / Common / AuthoringOM / Design / Connector.cs / 1305376 / Connector.cs
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms;
using System.Diagnostics;
using System.ComponentModel.Design.Serialization;
using System.Workflow.ComponentModel.Serialization;
namespace System.Workflow.ComponentModel.Design
{
#region Class ConnectionPointHitTestInfo
internal sealed class ConnectionPointHitTestInfo : HitTestInfo
{
private ConnectionPoint connectionPoint;
internal ConnectionPointHitTestInfo(ConnectionPoint connectionPoint)
: base(connectionPoint.AssociatedDesigner, HitTestLocations.Designer | HitTestLocations.Connector)
{
this.connectionPoint = connectionPoint;
}
internal ConnectionPoint ConnectionPoint
{
get
{
return this.connectionPoint;
}
}
}
#endregion
#region ConnectorEvent
public delegate void ConnectorEventHandler(object sender, ConnectorEventArgs e);
#region Class ConnectorEventArgs
public class ConnectorEventArgs : EventArgs
{
private Connector connector;
internal ConnectorEventArgs(Connector connector)
{
this.connector = connector;
}
public Connector Connector
{
get
{
return this.connector;
}
}
}
#endregion
#endregion
#region Class ConnectionPoint
public class ConnectionPoint
{
private ActivityDesigner associatedDesigner;
private DesignerEdges designerEdge;
private int connectionIndex;
public ConnectionPoint(ActivityDesigner associatedDesigner, DesignerEdges designerEdge, int connectionIndex)
{
if (associatedDesigner == null)
throw new ArgumentNullException("associatedDesigner");
if (connectionIndex < 0 || connectionIndex >= associatedDesigner.GetConnections(designerEdge).Count)
throw new ArgumentException(DR.GetString(DR.Error_ConnectionPoint), "connectionIndex");
this.associatedDesigner = associatedDesigner;
this.designerEdge = designerEdge;
this.connectionIndex = connectionIndex;
}
public ActivityDesigner AssociatedDesigner
{
get
{
return this.associatedDesigner;
}
}
public int ConnectionIndex
{
get
{
return this.connectionIndex;
}
}
public DesignerEdges ConnectionEdge
{
get
{
DesignerEdges designerEdge = this.designerEdge;
if (designerEdge != DesignerEdges.Left && designerEdge != DesignerEdges.Right &&
designerEdge != DesignerEdges.Top && designerEdge != DesignerEdges.Bottom)
{
designerEdge = DesignerGeometryHelper.ClosestEdgeToPoint(Location, this.associatedDesigner.Bounds, designerEdge);
}
return designerEdge;
}
}
public virtual Point Location
{
get
{
IList connections = this.associatedDesigner.GetConnections(this.designerEdge);
if (this.connectionIndex < connections.Count)
return connections[this.connectionIndex];
else
return Point.Empty;
}
}
public virtual Rectangle Bounds
{
get
{
IList connections = this.associatedDesigner.GetConnections(DesignerEdges.All);
if (connections.Count > 0)
{
Point location = Location;
Size size = DefaultSize;
Rectangle enclosingBounds = new Rectangle(new Point(location.X - size.Width / 2, location.Y - size.Height / 2), size);
return enclosingBounds;
}
else
{
return Rectangle.Empty;
}
}
}
public override bool Equals(object obj)
{
ConnectionPoint connectionPoint = obj as ConnectionPoint;
if (connectionPoint == null)
return false;
if (connectionPoint.AssociatedDesigner == this.associatedDesigner &&
connectionPoint.designerEdge == this.designerEdge &&
connectionPoint.ConnectionIndex == this.connectionIndex)
return true;
else
return false;
}
public override int GetHashCode()
{
return (this.associatedDesigner.GetHashCode() ^ this.designerEdge.GetHashCode() ^ this.connectionIndex.GetHashCode());
}
public void OnPaint(ActivityDesignerPaintEventArgs e, bool drawHighlighted)
{
Draw(e, Bounds);
}
internal static void Draw(ActivityDesignerPaintEventArgs e, Rectangle bounds)
{
bounds.Inflate(-1, -1);
e.Graphics.FillEllipse(Brushes.White, bounds);
e.Graphics.DrawEllipse(e.AmbientTheme.SelectionForegroundPen, bounds);
bounds.Inflate(-1, -1);
e.Graphics.FillEllipse(e.AmbientTheme.SelectionForegroundBrush, bounds);
}
private Size DefaultSize
{
get
{
Size defaultSize = WorkflowTheme.CurrentTheme.AmbientTheme.Margin;
defaultSize.Width += defaultSize.Width / 2;
defaultSize.Height += defaultSize.Height / 2;
if (this.associatedDesigner != null)
defaultSize = new Size(Math.Max(defaultSize.Width, (int)this.associatedDesigner.DesignerTheme.ForegroundPen.Width * 4), Math.Max(defaultSize.Height, (int)this.associatedDesigner.DesignerTheme.ForegroundPen.Width * 4));
return defaultSize;
}
}
}
#endregion
#region Class Connector
[DesignerSerializer(typeof(ConnectorLayoutSerializer), typeof(WorkflowMarkupSerializer))]
public class Connector : IDisposable
{
#region Members, Construction, Dispose
private AccessibleObject accessibilityObject;
private FreeformActivityDesigner parentDesigner;
private ConnectionPoint source = null;
private ConnectionPoint target = null;
private List segments = new List();
private bool connectorModified = false;
public Connector(ConnectionPoint source, ConnectionPoint target)
{
if (source == null)
throw new ArgumentNullException("source");
if (target == null)
throw new ArgumentNullException("target");
if (ConnectionManager.GetConnectorContainer(source.AssociatedDesigner) != ConnectionManager.GetConnectorContainer(target.AssociatedDesigner))
throw new ArgumentException(DR.GetString(DR.Error_Connector1));
this.source = source;
this.target = target;
}
void IDisposable.Dispose()
{
}
#endregion
#region Properties and Methods
public virtual AccessibleObject AccessibilityObject
{
get
{
if (this.accessibilityObject == null)
this.accessibilityObject = new ConnectorAccessibleObject(this);
return this.accessibilityObject;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ConnectionPoint Source
{
get
{
return this.source;
}
set
{
if (value == null)
throw new ArgumentNullException("value");
if (this.source.Equals(value))
return;
if (ConnectionManager.GetConnectorContainer(value.AssociatedDesigner) != ConnectionManager.GetConnectorContainer(this.target.AssociatedDesigner))
throw new ArgumentException(SR.GetString(SR.Error_InvalidConnectorSource), "value");
this.source = value;
PerformLayout();
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ConnectionPoint Target
{
get
{
return this.target;
}
set
{
if (value == null)
throw new ArgumentNullException("value");
if (this.target.Equals(value))
return;
if (ConnectionManager.GetConnectorContainer(value.AssociatedDesigner) != ConnectionManager.GetConnectorContainer(this.source.AssociatedDesigner))
throw new ArgumentException(SR.GetString(SR.Error_InvalidConnectorSource), "value");
this.target = value;
PerformLayout();
}
}
public virtual ReadOnlyCollection ConnectorSegments
{
get
{
List connectorSegments = new List();
if (this.source != null && this.target != null)
{
if (this.segments.Count == 0 || this.segments[0] != this.source.Location)
connectorSegments.Add(this.source.Location);
connectorSegments.AddRange(this.segments);
if (this.segments.Count == 0 || this.segments[this.segments.Count - 1] != this.target.Location)
connectorSegments.Add(this.target.Location);
}
return connectorSegments.AsReadOnly();
}
}
public Rectangle Bounds
{
get
{
Rectangle bounds = DesignerGeometryHelper.RectangleFromLineSegments(new List(ConnectorSegments).ToArray());
bounds.Inflate(1, 1);
return bounds;
}
}
public FreeformActivityDesigner ParentDesigner
{
get
{
return this.parentDesigner;
}
}
public virtual bool HitTest(Point point)
{
Size selectionSize = WorkflowTheme.CurrentTheme.AmbientTheme.SelectionSize;
//We go thru the line seagments and hittest
ReadOnlyCollection segments = ConnectorSegments;
for (int i = 1; i < segments.Count; i++)
{
if (DesignerGeometryHelper.PointOnLineSegment(point, new Point[] { segments[i - 1], segments[i] }, selectionSize))
return true;
}
return false;
}
public virtual void Offset(Size size)
{
for (int i = 0; i < this.segments.Count; i++)
this.segments[i] = new Point(this.segments[i].X + size.Width, this.segments[i].Y + size.Height);
}
public override bool Equals(object obj)
{
Connector connector = obj as Connector;
if (connector == null)
return false;
return (connector.Source == this.source && connector.target == this.target);
}
public override int GetHashCode()
{
if (this.source != null && this.target != null)
return this.source.GetHashCode() ^ this.target.GetHashCode();
else
return base.GetHashCode();
}
public void Invalidate()
{
WorkflowView workflowView = ParentView;
if (workflowView != null)
workflowView.InvalidateLogicalRectangle(Bounds);
}
public bool ConnectorModified
{
get
{
return this.connectorModified;
}
}
protected WorkflowView ParentView
{
get
{
return GetService(typeof(WorkflowView)) as WorkflowView;
}
}
protected void PerformLayout()
{
WorkflowView workflowView = ParentView;
if (workflowView != null)
workflowView.PerformLayout(false);
}
protected internal virtual ICollection ExcludedRoutingRectangles
{
get
{
return new Rectangle[] { };
}
}
//We want to allow framework or the derived classes only to set the connector segments
//We do not want any outside entity to set the connector segments randomly
protected internal void SetConnectorSegments(ICollection segments)
{
//Called by derived classes when they want to change connector routing
//
if (segments == null)
throw new ArgumentNullException("segments");
this.connectorModified = (this.parentDesigner != null && segments.Count > 0);
if (this.connectorModified)
Invalidate();
this.segments.Clear();
this.segments.AddRange(segments);
if (this.connectorModified)
Invalidate();
}
protected internal virtual void OnLayout(ActivityDesignerLayoutEventArgs e)
{
if (this.segments.Count > 0 &&
(this.segments[0] != Source.Location || this.segments[this.segments.Count - 1] != Target.Location))
this.connectorModified = false;
if (!connectorModified && ParentDesigner != null)
{
Point[] newSegments = ActivityDesignerConnectorRouter.Route(Source.AssociatedDesigner.Activity.Site, Source, Target, ExcludedRoutingRectangles);
this.segments.Clear();
this.segments.AddRange(newSegments);
}
}
protected internal virtual void OnPaint(ActivityDesignerPaintEventArgs e)
{
CompositeDesignerTheme theme = e.DesignerTheme as CompositeDesignerTheme;
if (theme != null)
{
Size arrowCapSize = new Size(theme.ConnectorSize.Width / 5, theme.ConnectorSize.Height / 5);
Size maxCapSize = theme.ConnectorSize;
ActivityDesignerPaint.DrawConnectors(e.Graphics, e.DesignerTheme.ForegroundPen, new List(ConnectorSegments).ToArray(), arrowCapSize, maxCapSize, theme.ConnectorStartCap, theme.ConnectorEndCap);
}
}
protected internal virtual void OnPaintSelected(ActivityDesignerPaintEventArgs e, bool primarySelection, Point[] segmentEditPoints)
{
CompositeDesignerTheme theme = e.DesignerTheme as CompositeDesignerTheme;
if (theme == null)
return;
using (Pen lineSelectionPen = new Pen(WorkflowTheme.CurrentTheme.AmbientTheme.SelectionForeColor, 1))
{
Size arrowCapSize = new Size(theme.ConnectorSize.Width / 5, theme.ConnectorSize.Height / 5);
Size maxCapSize = theme.ConnectorSize;
ActivityDesignerPaint.DrawConnectors(e.Graphics, lineSelectionPen, new List(ConnectorSegments).ToArray(), arrowCapSize, maxCapSize, theme.ConnectorStartCap, theme.ConnectorEndCap);
}
if (this.source != null)
this.source.OnPaint(e, false);
ReadOnlyCollection endSegmentEditPoints = ConnectorSegments;
for (int i = 1; i < endSegmentEditPoints.Count - 1; i++)
PaintEditPoints(e, endSegmentEditPoints[i], false);
for (int i = 0; i < segmentEditPoints.Length; i++)
PaintEditPoints(e, segmentEditPoints[i], true);
if (this.target != null)
this.target.OnPaint(e, false);
}
protected internal virtual void OnPaintEdited(ActivityDesignerPaintEventArgs e, Point[] segments, Point[] segmentEditPoints)
{
CompositeDesignerTheme theme = e.DesignerTheme as CompositeDesignerTheme;
if (theme == null)
return;
using (Pen editableConnectorPen = new Pen(e.AmbientTheme.SelectionForegroundPen.Color, e.AmbientTheme.SelectionForegroundPen.Width))
{
editableConnectorPen.DashStyle = DashStyle.Dash;
Size arrowCapSize = new Size(theme.ConnectorSize.Width / 5, theme.ConnectorSize.Height / 5);
Size maxCapSize = theme.ConnectorSize;
ActivityDesignerPaint.DrawConnectors(e.Graphics, editableConnectorPen, segments, arrowCapSize, maxCapSize, theme.ConnectorStartCap, theme.ConnectorEndCap);
}
if (this.source != null)
this.source.OnPaint(e, false);
for (int i = 1; i < segments.Length - 1; i++)
PaintEditPoints(e, segments[i], false);
for (int i = 0; i < segmentEditPoints.Length; i++)
PaintEditPoints(e, segmentEditPoints[i], true);
if (this.target != null)
this.target.OnPaint(e, false);
}
protected virtual object GetService(Type serviceType)
{
object service = null;
if (this.parentDesigner != null && this.parentDesigner.Activity != null && this.parentDesigner.Activity.Site != null)
service = this.parentDesigner.Activity.Site.GetService(serviceType);
return service;
}
#endregion
#region Helpers
private void PaintEditPoints(ActivityDesignerPaintEventArgs e, Point point, bool drawMidSegmentEditPoint)
{
Size size = (this.source != null) ? this.source.Bounds.Size : Size.Empty;
if (!size.IsEmpty)
{
Rectangle bounds = new Rectangle(point.X - size.Width / 2, point.Y - size.Height / 2, size.Width, size.Height);
if (drawMidSegmentEditPoint)
{
using (GraphicsPath path = new GraphicsPath())
{
path.AddLine(new Point(bounds.Left + bounds.Width / 2, bounds.Top), new Point(bounds.Right, bounds.Top + bounds.Height / 2));
path.AddLine(new Point(bounds.Right, bounds.Top + bounds.Height / 2), new Point(bounds.Left + bounds.Width / 2, bounds.Bottom));
path.AddLine(new Point(bounds.Left + bounds.Width / 2, bounds.Bottom), new Point(bounds.Left, bounds.Top + bounds.Height / 2));
path.AddLine(new Point(bounds.Left, bounds.Top + bounds.Height / 2), new Point(bounds.Left + bounds.Width / 2, bounds.Top));
e.Graphics.FillPath(Brushes.White, path);
e.Graphics.DrawPath(e.AmbientTheme.SelectionForegroundPen, path);
}
}
else
{
bounds.Inflate(-1, -1);
e.Graphics.FillEllipse(e.AmbientTheme.SelectionForegroundBrush, bounds);
}
}
}
internal void SetConnectorModified(bool modified)
{
this.connectorModified = modified;
}
internal FreeformActivityDesigner RenderingOwner
{
get
{
if (this.source == null || this.target == null)
return null;
//
List targetParents = new List();
ActivityDesigner designer = this.target.AssociatedDesigner;
while (designer != null)
{
FreeformActivityDesigner parentFreeFormDesigner = designer as FreeformActivityDesigner;
if (parentFreeFormDesigner != null)
targetParents.Add(parentFreeFormDesigner);
designer = designer.ParentDesigner;
}
//Go through the parent freeforms of source and match it with target, the first common
//parent freeform is the rendering designer
designer = this.source.AssociatedDesigner;
while (designer != null)
{
FreeformActivityDesigner parentFreeFormDesigner = designer as FreeformActivityDesigner;
if (parentFreeFormDesigner != null && targetParents.Contains(parentFreeFormDesigner))
break;
designer = designer.ParentDesigner;
}
return designer as FreeformActivityDesigner;
}
}
internal void SetParent(FreeformActivityDesigner parentDesigner)
{
//Make sure that this is in parent chain of both source and the target connection points
WorkflowView workflowView = ParentView;
if (this.parentDesigner != null && workflowView != null)
workflowView.InvalidateLogicalRectangle(this.parentDesigner.Bounds);
this.parentDesigner = parentDesigner;
if (this.parentDesigner != null && workflowView != null)
workflowView.InvalidateLogicalRectangle(this.parentDesigner.Bounds);
}
internal static Connector GetConnectorFromSelectedObject(object selectedObject)
{
Connector connector = null;
ConnectorHitTestInfo connectorHitTestInfo = selectedObject as ConnectorHitTestInfo;
if (connectorHitTestInfo != null)
{
FreeformActivityDesigner connectorContainer = connectorHitTestInfo.AssociatedDesigner as FreeformActivityDesigner;
int index = connectorHitTestInfo.MapToIndex();
if (connectorContainer != null && index >= 0 && index < connectorContainer.Connectors.Count)
connector = connectorContainer.Connectors[index];
}
return connector;
}
#region Properties used during serialization only
//NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
internal string SourceActivity
{
get
{
string activityName = String.Empty;
if (this.source != null)
activityName = this.source.AssociatedDesigner.Activity.QualifiedName;
return activityName;
}
set
{
}
}
//NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
internal int SourceConnectionIndex
{
get
{
int connectionIndex = 0;
if (this.source != null)
connectionIndex = this.source.ConnectionIndex;
return connectionIndex;
}
set
{
}
}
//NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
internal DesignerEdges SourceConnectionEdge
{
get
{
DesignerEdges connectionEdge = DesignerEdges.None;
if (this.source != null)
connectionEdge = this.source.ConnectionEdge;
return connectionEdge;
}
set
{
}
}
//NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
internal string TargetActivity
{
get
{
string activityName = String.Empty;
if (this.target != null)
activityName = this.target.AssociatedDesigner.Activity.QualifiedName;
return activityName;
}
set
{
}
}
//NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
internal int TargetConnectionIndex
{
get
{
int connectionIndex = 0;
if (this.target != null)
connectionIndex = this.target.ConnectionIndex;
return connectionIndex;
}
set
{
}
}
//NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
internal DesignerEdges TargetConnectionEdge
{
get
{
DesignerEdges connectionEdge = DesignerEdges.None;
if (this.target != null)
connectionEdge = this.target.ConnectionEdge;
return connectionEdge;
}
set
{
}
}
//NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
internal List Segments
{
get
{
return this.segments;
}
}
#endregion
#endregion
}
#endregion
#region Class ConnectorAccessibleObject
public class ConnectorAccessibleObject : AccessibleObject
{
private Connector connector;
public ConnectorAccessibleObject(Connector connector)
{
if (connector == null)
throw new ArgumentNullException("connector");
this.connector = connector;
}
public override Rectangle Bounds
{
get
{
WorkflowView parentView = this.connector.ParentDesigner.ParentView;
Rectangle bounds = this.connector.Bounds;
return new Rectangle(parentView.LogicalPointToScreen(bounds.Location), parentView.LogicalSizeToClient(bounds.Size));
}
}
public override AccessibleObject HitTest(int x, int y)
{
WorkflowView parentView = this.connector.ParentDesigner.ParentView;
if (this.connector.HitTest(parentView.ScreenPointToLogical(new Point(x, y))))
return this;
else
return null;
}
public override string Name
{
get
{
return this.connector.GetType().Name;
}
set
{
}
}
public override AccessibleObject Parent
{
get
{
return connector.ParentDesigner.AccessibilityObject;
}
}
public override AccessibleRole Role
{
get
{
return AccessibleRole.Diagram;
}
}
}
#endregion Class ConnectorAccessibleObject
#region Class ConnectorEditor
internal sealed class ConnectorEditor
{
private IServiceProvider serviceProvider;
private Connector editedConnector;
private EditPoint activeEditPoint;
private List editPoints = new List();
public ConnectorEditor(Connector connectorEdited)
{
this.editedConnector = connectorEdited;
this.serviceProvider = this.editedConnector.Source.AssociatedDesigner.Activity.Site;
CreateEditPoints();
}
public ConnectionPoint EditedConectionPoint
{
get
{
if (this.activeEditPoint != null)
return this.activeEditPoint.EditedConnectionPoint;
else
return null;
}
}
public Connector EditedConnector
{
get
{
return this.editedConnector;
}
}
public Cursor GetCursor(Point cursorPoint)
{
Cursor cursor = Cursors.Default;
if (this.activeEditPoint != null)
{
cursor = ConnectionManager.NewConnectorCursor;
}
else
{
foreach (EditPoint editPoint in this.editPoints)
{
if (editPoint.Bounds.Contains(cursorPoint))
{
cursor = ConnectionManager.SnappedConnectionCursor;
break;
}
}
}
return cursor;
}
public bool OnBeginEditing(Point point)
{
//Get all the editable points
CreateEditPoints();
EditPoint editPointHit = null;
for (int i = this.editPoints.Count - 1; i >= 0; i--)
{
if (this.editPoints[i].Bounds.Contains(point))
{
editPointHit = this.editPoints[i];
break;
}
}
if (editPointHit != null &&
(editPointHit.EditedConnectionPoint == null ||
ConnectionManager.GetConnectorContainer(editPointHit.EditedConnectionPoint.AssociatedDesigner) != null))
{
editPointHit.Location = point;
this.activeEditPoint = editPointHit;
}
Invalidate();
return (this.activeEditPoint != null);
}
public void OnContinueEditing(Point point)
{
if (this.activeEditPoint == null)
return;
Invalidate();
UpdateEditPoints(point);
Invalidate();
#if DISPLAYESCAPEREGIONS
WorkflowView workflowView = GetService(typeof(WorkflowView)) as WorkflowView;
if (workflowView != null)
workflowView.InvalidateClientRectangle(Rectangle.Empty);
#endif
}
public void OnEndEditing(Point point, bool commitChanges)
{
if (this.activeEditPoint == null)
return;
Invalidate();
if (commitChanges)
{
//This is to update the edit points based on the activepoint
UpdateEditPoints(point);
EditPoint activeEditPoint = this.activeEditPoint;
this.activeEditPoint = null;
//This call is to optimize the segments
UpdateEditPoints(point);
//If we were creating a new connector or modifying the connection end points
bool updateSegments = false;
if (activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
{
ConnectionManager connectionManager = GetService(typeof(ConnectionManager)) as ConnectionManager;
FreeformActivityDesigner connectorContainer = ConnectionManager.GetConnectorContainer(activeEditPoint.EditedConnectionPoint.AssociatedDesigner);
if (connectionManager != null && connectionManager.SnappedConnectionPoint != null && connectorContainer != null)
{
//Get the original source and targets
ConnectionPoint source = this.editedConnector.Source;
ConnectionPoint target = this.editedConnector.Target;
//Make sure that we set the source and target correctly
if (target.Equals(activeEditPoint.EditedConnectionPoint))
target = connectionManager.SnappedConnectionPoint;
else if (source.Equals(activeEditPoint.EditedConnectionPoint))
source = connectionManager.SnappedConnectionPoint;
//Check if it is a valid connection ie source can be connected to target
if (connectorContainer == ConnectionManager.GetConnectorContainer(target.AssociatedDesigner) &&
connectorContainer.CanConnectContainedDesigners(source, target))
{
this.editedConnector.Source = source;
this.editedConnector.Target = target;
if (this.editedConnector.ParentDesigner == null)
{
this.editedConnector = connectorContainer.AddConnector(source, target);
WorkflowDesignerLoader loader = GetService(typeof(WorkflowDesignerLoader)) as WorkflowDesignerLoader;
if (loader != null)
loader.SetModified(true);
}
connectorContainer.OnContainedDesignersConnected(source, target);
}
updateSegments = true;
}
}
else
{
updateSegments = true;
}
//Make sure that we apply the edit points to the connector
if (updateSegments)
{
this.editedConnector.SetConnectorSegments(GetPointsFromEditPoints(this.editPoints));
if (this.editedConnector.ParentDesigner != null)
{
this.editedConnector.ParentDesigner.OnConnectorChanged(new ConnectorEventArgs(this.editedConnector));
WorkflowDesignerLoader loader = GetService(typeof(WorkflowDesignerLoader)) as WorkflowDesignerLoader;
if (loader != null)
loader.SetModified(true);
}
}
PerformLayout();
}
Invalidate();
}
public bool HitTest(Point point)
{
for (int i = 0; i < this.editPoints.Count; i++)
{
EditPoint editPoint = this.editPoints[i];
if (editPoint.Bounds.Contains(point))
return true;
}
return false;
}
public void OnPaint(ActivityDesignerPaintEventArgs e, bool drawSelected, bool drawPrimarySelection)
{
List segments = new List();
List segmentEditPoints = new List();
for (int i = 0; i < this.editPoints.Count; i++)
{
EditPoint editPoint = this.editPoints[i];
if (editPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint ||
editPoint.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint)
segments.Add(editPoint.Location);
else
segmentEditPoints.Add(editPoint.Location);
}
//If the editing is in progress then we want to draw the dashed lines
if (drawSelected)
this.editedConnector.OnPaintSelected(e, drawPrimarySelection, segmentEditPoints.ToArray());
if (this.activeEditPoint != null)
this.editedConnector.OnPaintEdited(e, segments.ToArray(), segmentEditPoints.ToArray());
//
#if DISPLAYESCAPEREGIONS
if (this.activeEditPoint != null && this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
{
object source = null, target = null;
if (this.activeEditPoint.EditedConnectionPoint.Equals(Target))
{
target = this.activeEditPoint.Location;
source = Source;
}
else
{
source = this.activeEditPoint.Location;
target = Target;
}
List rectanglesToExclude;
List linesToExclude, pointsToExclude;
ActivityDesignerConnectorRouter.GetRoutingObstacles(this.serviceProvider, source, target, out rectanglesToExclude, out linesToExclude, out pointsToExclude);
ICollection userDefinedObstacles = this.editedConnector.ExcludedRoutingRectangles;
if (userDefinedObstacles != null)
{
foreach (Rectangle rectangle in userDefinedObstacles)
e.Graphics.DrawRectangle(Pens.DarkGreen, rectangle);
}
foreach (Rectangle rectangle in rectanglesToExclude)
e.Graphics.DrawRectangle(Pens.Red, rectangle);
for (int i = 0; i < linesToExclude.Count / 2; i++)
e.Graphics.DrawLine(Pens.Red, linesToExclude[i * 2], linesToExclude[(i * 2) + 1]);
}
#endif
}
#region Helpers
#region EditPointUpdation Logic
private ConnectionPoint Source
{
get
{
return this.editedConnector.Source;
}
}
private ConnectionPoint Target
{
get
{
return this.editedConnector.Target;
}
}
private void PerformLayout()
{
WorkflowView workflowView = GetService(typeof(WorkflowView)) as WorkflowView;
if (workflowView != null)
workflowView.PerformLayout(false);
}
private void Invalidate()
{
WorkflowView workflowView = GetService(typeof(WorkflowView)) as WorkflowView;
if (workflowView != null)
{
Rectangle bounds = DesignerGeometryHelper.RectangleFromLineSegments(GetPointsFromEditPoints(this.editPoints).ToArray());
bounds.Inflate(1, 1);
workflowView.InvalidateLogicalRectangle(bounds);
}
}
private object GetService(Type serviceType)
{
object service = null;
if (this.serviceProvider != null)
service = this.serviceProvider.GetService(serviceType);
return service;
}
private void CreateEditPoints()
{
this.editPoints.Clear();
AddEditPoints(EditPoint.EditPointTypes.ConnectionEditPoint);
AddEditPoints(EditPoint.EditPointTypes.MultiSegmentEditPoint);
AddEditPoints(EditPoint.EditPointTypes.MidSegmentEditPoint);
bool validEditPoints = ValidateEditPoints();
Debug.Assert(validEditPoints);
}
private void UpdateEditPoints(Point newPoint)
{
if (this.editPoints.Count < 2 ||
this.editPoints[0].Type != EditPoint.EditPointTypes.ConnectionEditPoint ||
this.editPoints[this.editPoints.Count - 1].Type != EditPoint.EditPointTypes.ConnectionEditPoint)
{
Debug.Assert(false);
return;
}
//STEP1: First we delete all the midsegmentpoints except the one which is being edited for simplicity
RemoveEditPoints(EditPoint.EditPointTypes.MidSegmentEditPoint);
//STEP2: Update points as per the type of edit point
if (this.activeEditPoint != null)
{
int activeEditPointIndex = this.editPoints.IndexOf(this.activeEditPoint);
EditPoint previous = (activeEditPointIndex > 0) ? this.editPoints[activeEditPointIndex - 1] : null;
EditPoint next = (activeEditPointIndex < this.editPoints.Count - 1) ? this.editPoints[activeEditPointIndex + 1] : null;
//Note that extra edit points are only added if we are connected to connection point
if (previous != null && previous.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
{
float slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, this.activeEditPoint.Location);
Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical;
int editPointOffset = Convert.ToInt32(DesignerGeometryHelper.DistanceBetweenPoints(previous.Location, (next != null) ? next.Location : this.activeEditPoint.Location)) / 4;
if (orientation == Orientation.Horizontal)
editPointOffset *= (previous.Location.X < this.activeEditPoint.Location.X) ? 1 : -1;
else
editPointOffset *= (previous.Location.Y < this.activeEditPoint.Location.X) ? 1 : -1;
activeEditPointIndex = this.editPoints.IndexOf(this.activeEditPoint);
Point editPointLocation = (orientation == Orientation.Horizontal) ? new Point(previous.Location.X + editPointOffset, previous.Location.Y) : new Point(previous.Location.X, previous.Location.Y + editPointOffset);
previous = new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation);
this.editPoints.InsertRange(activeEditPointIndex, new EditPoint[] { new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation), previous });
}
if (next != null && next.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
{
float slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(this.activeEditPoint.Location, next.Location);
Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical;
int editPointOffset = Convert.ToInt32(DesignerGeometryHelper.DistanceBetweenPoints((previous != null) ? previous.Location : this.activeEditPoint.Location, next.Location)) / 4;
if (orientation == Orientation.Horizontal)
editPointOffset *= (this.activeEditPoint.Location.X < next.Location.X) ? -1 : 1;
else
editPointOffset *= (this.activeEditPoint.Location.Y < next.Location.Y) ? -1 : 1;
activeEditPointIndex = this.editPoints.IndexOf(this.activeEditPoint);
Point editPointLocation = (orientation == Orientation.Horizontal) ? new Point(next.Location.X + editPointOffset, next.Location.Y) : new Point(next.Location.X, next.Location.Y + editPointOffset);
next = new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation);
this.editPoints.InsertRange(activeEditPointIndex + 1, new EditPoint[] { next, new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation) });
}
//STEP2: UPDATE THE EDIT POINTS
if (this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
{
this.activeEditPoint.Location = newPoint;
//When we start editing the end point we need to clear the slate and start over
RemoveEditPoints(EditPoint.EditPointTypes.MultiSegmentEditPoint);
object source = null, target = null;
if (this.activeEditPoint.EditedConnectionPoint.Equals(Target))
{
target = newPoint;
source = Source;
}
else
{
source = newPoint;
target = Target;
}
int newEditPointIndex = (this.editPoints.Count == 2) ? 1 : 0;
List newEditPoints = new List();
Point[] points = ActivityDesignerConnectorRouter.Route(this.serviceProvider, source, target, this.editedConnector.ExcludedRoutingRectangles);
for (int i = newEditPointIndex; i < points.Length - newEditPointIndex; i++ )
newEditPoints.Add(new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, points[i]));
this.editPoints.InsertRange(1, newEditPoints.ToArray());
}
else if (this.activeEditPoint.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint)
{
if (previous != null && previous.Type != EditPoint.EditPointTypes.ConnectionEditPoint && next != null && next.Type != EditPoint.EditPointTypes.ConnectionEditPoint)
{
//Update the previous point
float slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, this.activeEditPoint.Location);
Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical;
previous.Location = (orientation == Orientation.Horizontal) ? new Point(previous.Location.X, newPoint.Y) : new Point(newPoint.X, previous.Location.Y);
//Update the next point
slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(this.activeEditPoint.Location, next.Location);
orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical;
next.Location = (orientation == Orientation.Horizontal) ? new Point(next.Location.X, newPoint.Y) : new Point(newPoint.X, next.Location.Y);
//Update the current point
this.activeEditPoint.Location = newPoint;
}
else
{
Debug.Assert(false);
}
}
else if (this.activeEditPoint.Type == EditPoint.EditPointTypes.MidSegmentEditPoint)
{
if (previous != null && previous.Type != EditPoint.EditPointTypes.ConnectionEditPoint && next != null && next.Type != EditPoint.EditPointTypes.ConnectionEditPoint)
{
float slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, next.Location);
Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical;
//If the orientation is horizontal then we need to move the points vertically else we need to move the points horizontally
if (orientation == Orientation.Horizontal)
{
previous.Location = new Point(previous.Location.X, newPoint.Y);
next.Location = new Point(next.Location.X, newPoint.Y);
this.activeEditPoint.Location = new Point(this.activeEditPoint.Location.X, newPoint.Y);
}
else
{
previous.Location = new Point(newPoint.X, previous.Location.Y);
next.Location = new Point(newPoint.X, next.Location.Y);
this.activeEditPoint.Location = new Point(newPoint.X, this.activeEditPoint.Location.Y);
}
}
else
{
Debug.Assert(false);
}
}
}
//STEP3: Remove all the redundant edit points
RemoveCoincidingEditPoints();
//STEP4: Add back the segment mid points
AddEditPoints(EditPoint.EditPointTypes.MidSegmentEditPoint);
bool validEditPoints = ValidateEditPoints();
Debug.Assert(validEditPoints);
}
//Add edit points of specified type
private void AddEditPoints(EditPoint.EditPointTypes editPointType)
{
if (editPointType == EditPoint.EditPointTypes.ConnectionEditPoint)
{
if (this.editPoints.Count == 0 || !this.editPoints[0].EditedConnectionPoint.Equals(Source))
this.editPoints.Insert(0, new EditPoint(this, Source));
if (this.editPoints.Count < 2 || !this.editPoints[this.editPoints.Count - 1].EditedConnectionPoint.Equals(Target))
editPoints.Add(new EditPoint(this, Target));
}
else if (editPointType == EditPoint.EditPointTypes.MidSegmentEditPoint)
{
int minLengthForSegmentEditPoint = Source.Bounds.Width * 4;
for (int i = 0; i < this.editPoints.Count - 1; i++)
{
if (this.editPoints[i].Type != EditPoint.EditPointTypes.MidSegmentEditPoint &&
this.editPoints[i + 1].Type != EditPoint.EditPointTypes.MidSegmentEditPoint &&
DesignerGeometryHelper.DistanceOfLineSegments(new Point[] { this.editPoints[i].Location, this.editPoints[i + 1].Location }) > minLengthForSegmentEditPoint)
{
Point midPoint = DesignerGeometryHelper.MidPointOfLineSegment(this.editPoints[i].Location, this.editPoints[i + 1].Location);
this.editPoints.Insert(i + 1, new EditPoint(this, EditPoint.EditPointTypes.MidSegmentEditPoint, midPoint));
}
}
}
else if (editPointType == EditPoint.EditPointTypes.MultiSegmentEditPoint)
{
if (this.editPoints.Count == 2)
{
List segments = new List(this.editedConnector.ConnectorSegments);
if (segments.Count > 0 && segments[0] == Source.Location)
segments.RemoveAt(0);
if (segments.Count > 0 && segments[segments.Count - 1] == Target.Location)
segments.RemoveAt(segments.Count - 1);
List editPointsToAdd = new List();
for (int i = 0; i < segments.Count; i++)
editPointsToAdd.Add(new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, segments[i]));
this.editPoints.InsertRange(this.editPoints.Count - 1, editPointsToAdd.ToArray());
}
else
{
Debug.Assert(false);
}
}
}
//Remove edit points of specified type
private void RemoveEditPoints(EditPoint.EditPointTypes editPointType)
{
List editPointsToRemove = new List();
for (int i = 0; i < this.editPoints.Count; i++)
{
EditPoint editPoint = this.editPoints[i];
if (editPoint.Type == editPointType)
editPointsToRemove.Add(editPoint);
}
for (int i = 0; i < editPointsToRemove.Count; i++)
{
EditPoint editPoint = editPointsToRemove[i];
if (editPoint != this.activeEditPoint)
this.editPoints.Remove(editPoint);
}
}
//Remove points with same slope
private void RemoveCoincidingEditPoints()
{
if (this.editPoints.Count < 2 ||
this.editPoints[0].Type != EditPoint.EditPointTypes.ConnectionEditPoint ||
this.editPoints[this.editPoints.Count - 1].Type != EditPoint.EditPointTypes.ConnectionEditPoint ||
(this.activeEditPoint != null && this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint))
{
return;
}
//Just make sure that there are no mid segmment edit points or algorithm will fail
RemoveEditPoints(EditPoint.EditPointTypes.MidSegmentEditPoint);
//Create list of points to retain
List editPointsToRetain = new List();
for (int i = 0; i < this.editPoints.Count; i++)
{
if (this.editPoints[i].Type != EditPoint.EditPointTypes.MultiSegmentEditPoint ||
this.editPoints[i] == this.activeEditPoint ||
(i > 0 && this.editPoints[i - 1].Type == EditPoint.EditPointTypes.MidSegmentEditPoint) ||
(i < this.editPoints.Count - 1 && this.editPoints[i + 1].Type == EditPoint.EditPointTypes.MidSegmentEditPoint))
{
editPointsToRetain.Add(this.editPoints[i]);
}
}
//Step1: Get rid of all the line segments which are within tolerance range
for (int i = 1; i < this.editPoints.Count - 1; i++)
{
EditPoint previous = this.editPoints[i - 1];
EditPoint current = this.editPoints[i];
EditPoint next = this.editPoints[i + 1];
if (!editPointsToRetain.Contains(current))
{
double distance = DesignerGeometryHelper.DistanceOfLineSegments(new Point[] { previous.Location, current.Location });
if ((distance < current.Bounds.Width || distance < current.Bounds.Height) && next.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint)
{
float slope = DesignerGeometryHelper.SlopeOfLineSegment(current.Location, next.Location);
next.Location = (slope < 1) ? new Point(next.Location.X, previous.Location.Y) : new Point(previous.Location.X, next.Location.Y);
this.editPoints.Remove(current);
i -= 1;
}
else
{
distance = DesignerGeometryHelper.DistanceOfLineSegments(new Point[] { current.Location, next.Location });
if ((distance < current.Bounds.Width || distance < current.Bounds.Height) && previous.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint)
{
float slope = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, current.Location);
previous.Location = (slope < 1) ? new Point(previous.Location.X, next.Location.Y) : new Point(next.Location.X, previous.Location.Y);
this.editPoints.Remove(current);
i -= 1;
}
}
}
}
//Step2: We should make sure that the active edit point is always retained but those points which are coincidental are always removed
for (int i = 1; i < this.editPoints.Count - 1; i++)
{
EditPoint current = this.editPoints[i];
EditPoint previous = this.editPoints[i - 1];
EditPoint next = this.editPoints[i + 1];
if (!editPointsToRetain.Contains(current))
{
float slope1 = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, current.Location);
float slope2 = DesignerGeometryHelper.SlopeOfLineSegment(current.Location, next.Location);
if (Math.Abs(slope1) == Math.Abs(slope2))
{
this.editPoints.Remove(current);
i -= 1;
}
}
}
//Step3: Go thorugh each segment and ensure that there all the segments are either vertical or horizontal
for (int i = 0; i < this.editPoints.Count - 1; i++)
{
EditPoint current = this.editPoints[i];
EditPoint next = this.editPoints[i + 1];
float slope = DesignerGeometryHelper.SlopeOfLineSegment(current.Location, next.Location);
if (slope != 0 && slope != float.MaxValue)
{
Point location = (slope < 1) ? new Point(next.Location.X, current.Location.Y) : new Point(current.Location.X, next.Location.Y);
this.editPoints.Insert(i + 1, new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, location));
}
}
}
private bool ValidateEditPoints()
{
if (this.editPoints.Count < 2)
return false;
ConnectionPoint sourceConnection = this.editPoints[0].EditedConnectionPoint;
if (sourceConnection == null || !sourceConnection.Equals(Source))
return false;
ConnectionPoint targetConnection = this.editPoints[this.editPoints.Count - 1].EditedConnectionPoint;
if (targetConnection == null || !targetConnection.Equals(Target))
return false;
for (int i = 0; i < this.editPoints.Count - 1; i++)
{
if (this.editPoints[i].Type == EditPoint.EditPointTypes.MidSegmentEditPoint &&
this.editPoints[i + 1].Type == EditPoint.EditPointTypes.MidSegmentEditPoint)
return false;
}
return true;
}
#endregion
private List GetPointsFromEditPoints(List editPoints)
{
List segments = new List();
for (int i = 0; i < editPoints.Count; i++)
{
EditPoint editPoint = editPoints[i];
if (editPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint ||
editPoint.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint)
segments.Add(editPoint.Location);
}
return segments;
}
#endregion
#region Class EditPoint
private sealed class EditPoint
{
public enum EditPointTypes { ConnectionEditPoint = 1, MultiSegmentEditPoint, MidSegmentEditPoint };
private ConnectorEditor owner;
private EditPointTypes editPointType;
private Point point;
private ConnectionPoint connectionPoint;
public EditPoint(ConnectorEditor owner, EditPointTypes editPointType, Point point)
{
this.owner = owner;
this.editPointType = editPointType;
this.point = point;
}
public EditPoint(ConnectorEditor owner, ConnectionPoint connectionPoint)
{
this.owner = owner;
this.editPointType = EditPointTypes.ConnectionEditPoint;
this.connectionPoint = connectionPoint;
this.point = connectionPoint.Location;
}
public EditPointTypes Type
{
get
{
return this.editPointType;
}
}
public Point Location
{
get
{
return this.point;
}
set
{
this.point = value;
}
}
public Rectangle Bounds
{
get
{
Size controlPointSize = this.owner.Source.Bounds.Size;
return new Rectangle(this.point.X - controlPointSize.Width / 2, this.point.Y - controlPointSize.Height / 2, controlPointSize.Width, controlPointSize.Height);
}
}
public ConnectionPoint EditedConnectionPoint
{
get
{
return this.connectionPoint;
}
}
}
#endregion
}
#endregion
#region Class ActivityDesignerConnectorRouter
internal static class ActivityDesignerConnectorRouter
{
public static Point[] Route(IServiceProvider serviceProvider, object source, object target, ICollection userDefinedRoutingObstacles)
{
//Now call routing algorithm
List rectanglesToExclude;
List linesToExclude, pointsToExclude;
ActivityDesignerConnectorRouter.GetRoutingObstacles(serviceProvider, source, target, out rectanglesToExclude, out linesToExclude, out pointsToExclude);
if (userDefinedRoutingObstacles != null)
rectanglesToExclude.AddRange(userDefinedRoutingObstacles);
ActivityDesigner rootDesigner = ActivityDesigner.GetSafeRootDesigner(serviceProvider);
AmbientTheme ambientTheme = WorkflowTheme.CurrentTheme.AmbientTheme;
Point sourcePoint = (source is ConnectionPoint) ? ((ConnectionPoint)source).Location : (Point)source;
Point targetPoint = (target is ConnectionPoint) ? ((ConnectionPoint)target).Location : (Point)target;
Point[] routedPoints = ConnectorRouter.Route(sourcePoint, targetPoint, new Size(2 * ambientTheme.Margin.Width, 2 * ambientTheme.Margin.Height), rootDesigner.Bounds, rectanglesToExclude.ToArray(), linesToExclude.ToArray(), pointsToExclude.ToArray());
//
if (!AreAllSegmentsVerticalOrHorizontal(routedPoints))
routedPoints = ConnectorRouter.Route(sourcePoint, targetPoint, ambientTheme.Margin, rootDesigner.Bounds, new Rectangle[] { }, linesToExclude.ToArray(), new Point[] { });
//FALLBACK1
if (!AreAllSegmentsVerticalOrHorizontal(routedPoints))
{
float slope = DesignerGeometryHelper.SlopeOfLineSegment(sourcePoint, targetPoint);
Point intermediatePoint = (slope < 1) ? new Point(targetPoint.X, sourcePoint.Y) : new Point(sourcePoint.X, targetPoint.Y);
routedPoints = new Point[] { sourcePoint, intermediatePoint, targetPoint };
}
return routedPoints;
}
public static void GetRoutingObstacles(IServiceProvider serviceProvider, object source, object target, out List rectanglesToExclude, out List linesToExclude, out List pointsToExclude)
{
//Source or Targets can be either ConnectionPoint or a Point
AmbientTheme ambientTheme = WorkflowTheme.CurrentTheme.AmbientTheme;
ActivityDesigner rootDesigner = ActivityDesigner.GetSafeRootDesigner(serviceProvider);
ConnectionPoint sourceConnectionPoint = source as ConnectionPoint;
Point sourcePoint = (sourceConnectionPoint != null) ? sourceConnectionPoint.Location : (Point)source;
ActivityDesigner sourceDesigner = (sourceConnectionPoint != null) ? sourceConnectionPoint.AssociatedDesigner : rootDesigner.HitTest(sourcePoint).AssociatedDesigner;
ConnectionPoint targetConnectionPoint = target as ConnectionPoint;
Point targetPoint = (targetConnectionPoint != null) ? targetConnectionPoint.Location : (Point)target;
ActivityDesigner targetDesigner = (targetConnectionPoint != null) ? targetConnectionPoint.AssociatedDesigner : rootDesigner.HitTest(targetPoint).AssociatedDesigner;
//Collect the common parent chain of source and target
Dictionary parentDesignerDictionary = new Dictionary();
if (sourceDesigner != null)
{
//Collect designers in the source parent chain
CompositeActivityDesigner parentDesigner = sourceDesigner.ParentDesigner;
while (parentDesigner != null)
{
if (!parentDesignerDictionary.ContainsKey(parentDesigner.GetHashCode()))
parentDesignerDictionary.Add(parentDesigner.GetHashCode(), parentDesigner);
else
break;
parentDesigner = parentDesigner.ParentDesigner;
}
}
if (targetDesigner != null)
{
//Collect designer from target chain which are not in source chain
CompositeActivityDesigner parentDesigner = targetDesigner.ParentDesigner;
while (parentDesigner != null)
{
if (!parentDesignerDictionary.ContainsKey(parentDesigner.GetHashCode()))
parentDesignerDictionary.Add(parentDesigner.GetHashCode(), parentDesigner);
else
break;
parentDesigner = parentDesigner.ParentDesigner;
}
}
//Now go through the dictionary and add all the children that are not in the chain
rectanglesToExclude = new List();
pointsToExclude = new List();
foreach (CompositeActivityDesigner parentDesigner in parentDesignerDictionary.Values)
{
ReadOnlyCollection containedDesigners = parentDesigner.ContainedDesigners;
for (int j = 0; j < containedDesigners.Count; j++)
{
ActivityDesigner activityDesigner = containedDesigners[j];
if (activityDesigner.IsVisible &&
!parentDesignerDictionary.ContainsKey(activityDesigner.GetHashCode()) &&
activityDesigner != sourceDesigner &&
activityDesigner != targetDesigner)
{
Rectangle rectangleToExclude = activityDesigner.Bounds;
rectangleToExclude.Inflate(ambientTheme.Margin);
rectanglesToExclude.Add(rectangleToExclude);
}
}
//
}
//Now get the exclusion paths for source designer and target designer
linesToExclude = new List();
if (sourceDesigner != null && sourceDesigner == targetDesigner && !sourceDesigner.IsRootDesigner)
{
linesToExclude.AddRange(GetDesignerEscapeCover(sourceDesigner, new object[] { source, target }));
}
else
{
if (sourceDesigner != null && !sourceDesigner.IsRootDesigner)
linesToExclude.AddRange(GetDesignerEscapeCover(sourceDesigner, new object[] { source }));
if (targetDesigner != null && !targetDesigner.IsRootDesigner)
{
bool needToEscapeTargetDesigner = true;
CompositeActivityDesigner sourceParentDesigner = (sourceDesigner != null) ? sourceDesigner.ParentDesigner : null;
while (sourceParentDesigner != null)
{
if (targetDesigner == sourceParentDesigner)
{
needToEscapeTargetDesigner = false;
break;
}
sourceParentDesigner = (sourceDesigner != null) ? sourceParentDesigner.ParentDesigner : null;
}
//need to escape the target designer only if the source is not inside the parent
if(needToEscapeTargetDesigner)
linesToExclude.AddRange(GetDesignerEscapeCover(targetDesigner, new object[] { target }));
}
}
}
private static IList GetDesignerEscapeCover(ActivityDesigner designer, ICollection