1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
5 namespace System.Activities.Core.Presentation
7 using System.Activities;
8 using System.Activities.Presentation;
9 using System.Activities.Presentation.FreeFormEditing;
10 using System.Activities.Presentation.Hosting;
11 using System.Activities.Presentation.Internal.PropertyEditing;
12 using System.Activities.Presentation.Metadata;
13 using System.Activities.Presentation.Model;
14 using System.Activities.Presentation.View;
15 using System.Activities.Presentation.View.OutlineView;
16 using System.Activities.Presentation.ViewState;
17 using System.Activities.Statements;
18 using System.Collections.Generic;
19 using System.Collections.ObjectModel;
20 using System.ComponentModel;
22 using System.Reflection;
25 using System.Windows.Controls;
26 using System.Windows.Data;
27 using System.Windows.Documents;
28 using System.Windows.Input;
29 using System.Windows.Media;
30 using System.Windows.Threading;
32 [ActivityDesignerOptions(AlwaysCollapseChildren = true)]
33 partial class FlowchartDesigner : IAutoConnectContainer, IAutoSplitContainer
35 public static readonly DependencyProperty ConnectionPointsProperty = DependencyProperty.RegisterAttached("ConnectionPoints", typeof(List<ConnectionPoint>), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
36 public static readonly DependencyProperty LinkModelItemProperty = DependencyProperty.RegisterAttached("LinkModelItem", typeof(ModelItem), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
37 public static readonly DependencyProperty FlowElementModelItemProperty = DependencyProperty.RegisterAttached("FlowElementModelItem", typeof(ModelItem), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
39 public static readonly DependencyProperty FlowchartWidthProperty = DependencyProperty.Register("FlowchartWidth", typeof(double), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
40 public static readonly DependencyProperty FlowchartHeightProperty = DependencyProperty.Register("FlowchartHeight", typeof(double), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
42 public static readonly DependencyProperty TrueConnectionPointProperty = DependencyProperty.RegisterAttached("TrueConnectionPoint", typeof(ConnectionPoint), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
43 public static readonly DependencyProperty FalseConnectionPointProperty = DependencyProperty.RegisterAttached("FalseConnectionPoint", typeof(ConnectionPoint), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
45 public static readonly DependencyProperty ShowAllConditionsProperty = DependencyProperty.Register("ShowAllConditions", typeof(bool), typeof(FlowchartDesigner));
47 public static readonly RoutedCommand SetAsStartNodeCommand = new RoutedCommand("SetAsStartNode", typeof(FlowchartDesigner));
48 //public static readonly RoutedCommand ConnectNodesCommand = new RoutedCommand("ConnectNodes", typeof(FlowchartDesigner));
49 public static readonly RoutedCommand ShowAllConditionsCommand = new RoutedCommand("ShowAllConditionsCommand", typeof(FlowchartDesigner));
50 public static readonly RoutedCommand HideAllConditionsCommand = new RoutedCommand("HideAllConditionsCommand", typeof(FlowchartDesigner));
52 const double flowElementCaptionFontSize = 11;
53 const double DebugTimeMaxConnectorShapeDist = 10;
54 static readonly FontFamily flowElementCaptionFontFamily = new FontFamily("Tohoma");
55 static readonly FontStyle flowElementCaptionFontStyle = new FontStyle();
56 static readonly Typeface flowElementCaptionTypeface = new Typeface("Tohoma");
58 internal Dictionary<ModelItem, UIElement> modelElement;
59 //Consider FlowStep.Action = SomeActivity. FlowStep modelItem is referred as FlowNodeMI, SomeActivity modelItem is shapeMI and the designer for SomeActivity is the shape on canvas.
60 //To go from the FlowNodeMI to the shape on canvas, we can use the path: FlowNodeMI(FlowStep.Action)-> shapeMI (modelElement Dictionary)-> Actual UIElement shape
61 //However this path does not always work. For instance in delete case: FlowStep.Action is set to null to update the ModelItem.Parents property on the shapeMI
62 //flowNodeToUIElement dictionary is used to solve this problem.
63 Dictionary<ModelItem, UIElement> flowNodeToUIElement;
65 const double startSymbolTopMargin = 10.0;
66 const string shapeLocation = "ShapeLocation";
67 const string shapeSize = "ShapeSize";
68 const string TrueConnectorViewStateKey = "TrueConnector";
69 const string FalseConnectorViewStateKey = "FalseConnector";
70 const string CaseViewStateKeyAppendString = "Connector";
71 const string FlowSwitchDefaultViewStateKey = "Default";
72 const string ConnectorViewStateKey = "ConnectorLocation";
73 static Color ConnectionPointColor = Colors.LightGray;
74 UIElement lastConnectionPointMouseUpElement = null;
75 //shapeLocations is useful to avoid pasting on existing shapes.
76 //This is populated in 2 cases 1. When the shape with existing Viewstate is added 2. On ViewState changed.
77 HashSet<Point> shapeLocations = null;
78 //selectedConnector is a placeholder for the last connector selected.
79 //This removes the need for a dictionary mapping modelitem to connector for deletion.
80 //This will change if in future we plan to support multi-select + delete.
81 Connector selectedConnector;
82 //srcConnectionPoint is required for link addition gesture to store the source of the link.
83 ConnectionPoint srcConnectionPoint;
84 ConnectionPoint srcConnectionPointForAutoConnect;
85 ConnectionPoint srcConnectionPointForAutoSplit;
86 ConnectionPoint destConnectionPointForAutoSplit;
87 EdgeLocation entryEdgeForAutoSplit;
88 EdgeLocation exitEdgeForAutoSplit;
89 bool internalViewStateChange = false;
90 bool startNodeAdded = false;
91 bool updatingSelectedConnector;
92 internal FreeFormPanel panel = null;
93 AdornerLayer adornerLayer;
94 MenuItem setAsStartNode;
96 private bool isLoaded = false;
98 internal bool IsResizing { get; set; }
100 public FlowchartDesigner()
102 InitializeComponent();
103 this.modelElement = new Dictionary<ModelItem, UIElement>();
104 this.flowNodeToUIElement = new Dictionary<ModelItem, UIElement>();
105 this.shapeLocations = new HashSet<Point>();
106 this.selectedConnector = null;
107 ConstructSetAsStartNodeMenuItem();
109 this.Loaded += (s, e) =>
111 this.isLoaded = true;
113 if (this.ShowExpanded)
115 ((ICompositeViewEvents)this).RegisterDefaultCompositeView(this);
117 DesignerView designerView = this.Context.Services.GetService<DesignerView>() as DesignerView;
118 if (!designerView.ContextMenu.Items.Contains(setAsStartNode))
120 designerView.ContextMenu.Items.Add(setAsStartNode);
123 WorkflowCommandExtensionItem item = this.Context.Items.GetValue<WorkflowCommandExtensionItem>();
126 if (item.CommandExtensionCallback is DefaultCommandExtensionCallback)
128 this.InputBindings.Add(new KeyBinding(FlowchartDesignerCommands.ConnectNodesCommand, new DefaultCommandExtensionCallback.ChordKeyGesture(Key.E, Key.F)));
132 Selection.Subscribe(Context, OnSelectionChanged);
135 this.Unloaded += (s, e) =>
137 this.isLoaded = false;
139 if (object.Equals(this.DefaultCompositeView, this))
141 ((ICompositeViewEvents)this).UnregisterDefaultCompositeView(this);
143 DesignerView designerView = this.Context.Services.GetService<DesignerView>() as DesignerView;
144 designerView.ContextMenu.Items.Remove(setAsStartNode);
146 Selection.Unsubscribe(Context, OnSelectionChanged);
150 public static double FlowNodeCaptionFontSize
152 get { return flowElementCaptionFontSize; }
155 public static FontFamily FlowNodeCaptionFontFamily
157 get { return flowElementCaptionFontFamily; }
160 public static FontStyle FlowNodeCaptionFontStyle
162 get { return flowElementCaptionFontStyle; }
165 public static Typeface FlowElementCaptionTypeface
167 get { return flowElementCaptionTypeface; }
170 void OnSetAsStartNodeCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
172 //The condition is necessary so that the child flowchart inside a flowchart doesn't try to handle the event.
173 if (!object.Equals(e.Source, this))
175 e.CanExecute = !this.IsReadOnly;
180 void OnShowAllConditionsMenuLoaded(object sender, RoutedEventArgs e)
182 MenuItem item = sender as MenuItem;
185 bool expanded = (bool)GetValue(WorkflowViewElement.ShowExpandedProperty);
188 item.Visibility = Visibility.Visible;
192 item.Visibility = Visibility.Collapsed;
198 void OnHideAllConditionsMenuLoaded(object sender, RoutedEventArgs e)
200 MenuItem item = sender as MenuItem;
203 bool expanded = (bool)GetValue(WorkflowViewElement.ShowExpandedProperty);
206 item.Visibility = Visibility.Visible;
210 item.Visibility = Visibility.Collapsed;
216 void OnSetAsStartNodeCommandExecuted(object sender, ExecutedRoutedEventArgs e)
218 ModelItem selection = this.Context.Items.GetValue<Selection>().PrimarySelection;
219 Fx.Assert(this.modelElement.ContainsKey(selection), "Selection is not contained in this container");
220 this.ModelItem.Properties["StartNode"].SetValue(this.GetFlowElementMI(selection));
224 void ConstructSetAsStartNodeMenuItem()
226 setAsStartNode = new MenuItem();
227 setAsStartNode.Command = FlowchartDesigner.SetAsStartNodeCommand;
228 setAsStartNode.Header = this.SetAsStartNodeMenuItemHeader;
229 setAsStartNode.Visibility = Visibility.Collapsed;
230 setAsStartNode.Loaded += new RoutedEventHandler(OnSetAsStartNodeLoaded);
231 //AutomationProperties
232 setAsStartNode.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, "SetAsStartNodeMenuItem");
235 string SetAsStartNodeMenuItemHeader
237 get { return (string)this.FindResource("SetAsStartNodeMenuItemHeader"); }
240 void OnSetAsStartNodeLoaded(object sender, RoutedEventArgs e)
242 MenuItem setAsStartNodeMenuItem = sender as MenuItem;
243 setAsStartNodeMenuItem.Visibility = Visibility.Collapsed;
244 Selection selection = this.Context.Items.GetValue<Selection>();
245 if (selection.SelectionCount == 1 && this.modelElement.ContainsKey(selection.PrimarySelection))
247 setAsStartNodeMenuItem.Visibility = Visibility.Visible;
252 public static void RegisterMetadata(AttributeTableBuilder builder)
254 Type type = typeof(Flowchart);
255 builder.AddCustomAttributes(type, new DesignerAttribute(typeof(FlowchartDesigner)));
256 builder.AddCustomAttributes(type, type.GetProperty("StartNode"), BrowsableAttribute.No);
257 builder.AddCustomAttributes(type, type.GetProperty("Nodes"), BrowsableAttribute.No);
258 builder.AddCustomAttributes(type, type.GetProperty("Variables"), BrowsableAttribute.No);
259 builder.AddCustomAttributes(type, new FeatureAttribute(typeof(FlowchartSizeFeature)));
261 PropertyInfo nodesProperty = type.GetProperty("Nodes");
262 builder.AddCustomAttributes(type, nodesProperty, new ShowPropertyInOutlineViewAttribute());
264 type = typeof(FlowStep);
265 builder.AddCustomAttributes(type, type.GetProperty("Action"), BrowsableAttribute.No);
266 builder.AddCustomAttributes(type, type.GetProperty("Next"), BrowsableAttribute.No);
268 builder.AddCustomAttributes(type, new ShowInOutlineViewAttribute() { PromotedProperty = "Action" });
269 builder.AddCustomAttributes(type, type.GetProperty("Next"), new ShowPropertyInOutlineViewAsSiblingAttribute());
271 builder.AddCustomAttributes(typeof(FlowNode), new ShowInOutlineViewAttribute());
272 builder.AddCustomAttributes(typeof(Collection<FlowNode>), new ShowInOutlineViewAttribute());
274 CutCopyPasteHelper.AddDisallowedTypeForCopy(typeof(StartNode));
277 //Unregister all events. Reset startNodeAdded to enable reuse of the designer.
278 void CleanupFlowchart()
280 this.startNodeAdded = false;
281 this.panel.Children.Clear();
282 this.flowNodeToUIElement.Clear();
283 // Cleaning up the designers as they might be re-used.
284 foreach (UIElement element in this.modelElement.Values)
286 element.MouseEnter -= new MouseEventHandler(ChildElement_MouseEnter);
287 element.MouseLeave -= new MouseEventHandler(ChildElement_MouseLeave);
289 this.panel.LocationChanged -= new LocationChangedEventHandler(OnFreeFormPanelLocationChanged);
290 this.panel.ConnectorMoved -= new ConnectorMovedEventHandler(OnFreeFormPanelConnectorMoved);
291 this.panel.LayoutUpdated -= new EventHandler(OnFreeFormPanelLayoutUpdated);
292 this.panel.RequiredSizeChanged -= new RequiredSizeChangedEventHandler(OnFreeFormPanelRequiredSizeChanged);
294 ModelTreeManager modelTreeManager = (this.ModelItem as IModelTreeItem).ModelTreeManager;
295 modelTreeManager.EditingScopeCompleted -= new EventHandler<EditingScopeEventArgs>(ModelTreeManager_EditingScopeCompleted);
296 this.ViewStateService.ViewStateChanged -= new ViewStateChangedEventHandler(OnViewStateChanged);
299 void OnFreeFormPanelLoaded(object sender, RoutedEventArgs eventArgs)
301 //Adding the following check because of 137896: Inside tab control multiple Loaded events happen without an Unloaded event.
302 if (this.panel != null)
306 this.panel = (FreeFormPanel)sender;
307 if (this.ShowExpanded)
309 PopulateFlowchartChildren();
313 void OnFreeFormPanelUnLoaded(object sender, RoutedEventArgs eventArgs)
315 if (object.Equals(sender, this.panel))
321 void PopulateFlowchartChildren()
323 Fx.Assert(this.ShowExpanded, "This method should be called only when the flowchart designer is shown expanded.");
324 Fx.Assert(this.panel != null, "panel cannot be null");
325 this.panel.LocationChanged += new LocationChangedEventHandler(OnFreeFormPanelLocationChanged);
326 this.panel.ConnectorMoved += new ConnectorMovedEventHandler(OnFreeFormPanelConnectorMoved);
327 this.panel.LayoutUpdated += new EventHandler(OnFreeFormPanelLayoutUpdated);
328 this.panel.RequiredSizeChanged += new RequiredSizeChangedEventHandler(OnFreeFormPanelRequiredSizeChanged);
330 DesignerPerfEventProvider perfEventProvider = this.Context.Services.GetService<DesignerPerfEventProvider>();
331 perfEventProvider.FlowchartDesignerLoadStart();
332 ModelTreeManager modelTreeManager = (this.ModelItem as IModelTreeItem).ModelTreeManager;
333 modelTreeManager.EditingScopeCompleted += new EventHandler<EditingScopeEventArgs>(ModelTreeManager_EditingScopeCompleted);
334 this.ViewStateService.ViewStateChanged += new ViewStateChangedEventHandler(OnViewStateChanged);
336 this.startNodeAdded = false;
337 panel.Children.Clear();
338 this.modelElement.Clear();
339 this.flowNodeToUIElement.Clear();
340 this.shapeLocations.Clear();
342 this.FlowchartWidth = (double)TypeDescriptor.GetProperties(this.ModelItem)[FlowchartSizeFeature.WidthPropertyName].GetValue(this.ModelItem);
343 this.FlowchartHeight = (double)TypeDescriptor.GetProperties(this.ModelItem)[FlowchartSizeFeature.HeightPropertyName].GetValue(this.ModelItem);
346 AddFlowElementsToDesigner(this.ModelItem.Properties["Nodes"].Collection, true);
347 perfEventProvider.FlowchartDesignerLoadEnd();
350 //This is to keep this.selectedConnector upto date.
351 //Eg. cases included 1. create a link, select it and undo, 2. Move a link from one shape to another.
352 void OnFreeFormPanelLayoutUpdated(object sender, EventArgs e)
354 if (!this.panel.Children.Contains(this.selectedConnector))
356 this.selectedConnector = null;
360 public UIElement StartSymbol { get; set; }
362 internal static List<ConnectionPoint> GetConnectionPoints(DependencyObject obj)
364 return (List<ConnectionPoint>)obj.GetValue(FlowchartDesigner.ConnectionPointsProperty);
367 internal static ConnectionPoint GetFalseConnectionPoint(DependencyObject obj)
369 return (ConnectionPoint)obj.GetValue(FlowchartDesigner.FalseConnectionPointProperty);
372 internal static ModelItem GetLinkModelItem(DependencyObject obj)
374 return (ModelItem)obj.GetValue(FlowchartDesigner.LinkModelItemProperty);
377 internal static ModelItem GetFlowElementModelItem(DependencyObject obj)
379 return (ModelItem)obj.GetValue(FlowchartDesigner.FlowElementModelItemProperty);
382 internal static ConnectionPoint GetTrueConnectionPoint(DependencyObject obj)
384 return (ConnectionPoint)obj.GetValue(FlowchartDesigner.TrueConnectionPointProperty);
387 public double FlowchartWidth
389 get { return (double)this.GetValue(FlowchartDesigner.FlowchartWidthProperty); }
390 set { this.SetValue(FlowchartDesigner.FlowchartWidthProperty, value); }
393 public double FlowchartHeight
395 get { return (double)this.GetValue(FlowchartDesigner.FlowchartHeightProperty); }
396 set { this.SetValue(FlowchartDesigner.FlowchartHeightProperty, value); }
399 public bool ShowAllConditions
401 get { return (bool)GetValue(ShowAllConditionsProperty); }
402 set { SetValue(ShowAllConditionsProperty, value); }
405 ModelItem GetFlowElementMI(ModelItem shapeModelItem)
407 Fx.Assert(this.modelElement.ContainsKey(shapeModelItem), "The ModelItem does not exist.");
408 UIElement element = this.modelElement[shapeModelItem];
409 ModelItem flowElementMI = FlowchartDesigner.GetFlowElementModelItem(element);
410 Fx.Assert(flowElementMI != null, "FlowNode dependency property not set.");
411 return flowElementMI;
414 protected override void OnInitialized(EventArgs e)
416 base.OnInitialized(e);
419 //Returns actual link destination - Activity ModelItem in case of a FlowStep.
420 ModelItem GetCorrespondingElementOnCanvas(ModelItem model)
422 ModelItem destModelItem = model;
423 if (typeof(FlowStep).IsAssignableFrom(model.ItemType)
424 && model.Properties["Action"].Value != null)
426 destModelItem = model.Properties["Action"].Value;
428 if (typeof(Flowchart) == model.ItemType)
430 destModelItem = flowStart;
432 return destModelItem;
435 private bool IsRightToLeft
439 if (!this.isRightToLeft.HasValue)
441 this.isRightToLeft = FreeFormPanelUtilities.IsRightToLeft(this.flowchartContentPresenter);
444 return this.isRightToLeft.Value;
448 private void OnFlowchartGridMouseLeave(object sender, MouseEventArgs e)
450 bool endLinkCreation = !IsVisualHit(sender as UIElement, sender as UIElement, e.GetPosition(sender as IInputElement));
453 RemoveAdorner(this.panel, typeof(ConnectorCreationAdorner));
454 this.srcConnectionPoint = null;
458 private void OnFlowchartGridMouseMove(object sender, MouseEventArgs e)
460 if (this.srcConnectionPoint != null)
462 AutoScrollHelper.AutoScroll(e, this, 1);
463 Point[] points = ConnectorRouter.Route(this.panel, this.srcConnectionPoint, e);
469 List<Point> segments = new List<Point>(points);
470 //Remove the previous adorner.
471 RemoveAdorner(this.panel, typeof(ConnectorCreationAdorner));
473 AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this.srcConnectionPoint.ParentDesigner);
474 Fx.Assert(adornerLayer != null, "Adorner Layer does not exist");
475 ConnectorCreationAdorner newAdorner = new ConnectorCreationAdorner(this.panel, segments);
476 adornerLayer.Add(newAdorner);
482 private void OnFlowchartGridMouseUp(object sender, MouseButtonEventArgs e)
484 if (this.srcConnectionPoint != null)
486 RemoveAdorner(this.panel, typeof(ConnectorCreationAdorner));
487 this.srcConnectionPoint = null;
492 static void SetConnectionPoints(DependencyObject obj, List<ConnectionPoint> connectionPoints)
494 obj.SetValue(FlowchartDesigner.ConnectionPointsProperty, connectionPoints);
498 static void SetFalseConnectionPoint(DependencyObject obj, ConnectionPoint connectionPoint)
500 obj.SetValue(FlowchartDesigner.FalseConnectionPointProperty, connectionPoint);
503 static void SetLinkModelItem(DependencyObject obj, ModelItem modelItem)
505 obj.SetValue(FlowchartDesigner.LinkModelItemProperty, modelItem);
508 static void SetFlowElementModelItem(DependencyObject obj, ModelItem modelItem)
510 obj.SetValue(FlowchartDesigner.FlowElementModelItemProperty, modelItem);
513 static void SetTrueConnectionPoint(DependencyObject obj, ConnectionPoint connectionPoint)
515 obj.SetValue(FlowchartDesigner.TrueConnectionPointProperty, connectionPoint);
518 void ChildElement_MouseEnter(object sender, MouseEventArgs e)
520 Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
521 VirtualizedContainerService.VirtualizingContainer senderElement = sender as VirtualizedContainerService.VirtualizingContainer;
522 if ((senderElement != null || sender is StartSymbol) && !this.IsReadOnly)
524 AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer((Visual)sender);
525 Fx.Assert(adornerLayer != null, "Cannot get AdornerLayer.");
526 ConnectionPointsAdorner adorner = null;
527 if (sender is StartSymbol)
529 adorner = new FlowchartConnectionPointsAdorner((UIElement)sender, ConnectionPointsToShow((UIElement)sender, this.ModelItem), false, this.IsRightToLeft);
533 bool isSenderElementSelected = (((Selection)this.Context.Items.GetValue<Selection>()).SelectedObjects as ICollection<ModelItem>).Contains(senderElement.ModelItem);
534 adorner = new FlowchartConnectionPointsAdorner(senderElement, ConnectionPointsToShow(senderElement, senderElement.ModelItem), isSenderElementSelected, this.IsRightToLeft);
536 adornerLayer.Add(adorner);
537 adorner.MouseDown += new MouseButtonEventHandler(ConnectionPoint_MouseDown);
538 adorner.MouseUp += new MouseButtonEventHandler(ConnectionPoint_MouseUp);
539 adorner.MouseLeave += new MouseEventHandler(ConnectionPoint_MouseLeave);
544 //This method returns which connection points should be shown on hover of a shape.
545 List<ConnectionPoint> ConnectionPointsToShow(UIElement element, ModelItem model)
547 bool isInComingConnection = false;
549 //This condition checks if it is an incoming connection.
550 if (this.srcConnectionPoint != null || (this.panel.connectorEditor != null && this.panel.connectorEditor.IsConnectorEndBeingMoved))
552 isInComingConnection = true;
554 List<ConnectionPoint> connectionPointsToShow = new List<ConnectionPoint>();
556 if (GenericFlowSwitchHelper.IsGenericFlowSwitch(model.ItemType))
558 connectionPointsToShow.AddRange(FlowchartDesigner.GetConnectionPoints(element));
560 else if (typeof(FlowDecision).IsAssignableFrom(model.ItemType))
562 if (isInComingConnection)
564 connectionPointsToShow.AddRange(FlowchartDesigner.GetConnectionPoints(element));
568 connectionPointsToShow.Add(FlowchartDesigner.GetTrueConnectionPoint(element));
569 connectionPointsToShow.Add(FlowchartDesigner.GetFalseConnectionPoint(element));
570 List<Connector> outGoingConnectors = GetOutGoingConnectors(element);
571 if (this.panel.connectorEditor != null && this.panel.connectorEditor.IsConnectorStartBeingMoved)
573 //If the start of an outgoing connector is moved, its not an outgoing connector any more.
574 outGoingConnectors.Remove(this.panel.connectorEditor.Connector);
576 //Do not show True/False connection point if a link already exists.
577 foreach (Connector connector in outGoingConnectors)
579 connectionPointsToShow.Remove(FreeFormPanel.GetSourceConnectionPoint(connector));
583 else// Case where only one out going connector is allowed - Start and FlowStep.
585 ConnectionPointKind allowedType = ConnectionPointKind.Default;
586 bool isConnectionAllowed = false;
587 if (isInComingConnection)
589 allowedType = ConnectionPointKind.Incoming;
590 isConnectionAllowed = true;
594 List<Connector> outGoingConnectors = GetOutGoingConnectors(element);
595 if (this.panel.connectorEditor != null && this.panel.connectorEditor.IsConnectorStartBeingMoved)
597 outGoingConnectors.Remove(this.panel.connectorEditor.Connector);
599 //Outgoing Connection is allowed only if there are no outgoing connectors already.
600 if (outGoingConnectors.Count == 0)
602 allowedType = ConnectionPointKind.Outgoing;
603 isConnectionAllowed = true;
607 if (isConnectionAllowed)
609 foreach (ConnectionPoint connPoint in FlowchartDesigner.GetConnectionPoints(element))
611 if (connPoint.PointType == allowedType || connPoint.PointType == ConnectionPointKind.Default)
613 connectionPointsToShow.Add(connPoint);
618 //Do not show the connection points of a selected connector.
619 if (this.selectedConnector != null)
621 connectionPointsToShow.Remove(FreeFormPanel.GetSourceConnectionPoint(this.selectedConnector));
622 connectionPointsToShow.Remove(FreeFormPanel.GetDestinationConnectionPoint(this.selectedConnector));
624 return connectionPointsToShow;
628 void ChildElement_MouseLeave(object sender, MouseEventArgs e)
630 Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
631 bool removeConnectionPointsAdorner = true;
632 if (Mouse.DirectlyOver != null)
634 removeConnectionPointsAdorner = !typeof(ConnectionPointsAdorner).IsAssignableFrom(Mouse.DirectlyOver.GetType());
636 if (removeConnectionPointsAdorner)
638 RemoveAdorner(sender as UIElement, typeof(ConnectionPointsAdorner));
643 void ChildSizeChanged(object sender, SizeChangedEventArgs e)
645 Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
646 VirtualizedContainerService.VirtualizingContainer container = sender as VirtualizedContainerService.VirtualizingContainer;
647 if (container != null || sender is StartSymbol)
649 this.internalViewStateChange = true;
650 //Initializing storageModelItem for the case of FlowchartStartNode.
651 ModelItem storageModelItem = this.ModelItem;
652 if (container != null)
654 storageModelItem = GetFlowElementMI(container.ModelItem);
656 this.ViewStateService.StoreViewState(storageModelItem, shapeSize, ((UIElement)sender).DesiredSize);
657 this.internalViewStateChange = false;
661 void ConnectionPoint_MouseDown(object sender, MouseButtonEventArgs e)
663 UIElement srcElement = ((Adorner)sender).AdornedElement as UIElement;
664 this.srcConnectionPoint = ConnectionPointHitTest(srcElement, e.GetPosition(this.panel));
668 void ConnectionPoint_MouseLeave(object sender, MouseEventArgs e)
670 Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
671 UIElement adornedElement = ((Adorner)sender).AdornedElement as UIElement;
672 RemoveAdorner(adornedElement, typeof(ConnectionPointsAdorner));
676 void ConnectionPoint_MouseUp(object sender, MouseButtonEventArgs e)
678 Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
679 UIElement dest = ((Adorner)sender).AdornedElement as UIElement;
680 Fx.Assert(dest != null, "Adorned element is not a UIElement");
681 if (this.srcConnectionPoint != null)
683 ConnectionPoint destConnectionPoint = ConnectionPointHitTest(dest, e.GetPosition(this.panel));
684 if (destConnectionPoint != null && !this.srcConnectionPoint.Equals(destConnectionPoint))
686 string errorMessage = string.Empty;
687 if (!CreateLinkGesture(this.srcConnectionPoint, destConnectionPoint, out errorMessage, null) && !errorMessage.Equals(string.Empty))
689 ErrorReporting.ShowErrorMessage(errorMessage);
692 this.srcConnectionPoint = null;
693 RemoveAdorner(this.panel, typeof(ConnectorCreationAdorner));
694 RemoveAdorner(dest, typeof(FlowchartConnectionPointsAdorner));
698 //This will cause the FreeFormPanel to handle the event and is useful while moving connection end points of a connector.
699 lastConnectionPointMouseUpElement = dest;
704 void OnFreeFormPanelRequiredSizeChanged(object sender, RequiredSizeChangedEventArgs e)
706 Dispatcher.BeginInvoke(() =>
708 // Access the view state dictionary directly to avoid generating an undo item because of ViewStateAttachedPropertyFeature implementation.
709 Dictionary<string, object> viewState = WorkflowViewStateService.GetViewState(this.ModelItem.GetCurrentValue());
710 if (viewState == null)
712 viewState = new Dictionary<string, object>();
713 WorkflowViewStateService.SetViewState(this.ModelItem.GetCurrentValue(), viewState);
716 if (e.NewRequiredSize.Width > this.FlowchartWidth)
718 viewState[FlowchartSizeFeature.WidthPropertyName] = e.NewRequiredSize.Width;
719 this.FlowchartWidth = e.NewRequiredSize.Width;
722 if (e.NewRequiredSize.Height > this.FlowchartHeight)
724 viewState[FlowchartSizeFeature.HeightPropertyName] = e.NewRequiredSize.Height;
725 this.FlowchartHeight = e.NewRequiredSize.Height;
730 void OnFreeFormPanelLocationChanged(object sender, System.Activities.Presentation.FreeFormEditing.LocationChangedEventArgs e)
732 Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
733 Fx.Assert(sender is UIElement, "Sender should be of type UIElement");
734 Connector movedConnector = sender as Connector;
735 if (movedConnector != null)
737 //ViewState is undoable only when a user gesture moves a connector. If the freeformpanel routes a connector,
738 //the change is not undoable.
739 bool isUndoableViewState = false;
740 ModelItem linkModelItem = FlowchartDesigner.GetLinkModelItem(movedConnector);
741 ConnectionPoint source = FreeFormPanel.GetSourceConnectionPoint(movedConnector);
742 string viewStateKey = GetConnectorViewStateKey(linkModelItem, source);
743 ModelItem storageModelItem = GetConnectorViewStateStorageModelItem(linkModelItem);
744 PointCollection existingVS = this.ViewStateService.RetrieveViewState(storageModelItem, viewStateKey) as PointCollection;
745 if (existingVS != null && existingVS.Count > 0 && movedConnector.Points.Count > 0
746 && existingVS[0].Equals(movedConnector.Points[0]) && existingVS[existingVS.Count - 1].Equals(movedConnector.Points[movedConnector.Points.Count - 1]))
748 isUndoableViewState = true;
750 StoreConnectorViewState(movedConnector, isUndoableViewState);
754 //Save the location property of each shape on the CFx object for serialization and viewstate maintenance.
755 //This is called only when a shape without viewstate is autolayed out by the freeform panel.
756 VirtualizedContainerService.VirtualizingContainer container = sender as VirtualizedContainerService.VirtualizingContainer;
757 if (container != null)
759 StoreShapeViewState(container, e.NewLocation);
765 void UpdateFlowchartOnLinkVisualMoved(ConnectionPoint knownConnectionPoint, Point newPoint, Connector movedConnector, bool isSourceKnown)
767 Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
768 HitTestResult hitTestResult = VisualTreeHelper.HitTest(this.panel, newPoint);
769 if (hitTestResult == null)
773 //Test if the last connectionPoint hit, is the new location for the connector.
774 UIElement newViewElement = null;
775 ConnectionPoint newConnectionPoint = null;
777 //The case where the link is dropped on a connectionpoint.
778 if (this.lastConnectionPointMouseUpElement != null)
780 newConnectionPoint = this.ConnectionPointHitTest(this.lastConnectionPointMouseUpElement, newPoint);
781 if (newConnectionPoint != null)
783 newViewElement = this.lastConnectionPointMouseUpElement;
786 //The case where the link is dropped on a shape.
787 if (newViewElement == null)
789 newViewElement = VisualTreeUtils.FindVisualAncestor<StartSymbol>(hitTestResult.VisualHit);
790 if (newViewElement == null)
792 newViewElement = VisualTreeUtils.FindVisualAncestor<VirtualizedContainerService.VirtualizingContainer>(hitTestResult.VisualHit);
795 if (newViewElement != null)
797 if (this.panel.Children.Contains(newViewElement))
799 using (EditingScope es = (EditingScope)this.ModelItem.BeginEdit(SR.FCLinkMove))
801 //Delete the existing link and keep the caseKey
802 IFlowSwitchLink oldCaseKey = this.DeleteLink(movedConnector, true);
805 bool linkCreated = false;
806 string errorMessage = string.Empty;
809 if (newConnectionPoint == null)
811 linkCreated = CreateLinkGesture(knownConnectionPoint, newViewElement, newPoint, out errorMessage, true, oldCaseKey);
815 linkCreated = CreateLinkGesture(knownConnectionPoint, newConnectionPoint, out errorMessage, true, oldCaseKey);
820 //If the Link source is dropped onto itself, we need to set the isLinkValidDueToLinkMove flag.
821 bool isLinkValidDueToLinkMove = FreeFormPanel.GetSourceConnectionPoint(movedConnector).ParentDesigner.Equals(newViewElement);
822 if (newConnectionPoint == null)
824 linkCreated = CreateLinkGesture(newViewElement, knownConnectionPoint, newPoint, out errorMessage, isLinkValidDueToLinkMove, oldCaseKey);
828 linkCreated = CreateLinkGesture(newConnectionPoint, knownConnectionPoint, out errorMessage, isLinkValidDueToLinkMove, oldCaseKey);
833 if (!errorMessage.Equals(string.Empty))
835 ErrorReporting.ShowErrorMessage(errorMessage);
848 void OnFreeFormPanelConnectorMoved(object sender, ConnectorMovedEventArgs e)
850 Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
851 Connector movedConnector = sender as Connector;
852 int movedEndConnectorPointIndex = movedConnector.Points.Count - 1;
853 int newEndConnectorPointIndex = e.NewConnectorLocation.Count - 1;
855 if (movedConnector != null)
857 Fx.Assert(e.NewConnectorLocation.Count > 0, "Invalid connector editor");
858 if (!e.NewConnectorLocation[0].Equals(movedConnector.Points[0]))
861 ConnectionPoint destConnPoint = FreeFormPanel.GetDestinationConnectionPoint(movedConnector);
862 UpdateFlowchartOnLinkVisualMoved(destConnPoint, e.NewConnectorLocation[0], movedConnector, false);
864 else if (!e.NewConnectorLocation[newEndConnectorPointIndex].Equals(movedConnector.Points[movedEndConnectorPointIndex]))
867 ConnectionPoint srcConnPoint = FreeFormPanel.GetSourceConnectionPoint(movedConnector);
868 Point destPoint = e.NewConnectorLocation[newEndConnectorPointIndex];
869 UpdateFlowchartOnLinkVisualMoved(srcConnPoint, destPoint, movedConnector, true);
872 this.selectedConnector = movedConnector;
876 MultiBinding GetConnectionPointBinding(FrameworkElement element, double widthFraction, double heightFraction)
878 Fx.Assert(element != null, "FrameworkElement is null.");
879 MultiBinding bindings = new MultiBinding();
880 Binding sizeBinding = new Binding();
881 sizeBinding.Source = element;
882 sizeBinding.Path = new PropertyPath(FreeFormPanel.ChildSizeProperty);
883 Binding locationBinding = new Binding();
884 locationBinding.Source = element;
885 locationBinding.Path = new PropertyPath(FreeFormPanel.LocationProperty);
886 bindings.Bindings.Add(sizeBinding);
887 bindings.Bindings.Add(locationBinding);
888 bindings.Converter = new ConnectionPointConverter();
889 bindings.ConverterParameter = new List<Object> { widthFraction, heightFraction, element.Margin };
894 Connector GetConnector(ModelItem linkModelItem, ConnectionPoint srcConnPoint, ConnectionPoint destConnPoint)
896 Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
897 ConnectorWithoutStartDot connector = new ConnectorWithoutStartDot();
898 connector.FocusVisualStyle = null;
899 connector.Focusable = true;
900 DesignerView.SetCommandMenuMode(connector, CommandMenuMode.NoCommandMenu);
901 SetConnectorLabel(connector, srcConnPoint, linkModelItem);
903 connector.GotKeyboardFocus += new KeyboardFocusChangedEventHandler(OnConnectorGotKeyboardFocus);
904 connector.RequestBringIntoView += new RequestBringIntoViewEventHandler(OnConnectorRequestBringIntoView);
905 connector.MouseDown += new MouseButtonEventHandler(OnConnectorMouseDown);
906 connector.GotFocus += new RoutedEventHandler(OnConnectorGotFocus);
907 SetConnectorSrcDestConnectionPoints(connector, srcConnPoint, destConnPoint);
908 FlowchartDesigner.SetLinkModelItem(connector, linkModelItem);
909 connector.Unloaded += new RoutedEventHandler(OnConnectorUnloaded);
910 connector.AutoSplitContainer = this;
914 // To prevent the parent FlowchartDesigner from handling mouse events and setting the selection to itself.
915 void OnConnectorMouseDown(object sender, MouseButtonEventArgs e)
920 void SetConnectorLabel(Connector connector, ConnectionPoint srcConnPoint, ModelItem linkModelItem)
922 BindingBase labelBinding = null;
924 if (typeof(FlowDecision).IsAssignableFrom(linkModelItem.ItemType))
926 if (FlowchartDesigner.GetTrueConnectionPoint(srcConnPoint.ParentDesigner).Equals(srcConnPoint))
928 labelBinding = new Binding { Source = linkModelItem, Path = new PropertyPath("TrueLabel") };
932 labelBinding = new Binding { Source = linkModelItem, Path = new PropertyPath("FalseLabel") };
935 SetConnectorLabelToolTip(connector, labelBinding);
937 else if (typeof(IFlowSwitchLink).IsAssignableFrom(linkModelItem.ItemType))
939 IFlowSwitchLink flowSwitchLink = (IFlowSwitchLink)linkModelItem.GetCurrentValue();
940 labelBinding = flowSwitchLink.CreateConnectorLabelTextBinding();
941 SetConnectorLabelToolTip(connector, labelBinding);
945 void SetConnectorLabelToolTip(Connector connector, BindingBase binding)
947 connector.SetBinding(Connector.LabelTextProperty, binding);
948 ToolTip toolTip = new ToolTip();
949 toolTip.SetBinding(UserControl.ContentProperty, binding);
950 connector.SetLabelToolTip(toolTip);
953 void OnConnectorUnloaded(object sender, RoutedEventArgs e)
955 ModelItem primarySelection = this.Context.Items.GetValue<Selection>().PrimarySelection;
956 if (object.Equals(primarySelection, FlowchartDesigner.GetLinkModelItem(sender as DependencyObject)))
958 if (primarySelection != null)
960 Selection.Toggle(this.Context, primarySelection);
965 //Marking e.Handled = true to avoid scrolling in large workflows to bring the
966 //area of a connector in the center of the view region.
967 //Area covered by a connector includes the region between 0,0 of the panel and the edges of the connector.
968 void OnConnectorRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e)
975 //Returns a new connector if viewstate exists, null otherwise.
976 Connector GetConnectorViewState(UIElement source, UIElement dest, ModelItem linkModelItem, ConnectionPoint sourceConnectionPoint)
978 Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
979 Connector connector = null;
980 object connectorLocation = null;
981 if (typeof(FlowDecision).IsAssignableFrom(linkModelItem.ItemType))
983 Fx.Assert(sourceConnectionPoint != null, "Source connection point is null.");
984 if (sourceConnectionPoint.Equals(FlowchartDesigner.GetTrueConnectionPoint(this.modelElement[linkModelItem])))
986 connectorLocation = this.ViewStateService.RetrieveViewState(linkModelItem, TrueConnectorViewStateKey);
990 connectorLocation = this.ViewStateService.RetrieveViewState(linkModelItem, FalseConnectorViewStateKey);
993 else if (typeof(IFlowSwitchLink).IsAssignableFrom(linkModelItem.ItemType))
996 IFlowSwitchLink link = (IFlowSwitchLink)linkModelItem.GetCurrentValue();
997 if (link.IsDefaultCase)
999 key = FlowSwitchDefaultViewStateKey;
1003 key = link.CaseName + CaseViewStateKeyAppendString;
1005 //Transitioning from fake ModelItem world to real ModelItem world.
1006 ModelItem realFSModelItem = (this.ModelItem as IModelTreeItem).ModelTreeManager.WrapAsModelItem(link.ParentFlowSwitch);
1007 connectorLocation = this.ViewStateService.RetrieveViewState(realFSModelItem, key);
1011 connectorLocation = this.ViewStateService.RetrieveViewState(linkModelItem, ConnectorViewStateKey);
1013 PointCollection locationPts = connectorLocation as PointCollection;
1014 if (locationPts != null)
1016 ConnectionPoint srcConnPoint, destConnPoint;
1017 System.Diagnostics.Debug.WriteLine(this.isLoaded ? "About to call ConnectionPointHitTest - Loaded" : "About to call ConnectionPointHitTest - Not Loaded");
1018 srcConnPoint = ConnectionPointHitTest(source, locationPts[0]);
1019 destConnPoint = ConnectionPointHitTest(dest, locationPts[locationPts.Count - 1]);
1020 //In Debug mode, the size of the designer changes due to the debug adorner(border). Because of this connection points will move and
1021 //won't coincide with the viewstate.
1022 //The following code path is added for the scenario where we reload the flowchart designer by navigating back and forth on breadcrumb
1023 //when one of the flowchart activities has the debug border.
1024 //In this scenario we try to find the closest connection point from the end point stored in viewstate. If the distance between the two
1025 //is within the acceptable range, we will reuse the viewstate and avoid re-drawing the connector.
1026 if (this.IsReadOnly)
1030 if (srcConnPoint == null)
1032 pt = FindClosestConnectionPoint(locationPts[0], FlowchartDesigner.GetConnectionPoints(source), out dist);
1033 if (pt != null && pt.PointType != ConnectionPointKind.Incoming && dist <= DebugTimeMaxConnectorShapeDist)
1038 if (destConnPoint == null)
1040 pt = FindClosestConnectionPoint(locationPts[locationPts.Count - 1], FlowchartDesigner.GetConnectionPoints(dest), out dist);
1041 if (pt != null && pt.PointType != ConnectionPointKind.Outgoing && dist <= DebugTimeMaxConnectorShapeDist)
1047 if (srcConnPoint != null && destConnPoint != null)
1049 connector = GetConnector(linkModelItem, srcConnPoint, destConnPoint);
1050 connector.Points = locationPts;
1057 //Marking e.Handled true for the case where a connector is clicked on.
1058 //This is to prevent WorkflowViewElement class from making Flowchart as the current selection.
1059 void OnConnectorGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
1064 void OnConnectorGotFocus(object sender, RoutedEventArgs e)
1066 Connector clickedLine = e.Source as Connector;
1067 DesignerView designerView = this.Context.Services.GetService<DesignerView>();
1069 if (!designerView.IsMultipleSelectionMode)
1072 if (this.panel.connectorEditor == null || !clickedLine.Equals(this.panel.connectorEditor.Connector))
1074 this.panel.RemoveConnectorEditor();
1075 this.panel.connectorEditor = new ConnectorEditor(this.panel, clickedLine);
1078 if (this.panel.Children.Contains(clickedLine))
1080 this.updatingSelectedConnector = true;
1081 ModelItem lineModelItem = FlowchartDesigner.GetLinkModelItem(clickedLine);
1082 Selection newSelection = new Selection();
1083 // If the linkModelItem is FlowDecision or Flowchart, we don't want to add it to the selection
1084 if (IsLinkModelItemSelectable(lineModelItem))
1086 newSelection = new Selection(lineModelItem);
1088 this.Context.Items.SetValue(newSelection);
1089 this.selectedConnector = clickedLine;
1090 this.updatingSelectedConnector = false;
1096 private void OnSelectionChanged(Selection selection)
1098 // If selection changed, remove ConnectorEditor if existed.
1099 // Only if the selection changed is caused by adding ConnectorEditor when OnConnectorGotFocus, ignore.
1100 if (!this.updatingSelectedConnector && this.panel != null && this.panel.connectorEditor != null)
1102 this.panel.RemoveConnectorEditor();
1106 //widthFraction, heightFraction determine location of connectionpoint on the shape.
1107 ConnectionPoint CreateConnectionPoint(UIElement element, double widthFraction, double heightFraction, EdgeLocation location)
1109 ConnectionPoint connectionPoint = new ConnectionPoint();
1110 connectionPoint.EdgeLocation = location;
1111 connectionPoint.PointType = ConnectionPointKind.Default;
1112 connectionPoint.ParentDesigner = element;
1113 BindingOperations.SetBinding(connectionPoint, ConnectionPoint.LocationProperty, GetConnectionPointBinding(element as FrameworkElement, widthFraction, heightFraction));
1114 return connectionPoint;
1117 void PopulateConnectionPoints(UIElement element, ModelItem model)
1119 element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
1120 List<ConnectionPoint> connectionPoints;
1121 if (model != null &&
1122 GenericFlowSwitchHelper.IsGenericFlowSwitch(model.ItemType))
1124 connectionPoints = new List<ConnectionPoint>
1127 CreateConnectionPoint(element, 0.25, 0, EdgeLocation.Top),
1128 CreateConnectionPoint(element, 0.5, 0, EdgeLocation.Top),
1129 CreateConnectionPoint(element, 0.75, 0, EdgeLocation.Top),
1131 CreateConnectionPoint(element, 1, 0.25, EdgeLocation.Right),
1132 CreateConnectionPoint(element, 1, 0.50, EdgeLocation.Right),
1133 CreateConnectionPoint(element, 1, 0.75, EdgeLocation.Right),
1135 CreateConnectionPoint(element, 0.25, 1, EdgeLocation.Bottom),
1136 CreateConnectionPoint(element, 0.5, 1, EdgeLocation.Bottom),
1137 CreateConnectionPoint(element, 0.75, 1, EdgeLocation.Bottom),
1139 CreateConnectionPoint(element, 0, 0.25, EdgeLocation.Left),
1140 CreateConnectionPoint(element, 0, 0.50, EdgeLocation.Left),
1141 CreateConnectionPoint(element, 0, 0.75, EdgeLocation.Left),
1144 else if (model != null && typeof(FlowDecision).IsAssignableFrom(model.ItemType))
1146 ConnectionPoint trueConnectionPoint = CreateConnectionPoint(element, 0, 0.50, EdgeLocation.Left);
1147 trueConnectionPoint.PointType = ConnectionPointKind.Outgoing;
1148 FlowchartDesigner.SetTrueConnectionPoint(element, trueConnectionPoint);
1150 ConnectionPoint falseConnectionPoint = CreateConnectionPoint(element, 1, 0.50, EdgeLocation.Right);
1151 falseConnectionPoint.PointType = ConnectionPointKind.Outgoing;
1152 FlowchartDesigner.SetFalseConnectionPoint(element, falseConnectionPoint);
1154 connectionPoints = new List<ConnectionPoint>
1157 CreateConnectionPoint(element, 0.25, 0, EdgeLocation.Top),
1158 CreateConnectionPoint(element, 0.5, 0, EdgeLocation.Top),
1159 CreateConnectionPoint(element, 0.75, 0, EdgeLocation.Top),
1161 CreateConnectionPoint(element, 0.5, 1, EdgeLocation.Bottom),
1163 connectionPoints.ForEach((point) => point.PointType = ConnectionPointKind.Incoming);
1167 //First adding top, right, bottom and left default connection points in that order on all shapes other than flowswitch.
1168 //For shapes that do not need any of the points, we will remove that point explicitly.
1169 connectionPoints = new List<ConnectionPoint>
1171 CreateConnectionPoint(element, 0.5, 0, EdgeLocation.Top),
1172 CreateConnectionPoint(element, 1, 0.5, EdgeLocation.Right),
1173 CreateConnectionPoint(element, 0.5, 1, EdgeLocation.Bottom),
1174 CreateConnectionPoint(element, 0, 0.5, EdgeLocation.Left)
1178 if (model == null) // Start symbol: model = null
1180 foreach (ConnectionPoint connPoint in connectionPoints)
1182 connPoint.PointType = ConnectionPointKind.Outgoing;
1185 FlowchartDesigner.SetConnectionPoints(element, connectionPoints);
1188 void SetFlowElementModelItem(UIElement view, ModelItem model)
1190 ModelItem flowElementMI = model;
1191 if (flowElementMI != null && !IsFlowNode(flowElementMI))
1193 ModelItem flowStepMI = null;
1194 //Select the right FlowStep ModelItem out of view.ModelItem.Parents.
1195 foreach (ModelItem parentModelItem in flowElementMI.Parents)
1197 if (IsFlowNode(parentModelItem)
1198 && this.ModelItem.Properties["Nodes"].Collection.Contains(parentModelItem))
1200 flowStepMI = parentModelItem;
1204 flowElementMI = flowStepMI;
1206 Fx.Assert(flowElementMI != null, "Non FlowNode present on Flowchart");
1207 FlowchartDesigner.SetFlowElementModelItem(view as DependencyObject, flowElementMI);
1208 this.flowNodeToUIElement[flowElementMI] = view;
1212 UIElement ProcessAndGetModelView(ModelItem model)
1214 Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
1215 UIElement container;
1216 if (!this.modelElement.TryGetValue(model, out container))
1218 VirtualizedContainerService containerService = this.Context.Services.GetService<VirtualizedContainerService>();
1219 Fx.Assert(this.ViewService != null, "ViewService is null");
1220 container = containerService.GetContainer(model, this);
1221 //WorkflowViewElement view = (WorkflowViewElement)this.ViewService.GetView(model);
1222 //Fx.Assert(view != null, "View does not exist for a model Item");
1223 //DragDropHelper.SetSourceContainer(view, this);
1224 //element = (UIElement)view;
1225 //element.MouseEnter += new MouseEventHandler(ChildElement_MouseEnter);
1226 //element.MouseLeave += new MouseEventHandler(ChildElement_MouseLeave);
1227 container.MouseEnter += new MouseEventHandler(ChildElement_MouseEnter);
1228 container.MouseLeave += new MouseEventHandler(ChildElement_MouseLeave);
1230 ((FrameworkElement)container).SizeChanged += new SizeChangedEventHandler(ChildSizeChanged);
1231 this.modelElement.Add(model, container);
1232 PopulateConnectionPoints(container, model);
1233 this.SetFlowElementModelItem(container, model);
1235 //Getting the View state information.
1236 ModelItem flowElementMI = GetFlowElementMI(model);
1237 object locationOfShape = this.ViewStateService.RetrieveViewState(flowElementMI, shapeLocation);
1238 object sizeOfShape = this.ViewStateService.RetrieveViewState(flowElementMI, shapeSize);
1239 if (locationOfShape != null)
1241 Point locationPt = (Point)locationOfShape;
1242 FreeFormPanel.SetLocation(container, locationPt);
1243 this.shapeLocations.Add(locationPt);
1245 if (sizeOfShape != null)
1247 Size size = (Size)sizeOfShape;
1248 FreeFormPanel.SetChildSize(container, size);
1249 VirtualizedContainerService.VirtualizingContainer virtualizingContainer = container as VirtualizedContainerService.VirtualizingContainer;
1250 if (virtualizingContainer != null)
1252 virtualizingContainer.MinWidth = size.Width;
1253 virtualizingContainer.MinHeight = size.Height;
1261 void GetSrcDestConnectionPoints(UIElement source, UIElement dest, out ConnectionPoint srcConnPoint, out ConnectionPoint destConnPoint, out string errorMessage)
1263 srcConnPoint = null;
1264 destConnPoint = null;
1265 errorMessage = string.Empty;
1266 VirtualizedContainerService.VirtualizingContainer sourceContainer = source as VirtualizedContainerService.VirtualizingContainer;
1267 if (sourceContainer != null && typeof(FlowDecision).IsAssignableFrom(sourceContainer.ModelItem.ItemType))
1269 srcConnPoint = FindFlowDecisionSrcConnectionPoint(source, out errorMessage);
1270 if (srcConnPoint != null)
1272 destConnPoint = FindBestMatchDestConnectionPoint(srcConnPoint, dest, out errorMessage);
1278 List<ConnectionPoint> srcConnectionPoints = FlowchartDesigner.GetConnectionPoints(source);
1279 List<ConnectionPoint> destConnectionPoints = FlowchartDesigner.GetConnectionPoints(dest);
1280 if (sourceContainer != null && GenericFlowSwitchHelper.IsGenericFlowSwitch(sourceContainer.ModelItem.ItemType))
1282 FindBestMatchConnectionPointPair(srcConnectionPoints, destConnectionPoints, out srcConnPoint, out destConnPoint);
1287 FindBestMatchConnectionPointPair(srcConnectionPoints, destConnectionPoints, out srcConnPoint, out destConnPoint);
1292 //This returns the closest non-outgoing connectionPoint on dest. Return value will be different than sourceConnectionPoint.
1293 ConnectionPoint ClosestDestConnectionPoint(ConnectionPoint sourceConnectionPoint, UIElement dest, out string errorMessage)
1295 ConnectionPoint destConnectionPoint = null;
1296 errorMessage = string.Empty;
1297 if (sourceConnectionPoint.PointType != ConnectionPointKind.Incoming)
1299 destConnectionPoint = FindClosestConnectionPointNotOfType(sourceConnectionPoint, FlowchartDesigner.GetConnectionPoints(dest), ConnectionPointKind.Outgoing);
1303 errorMessage = SR.FCInvalidLink;
1305 return destConnectionPoint;
1309 //This returns the closest non-Incoming connectionPoint on source. Return value will be different than destConnectionPoint.
1310 ConnectionPoint ClosestSrcConnectionPoint(UIElement src, ConnectionPoint destConnectionPoint, out string errorMessage)
1312 ConnectionPoint sourceConnectionPoint = null;
1313 errorMessage = string.Empty;
1314 if (destConnectionPoint.PointType != ConnectionPointKind.Outgoing)
1316 VirtualizedContainerService.VirtualizingContainer srcContainer = src as VirtualizedContainerService.VirtualizingContainer;
1317 if (srcContainer != null && typeof(FlowDecision).IsAssignableFrom(srcContainer.ModelItem.ItemType))
1319 sourceConnectionPoint = FindFlowDecisionSrcConnectionPoint(src, out errorMessage);
1323 sourceConnectionPoint = FindClosestConnectionPointNotOfType(destConnectionPoint, FlowchartDesigner.GetConnectionPoints(src), ConnectionPointKind.Incoming);
1328 errorMessage = SR.FCInvalidLink;
1330 return sourceConnectionPoint;
1333 //Priority of selection: 1st true then false.
1334 ConnectionPoint FindFlowDecisionSrcConnectionPoint(UIElement decisionDesigner, out string errorMessage)
1336 ConnectionPoint sourceConnectionPoint = null;
1337 errorMessage = string.Empty;
1338 ConnectionPoint trueConnPoint = FlowchartDesigner.GetTrueConnectionPoint(decisionDesigner);
1339 ConnectionPoint falseConnPoint = FlowchartDesigner.GetFalseConnectionPoint(decisionDesigner);
1340 if (trueConnPoint.AttachedConnectors.Count == 0)
1342 sourceConnectionPoint = trueConnPoint;
1344 else if (falseConnPoint.AttachedConnectors.Count == 0)
1346 sourceConnectionPoint = falseConnPoint;
1350 errorMessage = SR.FCFlowConditionLinksExist;
1352 return sourceConnectionPoint;
1355 void SetConnectorSrcDestConnectionPoints(Connector connector, ConnectionPoint sourceConnectionPoint, ConnectionPoint destConnectionPoint)
1357 FreeFormPanel.SetSourceConnectionPoint(connector, sourceConnectionPoint);
1358 FreeFormPanel.SetDestinationConnectionPoint(connector, destConnectionPoint);
1359 sourceConnectionPoint.AttachedConnectors.Add(connector);
1360 destConnectionPoint.AttachedConnectors.Add(connector);
1363 //Save the connector.Points property on the CFx object for serialization and viewstate maintenance.
1364 void StoreConnectorViewState(ModelItem linkModelItem, PointCollection viewState, ConnectionPoint srcConnPoint, bool isUndoableViewState)
1366 ModelItem storageModelItem = GetConnectorViewStateStorageModelItem(linkModelItem);
1367 string viewStateKey = GetConnectorViewStateKey(linkModelItem, srcConnPoint);
1368 StoreConnectorViewState(storageModelItem, viewStateKey, viewState, isUndoableViewState);
1371 void StoreConnectorViewState(ModelItem storageModelItem, string viewStateKey, PointCollection viewState, bool isUndoableViewState)
1373 if (isUndoableViewState)
1375 this.ViewStateService.StoreViewStateWithUndo(storageModelItem, viewStateKey, viewState);
1379 this.ViewStateService.StoreViewState(storageModelItem, viewStateKey, viewState);
1383 void StoreConnectorViewState(ModelItem linkModelItem, PointCollection viewState, ConnectionPoint srcConnPoint)
1385 StoreConnectorViewState(linkModelItem, viewState, srcConnPoint, true);
1388 void StoreConnectorViewState(Connector connector, bool isUndoableViewState)
1390 //This method will be called whenever the FreeFormPanel raises a location changed event on a connector.
1391 //Such location changed events are a result of changes already commited in the UI. Hence we do not want to react to such view state changes.
1392 //Using internalViewStateChange flag for that purpose.
1393 this.internalViewStateChange = true;
1394 this.StoreConnectorViewState(FlowchartDesigner.GetLinkModelItem(connector), connector.Points, FreeFormPanel.GetSourceConnectionPoint(connector), isUndoableViewState);
1395 this.internalViewStateChange = false;
1398 string GetConnectorViewStateKey(ModelItem linkModelItem, ConnectionPoint srcConnPoint)
1400 string viewStateKey = ConnectorViewStateKey;
1401 if ((typeof(FlowDecision).IsAssignableFrom(linkModelItem.ItemType)))
1403 if (srcConnPoint.Equals(FlowchartDesigner.GetTrueConnectionPoint(this.modelElement[linkModelItem])))
1405 viewStateKey = TrueConnectorViewStateKey;
1409 viewStateKey = FalseConnectorViewStateKey;
1412 else if (typeof(IFlowSwitchLink).IsAssignableFrom(linkModelItem.ItemType))
1414 IFlowSwitchLink link = (IFlowSwitchLink)linkModelItem.GetCurrentValue();
1415 if (link.IsDefaultCase)
1417 viewStateKey = FlowSwitchDefaultViewStateKey;
1421 viewStateKey = link.CaseName + CaseViewStateKeyAppendString;
1424 return viewStateKey;
1427 ModelItem GetConnectorViewStateStorageModelItem(ModelItem linkModelItem)
1429 ModelItem storageModelItem = linkModelItem;
1430 if (typeof(IFlowSwitchLink).IsAssignableFrom(linkModelItem.ItemType))
1432 IFlowSwitchLink link = (IFlowSwitchLink)linkModelItem.GetCurrentValue();
1433 //Getting FlowSwitch ModelItem since there is no CFx object for linkModelItem.
1434 IModelTreeItem modelTreeItem = this.ModelItem as IModelTreeItem;
1435 storageModelItem = modelTreeItem.ModelTreeManager.WrapAsModelItem(link.ParentFlowSwitch);
1437 return storageModelItem;
1441 //Save the shape location on the CFx object for serialization and viewstate maintenance.
1442 void StoreShapeViewState(UIElement movedElement, Point newLocation)
1444 ModelItem storageModelItem;
1445 if (movedElement is StartSymbol)
1447 storageModelItem = this.ModelItem;
1451 ModelItem model = ((VirtualizedContainerService.VirtualizingContainer)movedElement).ModelItem;
1452 storageModelItem = GetFlowElementMI(model);
1454 StoreShapeViewState(storageModelItem, newLocation);
1457 void StoreShapeViewState(ModelItem storageModelItem, Point newLocation)
1459 if (this.ViewStateService.RetrieveViewState(storageModelItem, shapeLocation) != null)
1461 this.ViewStateService.StoreViewStateWithUndo(storageModelItem, shapeLocation, newLocation);
1465 this.ViewStateService.StoreViewState(storageModelItem, shapeLocation, newLocation);
1469 void PerformInternalMove(UIElement movedElement, Point newPoint, Point? shapeAnchorPoint,
1470 AutoConnectDirections autoConnectDirection, Connector connectorToSplit)
1472 using (EditingScope es = (EditingScope)this.ModelItem.BeginEdit(SR.FCLinkMove))
1474 RemoveAdorner(movedElement, typeof(ConnectionPointsAdorner));
1475 Point shapeLocation;
1476 Size size = FreeFormPanel.GetChildSize(movedElement);
1477 if (autoConnectDirection != AutoConnectDirections.None)
1479 shapeLocation = this.CalculateDropLocationForAutoConnect(autoConnectDirection, size);
1481 else if (shapeAnchorPoint.HasValue)
1483 shapeLocation = SnapVisualToGrid(movedElement, newPoint, shapeAnchorPoint.Value, true);
1487 Fx.Assert(newPoint.X.IsNoLessThan(0) && newPoint.Y.IsNoLessThan(0),
1488 "newPoint is negative");
1489 shapeLocation = newPoint;
1491 if (connectorToSplit != null)
1493 shapeLocation = this.CalculateDropLocationForAutoSplit(newPoint, shapeLocation, connectorToSplit, size);
1495 StoreShapeViewState(movedElement, shapeLocation);
1496 RerouteAttachedConnectors(movedElement);
1501 void RerouteAttachedConnectors(UIElement movedElement)
1503 foreach (Connector connector in GetAttachedConnectors(movedElement))
1505 Reroute(connector, true);
1509 void Reroute(Connector connector, bool withUndo)
1511 ConnectionPoint source = FreeFormPanel.GetSourceConnectionPoint(connector);
1512 ConnectionPoint destination = FreeFormPanel.GetDestinationConnectionPoint(connector);
1514 //Nulling out the PointCollection so that it doesn't interfere in line routing.
1515 connector.Points = new PointCollection();
1516 PointCollection viewState = new PointCollection(ConnectorRouter.Route(this.panel, source, destination));
1517 StoreConnectorViewState(FlowchartDesigner.GetLinkModelItem(connector), viewState, source, withUndo);
1522 // Returns the last dropped item - used for auto-connect and auto-split where only one item is allowed
1523 ModelItem DoFlowchartGridDrop(DragEventArgs e, AutoConnectDirections autoConnectDirection, Connector connectorToSplit)
1525 ModelItem droppedModelItem = null;
1526 ModelItem newFlowStepMI = null;
1527 e.Effects = DragDropEffects.None;
1528 IEnumerable<object> droppedObjects = DragDropHelper.GetDroppedObjects(this, e, Context);
1529 //Marking the event as being handled. In whichever case we want to route the event, it will be unmarked explicitly.
1531 List<WorkflowViewElement> movedViewElements = new List<WorkflowViewElement>();
1532 ShapeOffsetter shapeOffsetter = new ShapeOffsetter();
1533 Dictionary<WorkflowViewElement, Point> relativeLocations = DragDropHelper.GetDraggedViewElementRelativeLocations(e);
1534 ModelItem modelItemDroppedFromToolBox = null;
1535 Dictionary<object, FlowNode> objToNewFlowNodeMap = null;
1536 Dictionary<FlowNode, ModelItem> flowNodeModelItemMap = null;
1537 Dictionary<FlowNode, FlowNode> oldNewFlowNodeMap = null;
1538 this.PrepareForDrop(droppedObjects,
1539 out objToNewFlowNodeMap,
1540 out flowNodeModelItemMap,
1541 out oldNewFlowNodeMap);
1542 bool shouldStoreCurrentSizeViewState = true;
1543 foreach (object droppedObject in droppedObjects)
1545 if (droppedObject == null)
1549 droppedModelItem = droppedObject as ModelItem;
1552 Point anchorPoint = DragDropHelper.GetDragDropAnchorPoint(e);
1555 ICompositeView srcContainer = droppedModelItem != null
1556 ? DragDropHelper.GetCompositeView(droppedModelItem.View as WorkflowViewElement) as ICompositeView
1558 bool keepRelativePosition = srcContainer is FlowchartDesigner;
1559 // This is the case of dragging from toolbox
1560 if (anchorPoint.X < 0 && anchorPoint.Y < 0)
1562 keepRelativePosition = false;
1565 // This is the case of dragging from the designer surface
1566 else if (droppedModelItem != null)
1568 WorkflowViewElement view = (WorkflowViewElement)droppedModelItem.View;
1569 anchorPoint.Offset(-relativeLocations[view].X, -relativeLocations[view].Y);
1573 if (droppedModelItem != null && srcContainer != null && srcContainer.Equals(this))
1575 if (shouldStoreCurrentSizeViewState)
1577 // Moving may change the size of flowchart; need this to undo the size change.
1578 this.StoreCurrentSizeViewStateWithUndo();
1579 shouldStoreCurrentSizeViewState = false;
1582 PerformInternalMove(modelElement[droppedModelItem], e.GetPosition(this.panel), anchorPoint, autoConnectDirection, connectorToSplit);
1586 //External model Item drop.
1587 if (droppedModelItem != null)
1589 if ((IsFlowStepAction(droppedModelItem)
1590 || IsFlowNode(droppedModelItem))
1591 && !IsParentOf(droppedModelItem, this.ModelItem))
1593 if (shouldStoreCurrentSizeViewState)
1595 // Drop may change the size of flowchart; need this to undo the size change.
1596 this.StoreCurrentSizeViewStateWithUndo();
1597 shouldStoreCurrentSizeViewState = false;
1600 FlowNode flowElement = objToNewFlowNodeMap[droppedObject];
1601 ModelItem flowElementMI;
1602 if (flowNodeModelItemMap.TryGetValue(flowElement, out flowElementMI))
1604 // FlowNode comes from some other flowchart.
1605 this.ModelItem.Properties["Nodes"].Collection.Add(flowElementMI);
1609 // FlowNode is a new created one, which means this is an Activity dragged
1610 // from somewhere else, outside of Flowchart.
1611 flowElementMI = this.ModelItem.Properties["Nodes"].Collection.Add(flowElement);
1612 flowNodeModelItemMap[flowElement] = flowElementMI;
1614 newFlowStepMI = flowElementMI;
1618 //We want to route the event in the case that the flowchart is dropped upon itself.
1619 if (droppedModelItem.Equals(this.ModelItem))
1623 //Don't add anything for what is neither a Activity nor a flowlink.
1627 if (droppedModelItem != null && droppedModelItem.View != null)
1629 movedViewElements.Add((WorkflowViewElement)droppedModelItem.View);
1632 // the external item may come from other panel (sequence) which is already given
1633 // a size by its previous layout panel. That might give an inaccurate size to the
1634 // dropped object (i.e. Bug 198290). Therefore, when the object is dropped externally
1635 // the FC should erases its previous hint size, forcing the FC to recompute an appropriate
1636 // size based on the workflowelementview size.
1637 VirtualizedContainerService.SetHintSize(droppedModelItem.GetCurrentValue(), null);
1642 if (typeof(Activity).IsAssignableFrom(droppedObject.GetType()))
1644 FlowStep flowStep = new FlowStep();
1645 flowStep.Action = (Activity)droppedObject;
1646 if (shouldStoreCurrentSizeViewState)
1648 // Drop may change the size of flowchart; need this to undo the size change.
1649 this.StoreCurrentSizeViewStateWithUndo();
1650 shouldStoreCurrentSizeViewState = false;
1653 newFlowStepMI = this.ModelItem.Properties["Nodes"].Collection.Add(flowStep);
1654 droppedModelItem = newFlowStepMI.Properties["Action"].Value;
1656 else if (typeof(FlowNode).IsAssignableFrom(droppedObject.GetType()))
1658 if (shouldStoreCurrentSizeViewState)
1660 // Drop may change the size of flowchart; need this to undo the size change.
1661 this.StoreCurrentSizeViewStateWithUndo();
1662 shouldStoreCurrentSizeViewState = false;
1664 droppedModelItem = this.ModelItem.Properties["Nodes"].Collection.Add(droppedObject);
1665 newFlowStepMI = droppedModelItem;
1668 // Now, toolbox drop doesn't support multiple drop
1669 // If multi-drop from tool box, use an array here.
1670 modelItemDroppedFromToolBox = droppedModelItem;
1671 keepRelativePosition = false;
1674 WorkflowViewElement view = droppedModelItem.View as WorkflowViewElement;
1675 if (view == null || view.ExpandState)
1677 //Creating a new view to get the size of collapsed view.
1678 view = this.ViewService.GetView(droppedModelItem) as WorkflowViewElement;
1679 ViewUtilities.MeasureView(view, true);
1684 PostDropUpdateViewState(view,
1686 autoConnectDirection,
1688 e.GetPosition(this.panel),
1690 keepRelativePosition,
1696 // Remap references.
1697 // The re-map here is different from the remaping in copy/paste.
1698 // In copy paste, all the values are copied. but here, some value are
1699 // set by Properties["key"].SetValue().
1700 // Don't move this into PrepareMove. Some value setting is added to
1701 // Change. So the operation here will decide the order of Change.Apply().
1702 // PropertyChange in some case, must happen after ModelItem is moved to
1704 foreach (FlowNode flowNode in oldNewFlowNodeMap.Keys)
1706 UpdateCloneReferenceByModelItem(flowNode, flowNodeModelItemMap, oldNewFlowNodeMap);
1709 DragDropHelper.SetDragDropMovedViewElements(e, movedViewElements);
1711 //Backward compatibility for 4.0
1712 if (droppedObjects.Count() == 1 && movedViewElements.Count == 1)
1714 #pragma warning disable 618
1715 DragDropHelper.SetDragDropCompletedEffects(e, DragDropEffects.Move);
1716 #pragma warning restore 618
1719 if (modelItemDroppedFromToolBox != null)
1721 // if it is dropped from toolbox, select
1722 this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action)(() =>
1724 UIElement view = (UIElement)(modelItemDroppedFromToolBox.View);
1727 Keyboard.Focus(view);
1728 Selection.SelectOnly(this.Context, modelItemDroppedFromToolBox);
1733 if (droppedModelItem != null)
1735 if (IsFlowNode(droppedModelItem))
1737 return droppedModelItem;
1739 else if (IsFlowStepAction(droppedModelItem))
1741 if (newFlowStepMI != null)
1743 return newFlowStepMI;
1747 return this.GetParentFlowStepModelItem(droppedModelItem);
1756 Point CalculateDropLocationForAutoConnect(AutoConnectDirections autoConnectDirection, Size droppedSize)
1758 return AutoConnectHelper.CalculateDropLocation(droppedSize, this.panel.CurrentAutoConnectTarget, autoConnectDirection, this.shapeLocations);
1761 Point CalculateDropLocationForAutoSplit(Point mousePosition, Point originalDropLocation, Connector connector, Size droppedSize)
1763 return AutoSplitHelper.CalculateDropLocation(mousePosition, originalDropLocation, connector, droppedSize, this.shapeLocations);
1766 private void OnFlowchartGridDrop(object sender, DragEventArgs e)
1768 ModelItemHelper.TryCreateImmediateEditingScopeAndExecute(this.ModelItem.GetEditingContext(), System.Activities.Presentation.SR.CollectionAddEditingScopeDescription, (es) =>
1770 this.DoFlowchartGridDrop(e, AutoConnectDirections.None, null);
1778 // Prepare some maps for drag/drop in Flowchart
1779 // objToNewFlowNodeMap :
1780 // <DroppedObject, FlowNodeToBeDropInDestFlowchart>
1781 // flowNodeNewModelItemMap :
1782 // <OldFlowNode, NewModelItemInDestFlowchart>
1783 // For FlowSwitch/FlowDecision: NewModelItemINDestFlowchart is the source ModelItem.
1784 // For FlowStep, leeve NewModeItemInDestFlowchart Null, this value could not be decided here.
1785 // flowNodeMovingMap:
1786 // <OldFlowNode, NewFlowNode>
1787 // If a droppedObject is not a flownode, say it is from tool box, this value is empty.
1788 private void PrepareForDrop(IEnumerable<object> objects,
1789 out Dictionary<object, FlowNode> objToNewFlowNodeMap,
1790 out Dictionary<FlowNode, ModelItem> flowNodeNewModelItemMap,
1791 out Dictionary<FlowNode, FlowNode> oldNewFlowNodeMap)
1793 Fx.Assert(objects != null, "dropping null objects");
1794 objToNewFlowNodeMap = new Dictionary<object, FlowNode>();
1795 oldNewFlowNodeMap = new Dictionary<FlowNode, FlowNode>();
1796 flowNodeNewModelItemMap = new Dictionary<FlowNode, ModelItem>();
1797 // 1) Get flow node and Composite view
1798 foreach (object obj in objects)
1802 Fx.Assert("obj == null");
1806 objToNewFlowNodeMap[obj] = null;
1807 ModelItem modelItem = obj as ModelItem;
1808 if (modelItem == null)
1810 // if not a model item, return
1814 ICompositeView compositeView = DragDropHelper.GetCompositeView(modelItem.View as WorkflowViewElement) as ICompositeView;
1815 if (compositeView == null)
1820 // This means a internal move, no extra thing needed.
1821 if (compositeView.Equals(this))
1827 if (!IsFlowStepAction(modelItem)
1828 && !IsFlowNode(modelItem))
1831 Fx.Assert(modelItem.ItemType.Equals(typeof(StartNode)),
1832 "should not happen. Not a Activity, FlowNode or StartSymbol");
1833 // don't do anything if the dropped object is either an Activity or
1838 FlowNode flowNode = null;
1839 FlowchartDesigner fcDesigner = compositeView as FlowchartDesigner;
1840 if (fcDesigner != null)
1842 // If the source view is a FlowchartDesigner, we need to do some to prepare
1844 ModelItem flowElementMI = fcDesigner.GetFlowElementMI(modelItem);
1845 Fx.Assert(flowElementMI != null, "flowElementMI != null");
1846 FlowNode oldFlowNode = flowElementMI.GetCurrentValue() as FlowNode;
1847 Fx.Assert(oldFlowNode != null, "oldFlowNode != null");
1848 flowNode = oldFlowNode;
1849 flowNodeNewModelItemMap[oldFlowNode] = flowElementMI;
1850 oldNewFlowNodeMap[oldFlowNode] = flowNode;
1854 // The object is moved from somewhere else, say Sequence.
1855 // We create a FlowStep for it.
1856 FlowStep flowStep = new FlowStep();
1857 flowStep.Action = (Activity)modelItem.GetCurrentValue();
1858 flowNode = flowStep;
1860 objToNewFlowNodeMap[obj] = flowNode;
1864 // Move the object to correct position after drop
1865 private void PostDropUpdateViewState(WorkflowViewElement view,
1866 ModelItem flownodeMI,
1867 AutoConnectDirections autoConnectDirection,
1868 Connector connectorToSplit,
1871 bool keepRelativePosition,
1872 ShapeOffsetter shapeOffsetter)
1874 Fx.Assert((view != null && flownodeMI != null),
1875 "movedItem != null && flownodeMI != null");
1876 Point shapeLocationPtr;
1877 if (autoConnectDirection != AutoConnectDirections.None)
1879 shapeLocationPtr = this.CalculateDropLocationForAutoConnect(autoConnectDirection, view.DesiredSize);
1883 shapeLocationPtr = SnapVisualToGrid(view, newPoint, anchorPoint, keepRelativePosition);
1884 if (!keepRelativePosition)
1886 // To avoid overlaps
1887 shapeLocationPtr = shapeOffsetter.OffsetShapeLocation(shapeLocationPtr);
1891 if (connectorToSplit != null)
1893 shapeLocationPtr = this.CalculateDropLocationForAutoSplit(newPoint, shapeLocationPtr, connectorToSplit, view.DesiredSize);
1897 if (keepRelativePosition)
1899 this.OffsetDroppedItemToNewPosition(flownodeMI, shapeLocationPtr);
1903 this.StoreShapeViewState(flownodeMI, shapeLocationPtr);
1907 private void OffsetDroppedItemToNewPosition(ModelItem flownodeMI, Point newLocationPtr)
1909 object locationOfShape = this.ViewStateService.RetrieveViewState(flownodeMI, shapeLocation);
1910 if (locationOfShape == null)
1914 Point oldLocationPoint = (Point)locationOfShape;
1915 Vector offset = newLocationPtr - oldLocationPoint;
1916 this.OffSetViewState(offset, flownodeMI, true);
1919 private void OnFlowchartGridDragEnter(object sender, DragEventArgs e)
1921 OnFlowchartGridDrag(sender, e);
1924 private void OnFlowchartGridDragOver(object sender, DragEventArgs e)
1926 OnFlowchartGridDrag(sender, e);
1929 private bool IsDropAllowed(DragEventArgs e)
1931 return DragDropHelper.AllowDrop(e.Data, this.Context, typeof(Activity), typeof(FlowNode), typeof(StartNode));
1934 private void OnFlowchartGridDrag(object sender, DragEventArgs e)
1938 if (!this.IsDropAllowed(e))
1940 e.Effects = DragDropEffects.None;
1946 static bool IsLinkModelItemSelectable(ModelItem linkModelItem)
1948 return linkModelItem != null &&
1949 // link from FlowDecision
1950 !typeof(FlowDecision).IsAssignableFrom(linkModelItem.ItemType) &&
1951 // link from StartNode
1952 !typeof(Flowchart).IsAssignableFrom(linkModelItem.ItemType);
1955 private void OnFlowchartGridKeyDown(object sender, KeyEventArgs e)
1957 if (srcConnectionPoint != null)
1959 // Ignore KeyBoard input when creating connector.
1964 Selection currentSelection = this.Context.Items.GetValue<Selection>();
1965 if (e.Key == Key.Delete && this.selectedConnector != null && currentSelection.SelectionCount <= 1)
1967 // process the delete if only the connector is selected
1968 ModelItem primarySelection = currentSelection.PrimarySelection;
1970 ModelItem linkModelItem = FlowchartDesigner.GetLinkModelItem(this.selectedConnector);
1971 if ((primarySelection == null && !IsLinkModelItemSelectable(linkModelItem)) ||
1972 object.Equals(primarySelection, linkModelItem))
1974 DeleteLink(this.selectedConnector);
1975 this.selectedConnector = null;
1979 else if ((new List<Key> { Key.Left, Key.Right, Key.Up, Key.Down }).Contains(e.Key)
1980 && currentSelection.SelectedObjects.All<ModelItem>((p) => { return this.modelElement.ContainsKey(p); }))
1982 KeyboardMove(e.Key);
1987 private void FlowchartDesignerKeyDown(object sender, KeyEventArgs e)
1989 // Ignore KeyBoard input when in resizing mode.
1990 e.Handled = IsResizing;
1993 private void FlowchartDesignerPreviewKeyDown(object sender, KeyEventArgs e)
1995 // Enter cannot be captured in KeyDown, so handle it in PreviewKeyDown event.
1996 e.Handled = IsResizing && e.Key == Key.Enter;
1999 void KeyboardMove(Key key)
2001 Vector moveDir = FreeFormPanel.CalculateMovement(key, this.IsRightToLeft);
2003 using (EditingScope es = (EditingScope)this.ModelItem.BeginEdit(SR.ItemMove))
2005 bool shouldStoreCurrentSizeViewState = true;
2006 foreach (ModelItem selectedModelItem in this.Context.Items.GetValue<Selection>().SelectedObjects)
2008 UIElement shapeToMove = this.modelElement[selectedModelItem];
2009 Point currentLocation = FreeFormPanel.GetLocation(shapeToMove);
2010 Point newLocation = Point.Add(currentLocation, moveDir);
2012 // Make sure the newLocation is positive.
2013 newLocation.X = FreeFormPanel.ZeroIfNegative(newLocation.X);
2014 newLocation.Y = FreeFormPanel.ZeroIfNegative(newLocation.Y);
2016 if (newLocation == currentLocation)
2021 if (shouldStoreCurrentSizeViewState)
2023 // Moving may change the size of flowchart; need this to undo the size change.
2024 this.StoreCurrentSizeViewStateWithUndo();
2025 shouldStoreCurrentSizeViewState = false;
2027 PerformInternalMove(shapeToMove, newLocation, null, AutoConnectDirections.None, null);
2033 private void OnFlowchartGridPreviewMouseDown(object sender, MouseButtonEventArgs e)
2035 this.selectedConnector = null;
2038 private void OnFlowchartGridPreviewMouseUp(object sender, MouseButtonEventArgs e)
2040 if (this.srcConnectionPoint != null)
2042 UIElement destElement = VisualTreeUtils.FindVisualAncestor<VirtualizedContainerService.VirtualizingContainer>(e.OriginalSource as DependencyObject);
2043 if (destElement != null && this.panel.Children.Contains(destElement))
2045 string errorMessage = string.Empty;
2046 Point mouseUpLocation = e.GetPosition(sender as IInputElement);
2048 if (!CreateLinkGesture(this.srcConnectionPoint, destElement, mouseUpLocation, out errorMessage, false, null) && !errorMessage.Equals(string.Empty))
2050 ErrorReporting.ShowErrorMessage(errorMessage);
2052 this.srcConnectionPoint = null;
2053 RemoveAdorner(this.panel, typeof(ConnectorCreationAdorner));
2054 RemoveAdorner(destElement, typeof(FlowchartConnectionPointsAdorner));
2055 // Simulate a MouseEnter to show connection points
2056 ChildElement_MouseEnter(destElement, null);
2063 Default = 0, ErrorConnector = 1
2066 void OnConnectNodesCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
2068 e.CanExecute = false;
2069 Selection selection = this.Context.Items.GetValue<Selection>();
2070 if (selection.SelectionCount > 1)
2072 e.CanExecute = true;
2073 foreach (ModelItem item in selection.SelectedObjects)
2075 if (!this.modelElement.ContainsKey(item))
2077 e.CanExecute = false;
2085 void OnConnectNodesCommandExecuted(object sender, ExecutedRoutedEventArgs e)
2087 using (EditingScope es = (EditingScope)this.ModelItem.BeginEdit(SR.FCCreateLink))
2089 List<ModelItem> selectedFlowchartItems = new List<ModelItem>(this.Context.Items.GetValue<Selection>().SelectedObjects);
2090 selectedFlowchartItems.Reverse();
2091 CreateLinks(selectedFlowchartItems);
2096 void OnShowAllConditionsCommandExecuted(object sender, ExecutedRoutedEventArgs e)
2098 this.ShowAllConditions = false;
2099 this.ShowAllConditions = true;
2102 void OnShowAllConditionsCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
2104 e.CanExecute = true;
2108 void OnHideAllConditionsCommandExecuted(object sender, ExecutedRoutedEventArgs e)
2110 this.ShowAllConditions = true;
2111 this.ShowAllConditions = false;
2114 void OnHideAllConditionsCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
2116 e.CanExecute = true;
2120 internal static void DropActivityBelow(ViewStateService viewStateService, ModelItem modelItem, Activity activity, double interval)
2122 // Extracting information
2123 ModelItem flowStep = modelItem.Parent;
2124 ModelItemCollection nodes = flowStep.Parent as ModelItemCollection;
2125 ModelItem flowchart = nodes.Parent;
2126 FlowchartDesigner flowchartDesigner = ((FlowchartDesigner)flowchart.View);
2128 // Creating FlowStep ModelItem
2129 ModelTreeManager modelTreeManager = (modelItem as IModelTreeItem).ModelTreeManager;
2130 FlowStep step = new FlowStep() { Action = activity };
2131 ModelItem activityModelItem = modelTreeManager.WrapAsModelItem(step);
2133 // Compute the 'correct' location
2134 Point point = (Point)viewStateService.RetrieveViewState(flowStep, shapeLocation);
2135 point.Y += (((WorkflowViewElement)modelItem.View).ActualHeight + interval);
2136 viewStateService.StoreViewState(activityModelItem, shapeLocation, point);
2137 flowchartDesigner.UpdateViewStateToAvoidOverlapOnPaste(new List<ModelItem> { activityModelItem });
2139 // Add it to the model tree
2140 nodes.Add(activityModelItem);
2143 void OnAdornerDecoratorLoaded(object sender, RoutedEventArgs e)
2145 this.adornerLayer = ((AdornerDecorator)sender).AdornerLayer;
2146 // This might not be the best event to handle, ideally we would like to have the event when the list of adorner changes.
2147 this.adornerLayer.LayoutUpdated += new EventHandler(OnAdornerLayerLayoutUpdated);
2150 void OnAdornerLayerLayoutUpdated(object sender, EventArgs e)
2152 // Extract the set of all adorners
2153 List<Adorner> adornerList = new List<Adorner>();
2154 foreach (object logicalChild in LogicalTreeHelper.GetChildren(adornerLayer))
2156 Fx.Assert(logicalChild is Adorner, "What else could an adornerLayer hold?");
2157 adornerList.Add((Adorner)logicalChild);
2159 Adorner[] adorners = adornerList.ToArray();
2160 if (FlowchartDesigner.Pack(adorners, (adorner => adorner is FlowchartExpressionAdorner)))
2162 foreach (Adorner adorner in adorners)
2164 adornerLayer.Remove(adorner);
2166 foreach (Adorner adorner in adorners)
2168 adornerLayer.Add(adorner);
2173 // do not proprogate up to FlowchartDesigner, because designer will set selection to itself on GotFocus event.
2174 private void OnAdornerLayerGotFocus(object sender, RoutedEventArgs e)
2179 // Within the connection points with least number of connectors, get the one closest to the midpoint.
2180 private static ConnectionPoint GetConnectionPointForAutoConnect(List<ConnectionPoint> availableConnectionPoints)
2182 int minConnectorCount = availableConnectionPoints.Min<ConnectionPoint>((p) =>
2184 return p.AttachedConnectors.Count;
2187 List<ConnectionPoint> connectionPoints = new List<ConnectionPoint>(availableConnectionPoints.Where<ConnectionPoint>((p) =>
2189 return p.AttachedConnectors.Count == minConnectorCount;
2192 ConnectionPoint midPoint = availableConnectionPoints[availableConnectionPoints.Count / 2];
2193 if (connectionPoints.Contains(midPoint))
2198 return ConnectionPoint.GetClosestConnectionPoint(connectionPoints, midPoint.Location, out dist);
2201 private ConnectionPoint GetSourceConnectionPointForAutoConnect(UIElement designer, EdgeLocation edgeLocation)
2203 List<ConnectionPoint> connectionPoints = FlowchartDesigner.GetAllConnectionPoints(designer);
2204 connectionPoints = new List<ConnectionPoint>(connectionPoints.Where<ConnectionPoint>((p) =>
2206 return p != null && p.PointType != ConnectionPointKind.Incoming && p.EdgeLocation == edgeLocation;
2208 Fx.Assert(connectionPoints.Count > 0, "There should be at least one src connection point available");
2209 return FlowchartDesigner.GetConnectionPointForAutoConnect(connectionPoints);
2212 internal static ConnectionPoint GetDestinationConnectionPointForAutoConnect(UIElement dest, ConnectionPoint srcConnPoint)
2214 EdgeLocation destEdgeLocation = EdgeLocation.Top;
2215 if (!((dest is VirtualizedContainerService.VirtualizingContainer) && ((VirtualizedContainerService.VirtualizingContainer)dest).ModelItem.ItemType == typeof(FlowDecision)))
2217 switch (srcConnPoint.EdgeLocation)
2219 case EdgeLocation.Top:
2220 destEdgeLocation = EdgeLocation.Bottom;
2222 case EdgeLocation.Bottom:
2223 destEdgeLocation = EdgeLocation.Top;
2225 case EdgeLocation.Left:
2226 destEdgeLocation = EdgeLocation.Right;
2228 case EdgeLocation.Right:
2229 destEdgeLocation = EdgeLocation.Left;
2233 List<ConnectionPoint> destConnectionPoints = new List<ConnectionPoint>(FlowchartDesigner.GetConnectionPoints(dest).Where<ConnectionPoint>((p) =>
2235 return p.PointType != ConnectionPointKind.Outgoing && p.EdgeLocation == destEdgeLocation;
2237 Fx.Assert(destConnectionPoints.Count > 0, "There should be at least one dest connection point available");
2238 return FlowchartDesigner.GetConnectionPointForAutoConnect(destConnectionPoints);
2241 private ModelItem GetSourceModelItemForAutoConnect(UIElement sourceElement)
2243 ModelItem sourceModelItem = null;
2244 if (sourceElement is WorkflowViewElement)
2246 sourceModelItem = ((WorkflowViewElement)sourceElement).ModelItem;
2248 else if (sourceElement is VirtualizedContainerService.VirtualizingContainer)
2250 sourceModelItem = ((VirtualizedContainerService.VirtualizingContainer)sourceElement).ModelItem;
2252 if (sourceModelItem != null && IsFlowStepAction(sourceModelItem))
2254 sourceModelItem = this.GetParentFlowStepModelItem(sourceModelItem);
2255 Fx.Assert(typeof(FlowStep).IsAssignableFrom(sourceModelItem.ItemType), "The parent should be FlowNode");
2257 return sourceModelItem;
2260 private ModelItem GetParentFlowStepModelItem(ModelItem activityModelItem)
2262 foreach (ModelItem flowNodeModelItem in this.ModelItem.Properties["Nodes"].Collection)
2264 if (typeof(FlowStep).IsAssignableFrom(flowNodeModelItem.ItemType))
2266 if (flowNodeModelItem.Properties["Action"].Value == activityModelItem)
2268 return flowNodeModelItem;
2275 public void DoAutoConnect(DragEventArgs e, UIElement targetElement, AutoConnectDirections direction)
2277 UIElement sourceElement = targetElement;
2278 bool immediatelyCommit = ModelItemHelper.CanCreateImmediateEditingScope(this.ModelItem);
2280 using (EditingScope scope = (EditingScope)this.ModelItem.BeginEdit(SR.AutoConnect, immediatelyCommit))
2282 ModelItem droppedModelItem = this.DoFlowchartGridDrop(e, direction, null);
2283 bool autoConnected = false;
2284 if (droppedModelItem != null)
2286 ModelItem sourceModelItem = this.GetSourceModelItemForAutoConnect(sourceElement);
2287 if (sourceModelItem != null)
2289 if (sourceModelItem.ItemType == typeof(FlowStep))
2291 sourceModelItem.Properties["Next"].SetValue(droppedModelItem);
2292 autoConnected = true;
2294 else if (sourceModelItem.ItemType == typeof(FlowDecision))
2296 if (direction == AutoConnectDirections.Left)
2298 sourceModelItem.Properties["True"].SetValue(droppedModelItem);
2299 autoConnected = true;
2301 else if (direction == AutoConnectDirections.Right)
2303 sourceModelItem.Properties["False"].SetValue(droppedModelItem);
2304 autoConnected = true;
2307 else if (GenericFlowSwitchHelper.IsGenericFlowSwitch(sourceModelItem.ItemType))
2309 string message = string.Empty;
2310 autoConnected = this.CreateFlowSwitchLink(this.srcConnectionPointForAutoConnect, sourceModelItem, droppedModelItem, null, null, ref message);
2312 else if (sourceModelItem.ItemType == typeof(StartNode))
2314 this.ModelItem.Properties["StartNode"].SetValue(droppedModelItem);
2315 autoConnected = true;
2321 this.srcConnectionPointForAutoConnect = this.GetSourceConnectionPointForAutoConnect(sourceElement, AutoConnectHelper.AutoConnectDirection2EdgeLocation(direction));
2331 public AutoConnectDirections GetDirectionsAllowed(DragEventArgs e, UIElement targetElement)
2333 List<Type> draggedTypes = DragDropHelper.GetDraggedTypes(e.Data);
2334 if (draggedTypes.Count != 1)
2336 return AutoConnectDirections.None;
2338 Type draggedType = draggedTypes[0];
2339 if (!typeof(Activity).IsAssignableFrom(draggedType) &&
2340 !typeof(FlowNode).IsAssignableFrom(draggedType) &&
2341 !IsActivityTemplateFactory(draggedType))
2343 return AutoConnectDirections.None;
2345 ModelItem sourceModelItem = this.GetSourceModelItemForAutoConnect(targetElement);
2346 if (sourceModelItem != null)
2348 if (sourceModelItem.ItemType == typeof(FlowStep))
2350 if (sourceModelItem.Properties["Next"].Value != null)
2352 return AutoConnectDirections.None;
2355 else if (sourceModelItem.ItemType == typeof(FlowDecision))
2357 AutoConnectDirections directions = AutoConnectDirections.None;
2358 if (sourceModelItem.Properties["True"].Value == null)
2360 directions |= AutoConnectDirections.Left;
2362 if (sourceModelItem.Properties["False"].Value == null)
2364 directions |= AutoConnectDirections.Right;
2368 else if (sourceModelItem.ItemType == typeof(StartNode))
2370 if (this.ModelItem.Properties["StartNode"].Value != null)
2372 return AutoConnectDirections.None;
2376 return AutoConnectDirections.Top | AutoConnectDirections.Bottom | AutoConnectDirections.Left | AutoConnectDirections.Right;
2379 public bool CanAutoSplit(DragEventArgs e)
2381 if (!this.IsDropAllowed(e))
2385 ModelItem draggedModelItem = e.Data.GetData(DragDropHelper.ModelItemDataFormat) as ModelItem;
2388 if (draggedModelItem is FakeModelItemImpl)
2393 if (draggedModelItem != null && this.modelElement.ContainsKey(draggedModelItem))
2395 if (this.GetAttachedConnectors(this.modelElement[draggedModelItem]).Count > 0)
2403 internal static int GetConnectionPointIndex(UIElement element, ConnectionPoint pnt)
2405 List<ConnectionPoint> list = FlowchartDesigner.GetAllConnectionPoints(element);
2406 return list.IndexOf(pnt);
2409 internal static ConnectionPoint GetConnectionPointFromIndex(UIElement element, int index)
2411 List<ConnectionPoint> list = FlowchartDesigner.GetAllConnectionPoints(element);
2412 if (index >= 0 && index < list.Count)
2419 internal UIElement GetView(ModelItem item)
2421 if (item == this.ModelItem)
2423 return this.StartSymbol;
2425 return this.modelElement[item];
2428 public void DoAutoSplit(DragEventArgs e, Connector connector)
2430 bool immediatelyCommit = ModelItemHelper.CanCreateImmediateEditingScope(this.ModelItem);
2432 using (EditingScope scope = (EditingScope)this.ModelItem.BeginEdit(SR.AutoSplit, immediatelyCommit))
2434 ModelItem droppedModelItem = this.DoFlowchartGridDrop(e, AutoConnectDirections.None, connector);
2435 bool autoSplit = false;
2436 ModelItem sourceModelItem = null;
2437 ModelItem destinationModelItem = null;
2438 if (droppedModelItem != null)
2440 this.StoreConnectorViewState(connector, true);
2441 IFlowSwitchLink flowSwitchLink = this.DeleteLink(connector, true);
2443 bool linkCreated = true;
2444 string message = string.Empty;
2445 UIElement srcDesigner = FreeFormPanel.GetSourceConnectionPoint(connector).ParentDesigner;
2446 UIElement destDesigner = FreeFormPanel.GetDestinationConnectionPoint(connector).ParentDesigner;
2447 if (srcDesigner is StartSymbol)
2449 sourceModelItem = this.ModelItem;
2450 this.ModelItem.Properties["StartNode"].SetValue(droppedModelItem);
2452 else if (srcDesigner is VirtualizedContainerService.VirtualizingContainer)
2454 sourceModelItem = ((VirtualizedContainerService.VirtualizingContainer)srcDesigner).ModelItem;
2455 ModelItem srcFlowNodeModelItem = sourceModelItem;
2456 if (!IsFlowNode(srcFlowNodeModelItem))
2458 srcFlowNodeModelItem = this.GetParentFlowStepModelItem(srcFlowNodeModelItem);
2460 Fx.Assert(IsFlowNode(srcFlowNodeModelItem), "srcFlowNodeModelItem should be a FlowNode");
2462 if (typeof(FlowStep) == srcFlowNodeModelItem.ItemType)
2464 srcFlowNodeModelItem.Properties["Next"].SetValue(droppedModelItem);
2466 else if (typeof(FlowDecision) == srcFlowNodeModelItem.ItemType && FreeFormPanel.GetSourceConnectionPoint(connector).Equals(GetTrueConnectionPoint(srcDesigner)))
2468 srcFlowNodeModelItem.Properties["True"].SetValue(droppedModelItem);
2470 else if (typeof(FlowDecision) == srcFlowNodeModelItem.ItemType && FreeFormPanel.GetSourceConnectionPoint(connector).Equals(GetFalseConnectionPoint(srcDesigner)))
2472 srcFlowNodeModelItem.Properties["False"].SetValue(droppedModelItem);
2474 else if (GenericFlowSwitchHelper.IsGenericFlowSwitch(srcFlowNodeModelItem.ItemType))
2476 linkCreated = CreateFlowSwitchLink(FreeFormPanel.GetSourceConnectionPoint(connector), srcFlowNodeModelItem, droppedModelItem, flowSwitchLink, null, ref message);
2480 if (linkCreated && string.IsNullOrEmpty(message))
2482 destinationModelItem = ((VirtualizedContainerService.VirtualizingContainer)destDesigner).ModelItem;
2483 ModelItem destFlowNodeModelItem = destinationModelItem;
2484 if (!IsFlowNode(destFlowNodeModelItem))
2486 destFlowNodeModelItem = this.GetParentFlowStepModelItem(destFlowNodeModelItem);
2489 Fx.Assert(IsFlowNode(destFlowNodeModelItem), "destFlowNodeModelItem should be a FlowNode");
2491 if (droppedModelItem.ItemType == typeof(FlowStep))
2493 droppedModelItem.Properties["Next"].SetValue(destFlowNodeModelItem);
2496 else if (GenericFlowSwitchHelper.IsGenericFlowSwitch(droppedModelItem.ItemType))
2498 droppedModelItem.Properties["Default"].SetValue(destFlowNodeModelItem);
2501 else if (droppedModelItem.ItemType == typeof(FlowDecision))
2503 droppedModelItem.Properties["True"].SetValue(destFlowNodeModelItem);
2511 Fx.Assert(sourceModelItem != null, "sourceModelItem != null");
2512 Fx.Assert(destinationModelItem != null, "destinationModelItem != null");
2514 int srcIndex = GetConnectionPointIndex(this.GetView(sourceModelItem), FreeFormPanel.GetSourceConnectionPoint(connector));
2515 int desIndex = GetConnectionPointIndex(this.GetView(destinationModelItem), FreeFormPanel.GetDestinationConnectionPoint(connector));
2517 EdgeLocation entryEdgeForAutoSplit;
2518 EdgeLocation exitEdgeForAutoSplit;
2519 AutoSplitHelper.CalculateEntryExitEdges(e.GetPosition(this.panel),
2520 connector, out entryEdgeForAutoSplit, out exitEdgeForAutoSplit);
2522 FlowchartDesigner.SetAutoSplitDataWithUndo(
2523 this.ModelItem, sourceModelItem, destinationModelItem, srcIndex, desIndex, entryEdgeForAutoSplit, exitEdgeForAutoSplit);
2534 private ConnectionPoint GetEmptyEdgeMidConnectionPointNotOfType(UIElement designer, EdgeLocation edgeLocation, ConnectionPointKind invalidType)
2536 List<ConnectionPoint> connectionPoints = FlowchartDesigner.GetAllConnectionPoints(designer);
2537 connectionPoints = new List<ConnectionPoint>(connectionPoints.Where<ConnectionPoint>((p) =>
2539 return p != null && p.PointType != invalidType && p.AttachedConnectors.Count == 0 && p.EdgeLocation == edgeLocation;
2542 if (connectionPoints.Count > 0)
2544 return connectionPoints[connectionPoints.Count / 2];
2550 private ConnectionPoint GetDestinationConnectionPointForAutoSplit(ConnectionPoint srcConnPoint, UIElement destDesigner)
2552 this.MeasureView(destDesigner);
2553 ConnectionPoint point = this.GetEmptyEdgeMidConnectionPointNotOfType(destDesigner, this.entryEdgeForAutoSplit, ConnectionPointKind.Outgoing);
2556 point = this.FindClosestConnectionPointNotOfType(srcConnPoint, new List<ConnectionPoint>(FlowchartDesigner.GetConnectionPoints(destDesigner).Where<ConnectionPoint>(p =>
2558 return p.AttachedConnectors.Count == 0;
2559 })), ConnectionPointKind.Outgoing);
2565 private ConnectionPoint GetSourceConnectionPointForAutoSplit(ConnectionPoint destConnPoint, UIElement srcDesigner)
2567 this.MeasureView(srcDesigner);
2568 ConnectionPoint point = this.GetEmptyEdgeMidConnectionPointNotOfType(srcDesigner, this.exitEdgeForAutoSplit, ConnectionPointKind.Incoming);
2571 point = this.FindClosestConnectionPointNotOfType(destConnPoint, new List<ConnectionPoint>(FlowchartDesigner.GetConnectionPoints(srcDesigner).Where<ConnectionPoint>(p =>
2573 return p.AttachedConnectors.Count == 0;
2574 })), ConnectionPointKind.Incoming);
2580 private void MeasureView(UIElement view)
2582 if (this.panel.Children.Contains(view))
2584 this.panel.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
2586 else if (VisualTreeHelper.GetParent(view) == null)
2588 StackPanel stackPanel = new StackPanel();
2589 stackPanel.Children.Add(view);
2590 if (view is VirtualizedContainerService.VirtualizingContainer)
2592 ((VirtualizedContainerService.VirtualizingContainer)view).Populate();
2594 stackPanel.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
2595 stackPanel.UpdateLayout();
2596 stackPanel.Children.Remove(view);
2597 FreeFormPanel.SetChildSize(view, view.DesiredSize);
2601 Fx.Assert(false, "The view should either be un-rooted or contained in the FreeFormPanel");
2605 private void StoreCurrentSizeViewStateWithUndo()
2607 this.ViewStateService.StoreViewStateWithUndo(
2609 FlowchartSizeFeature.WidthPropertyName,
2610 this.ViewStateService.RetrieveViewState(this.ModelItem, FlowchartSizeFeature.WidthPropertyName));
2612 this.ViewStateService.StoreViewStateWithUndo(
2614 FlowchartSizeFeature.HeightPropertyName,
2615 this.ViewStateService.RetrieveViewState(this.ModelItem, FlowchartSizeFeature.HeightPropertyName));
2618 static private bool IsFlowStepAction(ModelItem modelItem)
2620 // modelItem.CurrentValue is an Activity which is an Action of a FlowStep
2621 return typeof(Activity).IsAssignableFrom(modelItem.ItemType);
2624 static private bool IsFlowNode(ModelItem modelItem)
2626 return typeof(FlowNode).IsAssignableFrom(modelItem.ItemType);
2629 sealed private class ShapeOffsetter
2631 private Point lastShape;
2632 private bool isFirstShape = true;
2634 public Point OffsetShapeLocation(Point shapeLocation)
2636 if (this.isFirstShape)
2638 this.lastShape = shapeLocation;
2639 this.isFirstShape = false;
2640 return shapeLocation;
2643 // the shapeLocation must be at least at right-down of lastShape
2644 Double detX = shapeLocation.X - (lastShape.X + FreeFormPanel.GridSize);
2645 Double detY = shapeLocation.Y - (lastShape.Y + FreeFormPanel.GridSize);
2646 if (detX < 0 || detY < 0)
2649 // then offset shapeLocation.
2650 // offsetX and offsetY must be from Integer * FreeFormPanel.GridSize, because
2651 // shapeLocation is aligned to grid, and we expect after the offset, it is
2653 Double offsetX = Math.Ceiling(-detX / FreeFormPanel.GridSize) * FreeFormPanel.GridSize;
2654 Double offsetY = Math.Ceiling(-detY / FreeFormPanel.GridSize) * FreeFormPanel.GridSize;
2655 shapeLocation.Offset(offsetX, offsetY);
2657 this.lastShape = shapeLocation;
2658 return this.lastShape;
2662 private static void SetAutoSplitDataWithUndo(
2663 ModelItem fcModelItem,
2664 ModelItem srcModelItem,
2665 ModelItem destModelItem,
2668 EdgeLocation entryEdgeForAutoSplit,
2669 EdgeLocation exitEdgeForAutoSplit)
2671 using (EditingScope es = (EditingScope)fcModelItem.BeginEdit(SR.AutoSplit, false))
2674 new SetAutoSplitConnectionPointChange(
2680 entryEdgeForAutoSplit,
2681 exitEdgeForAutoSplit
2687 private static bool IsActivityTemplateFactory(Type type)
2689 return type.GetInterface(typeof(IActivityTemplateFactory).FullName) != null ||
2690 type.GetInterface(typeof(IActivityTemplateFactory<>).FullName) != null;
2693 // In AutoSplit, a mark,this.SrcConnPntAutoSplit & this.DestConnPntAutoSplit,
2694 // is set telling the CreateLink, which is called later in Complete(), to use AutoSplit
2695 // way to route the connector.
2696 // SetAutoSplitConnectionPointChange makes sure the mark is set during Redo.
2697 // This Change does nothing in Undo.
2698 private class SetAutoSplitConnectionPointChange : Change
2700 private ModelItem Owner { get; set; }
2701 private EdgeLocation EntryEdgeForAutoSplit { get; set; }
2702 private EdgeLocation ExitEdgeForAutoSplit { get; set; }
2703 private ModelItem SrcModelItem { get; set; }
2704 private ModelItem DestModelItem { get; set; }
2705 private int SrcConnPntIndex { get; set; }
2706 private int DestConnPntIndex { get; set; }
2708 private bool IsUndo { get; set; }
2710 public SetAutoSplitConnectionPointChange(
2711 ModelItem fcModelItem,
2712 ModelItem srcModelItem,
2713 ModelItem destModelItem,
2716 EdgeLocation entryEdgeForAutoSplit,
2717 EdgeLocation exitEdgeForAutoSplit
2720 this.Owner = fcModelItem;
2721 this.SrcModelItem = srcModelItem;
2722 this.DestModelItem = destModelItem;
2723 this.SrcConnPntIndex = srcIndex;
2724 this.DestConnPntIndex = destIndex;
2725 this.EntryEdgeForAutoSplit = entryEdgeForAutoSplit;
2726 this.ExitEdgeForAutoSplit = exitEdgeForAutoSplit;
2727 this.IsUndo = false;
2730 private SetAutoSplitConnectionPointChange()
2734 public override string Description
2736 get { return SR.AutoSplit; }
2739 public override bool Apply()
2745 FlowchartDesigner designer = this.Owner.View as FlowchartDesigner;
2746 Fx.Assert(designer != null, "null designer");
2747 UIElement srcElem = designer.GetView(this.SrcModelItem);
2748 UIElement desElem = designer.GetView(this.DestModelItem);
2749 ConnectionPoint srcConnPnt = GetConnectionPointFromIndex(srcElem, this.SrcConnPntIndex);
2750 ConnectionPoint desConnPnt = GetConnectionPointFromIndex(desElem, this.DestConnPntIndex);
2751 Fx.Assert(srcConnPnt != null, "srcConnPnt");
2752 Fx.Assert(desConnPnt != null, "desConnPnt");
2755 designer.srcConnectionPointForAutoSplit = srcConnPnt;
2756 designer.destConnectionPointForAutoSplit = desConnPnt;
2757 designer.entryEdgeForAutoSplit = this.EntryEdgeForAutoSplit;
2758 designer.exitEdgeForAutoSplit = this.ExitEdgeForAutoSplit;
2762 public override Change GetInverse()
2764 return new SetAutoSplitConnectionPointChange
2767 IsUndo = !this.IsUndo,
2768 EntryEdgeForAutoSplit = this.EntryEdgeForAutoSplit,
2769 ExitEdgeForAutoSplit = this.ExitEdgeForAutoSplit,
2770 SrcModelItem = this.SrcModelItem,
2771 DestModelItem = this.DestModelItem,
2772 SrcConnPntIndex = this.SrcConnPntIndex,
2773 DestConnPntIndex = this.DestConnPntIndex