|
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.Activities.Core.Presentation
{
using System.Activities.Presentation;
using System.Activities.Presentation.FreeFormEditing;
using System.Activities.Presentation.Model;
using System.Activities.Presentation.View;
using System.Activities.Statements;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Runtime;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Threading;
using System.Windows.Input;
using System.Windows.Media;
partial class FlowchartDesigner
{
ModelItem flowStart;
const string ExpandViewStateKey = "IsExpanded";
void CreateStartSymbol()
{
//Instantiate the start symbol
StartSymbol start = System.Activities.Core.Presentation.StartSymbol.CreateStartSymbol(this.Context);
start.Text = "Start";
this.flowStart = start.ModelItem;
DragDropHelper.SetCompositeView(start, this);
modelElement.Add(flowStart, start);
start.SizeChanged += new SizeChangedEventHandler(ChildSizeChanged);
this.StartSymbol = start;
PopulateConnectionPoints(this.StartSymbol, null);
this.StartSymbol.MouseEnter += new MouseEventHandler(ChildElement_MouseEnter);
this.StartSymbol.MouseLeave += new MouseEventHandler(ChildElement_MouseLeave);
//Getting the View state information.
object locationOfShape = this.ViewStateService.RetrieveViewState(this.ModelItem, shapeLocation);
object sizeOfShape = this.ViewStateService.RetrieveViewState(this.ModelItem, shapeSize);
if (locationOfShape != null)
{
Point locationPt = (Point)locationOfShape;
FreeFormPanel.SetLocation(this.StartSymbol, locationPt);
}
else
{
//Set the location of the start symbol.
this.StartSymbol.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
double startHeight = this.StartSymbol.DesiredSize.Height;
double startWidth = this.StartSymbol.DesiredSize.Width;
Point startPoint = new Point(panel.MinWidth / 2, startSymbolTopMargin + startHeight / 2);
Point startLocation = SnapVisualToGrid(this.StartSymbol, startPoint, new Point(-1, -1), false);
FreeFormPanel.SetLocation(this.StartSymbol, startLocation);
this.internalViewStateChange = true;
this.StoreShapeViewState(this.ModelItem, startLocation);
this.internalViewStateChange = false;
}
if (sizeOfShape != null)
{
FreeFormPanel.SetChildSize(this.StartSymbol, (Size)sizeOfShape);
}
}
protected override void OnModelItemChanged(object newItem)
{
// Make flowchart designer always collapse by default, but only if the user didnt explicitly specify collapsed or expanded.
ViewStateService viewStateService = this.Context.Services.GetService<ViewStateService>();
if (viewStateService != null)
{
bool? isExpanded = (bool?)viewStateService.RetrieveViewState((ModelItem)newItem, ExpandViewStateKey);
if (isExpanded == null)
{
viewStateService.StoreViewState((ModelItem)newItem, ExpandViewStateKey, false);
}
}
base.OnModelItemChanged(newItem);
}
void OnViewStateChanged(object sender, ViewStateChangedEventArgs e)
{
Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
Fx.Assert(e.ParentModelItem != null, "ViewState should be associated with some modelItem");
Connector changedConnector = null;
if (e.ParentModelItem == this.ModelItem)
{
if (string.Equals(e.Key, FlowchartSizeFeature.WidthPropertyName, StringComparison.Ordinal))
{
this.FlowchartWidth = (double)TypeDescriptor.GetProperties(this.ModelItem)[FlowchartSizeFeature.WidthPropertyName].GetValue(this.ModelItem);
}
else if (string.Equals(e.Key, FlowchartSizeFeature.HeightPropertyName, StringComparison.Ordinal))
{
this.FlowchartHeight = (double)TypeDescriptor.GetProperties(this.ModelItem)[FlowchartSizeFeature.HeightPropertyName].GetValue(this.ModelItem);
}
}
if ((IsFlowNode(e.ParentModelItem) || this.ModelItem.Equals(e.ParentModelItem)) && !this.internalViewStateChange)
{
ModelItem itemOnCanvas = this.GetCorrespondingElementOnCanvas(e.ParentModelItem);
if (this.modelElement.ContainsKey(itemOnCanvas))
{
if (e.Key.Equals(shapeLocation))
{
if (e.NewValue != null)
{
FreeFormPanel.SetLocation(this.modelElement[itemOnCanvas], (Point)e.NewValue);
this.panel.InvalidateMeasure();
if (e.OldValue != null)
{
this.shapeLocations.Remove((Point)e.OldValue);
}
this.shapeLocations.Add((Point)e.NewValue);
}
}
else
{
if (this.ModelItem.Equals(e.ParentModelItem)
&& e.Key.Equals(ConnectorViewStateKey))
{
changedConnector = this.GetLinkOnCanvas(e.ParentModelItem, e.ParentModelItem.Properties["StartNode"].Value, "StartNode");
}
else if (typeof(FlowStep).IsAssignableFrom(e.ParentModelItem.ItemType)
&& e.Key.Equals(ConnectorViewStateKey))
{
changedConnector = this.GetLinkOnCanvas(e.ParentModelItem, e.ParentModelItem.Properties["Next"].Value, "Next");
}
else if (typeof(FlowDecision).IsAssignableFrom(e.ParentModelItem.ItemType))
{
if (e.Key.Equals(TrueConnectorViewStateKey))
{
changedConnector = this.GetLinkOnCanvas(e.ParentModelItem, e.ParentModelItem.Properties["True"].Value, "True");
}
else if (e.Key.Equals(FalseConnectorViewStateKey))
{
changedConnector = this.GetLinkOnCanvas(e.ParentModelItem, e.ParentModelItem.Properties["False"].Value, "False");
}
}
else if (GenericFlowSwitchHelper.IsGenericFlowSwitch(e.ParentModelItem.ItemType))
{
if (e.Key.Equals(FlowchartDesigner.FlowSwitchDefaultViewStateKey, StringComparison.CurrentCulture))
{
changedConnector = this.GetLinkOnCanvas(e.ParentModelItem, e.ParentModelItem.Properties["Default"].Value, e.Key);
}
else if (e.Key.EndsWith(CaseViewStateKeyAppendString, StringComparison.CurrentCulture))
{
string switchCaseName = e.Key.Substring(0, e.Key.Length - CaseViewStateKeyAppendString.Length);
object switchCase = switchCaseName;
Type genericType = e.ParentModelItem.ItemType.GetGenericArguments()[0];
switchCase = GenericFlowSwitchHelper.GetObject(switchCaseName, genericType);
if (GenericFlowSwitchHelper.ContainsCaseKey(e.ParentModelItem.Properties["Cases"], switchCase))
{
//Prepending with GenericFlowSwitchHelper.FlowSwitchCasesKeyIdentifier to differentiate between the property Default and the key Default.
changedConnector = this.GetLinkOnCanvas(e.ParentModelItem, GenericFlowSwitchHelper.GetCaseModelItem(e.ParentModelItem.Properties["Cases"], switchCase), GenericFlowSwitchHelper.FlowSwitchCasesKeyIdentifier + switchCase);
}
}
}
}
}
}
if (changedConnector != null)
{
if (e.NewValue != null)
{
Fx.Assert(e.NewValue is PointCollection, "e.NewValue is not PointCollection");
changedConnector.Points = e.NewValue as PointCollection;
this.panel.RemoveConnectorEditor();
this.panel.InvalidateMeasure();
}
}
}
void RefreshFlowSwitchLinkModelItem(ModelItem flowSwitchModelItem, Connector connector, bool isDefault)
{
ModelItem oldLinkModelItem = FlowchartDesigner.GetLinkModelItem(connector);
IModelTreeItem modelTreeItem = flowSwitchModelItem as IModelTreeItem;
IFlowSwitchLink link = GenericFlowSwitchHelper.CreateFlowSwitchLink(flowSwitchModelItem.ItemType, flowSwitchModelItem, ((IFlowSwitchLink)oldLinkModelItem.GetCurrentValue()).CaseObject, isDefault);
ModelItem linkModelItem = new FakeModelItemImpl(modelTreeItem.ModelTreeManager, link.GetType(), link, null);
link.ModelItem = linkModelItem;
FlowchartDesigner.SetLinkModelItem(connector, linkModelItem);
connector.SetBinding(Connector.LabelTextProperty, link.CreateConnectorLabelTextBinding());
Selection currentSelection = this.Context.Items.GetValue<Selection>();
if (currentSelection.SelectedObjects.Contains(oldLinkModelItem))
{
Selection.Toggle(this.Context, oldLinkModelItem);
Selection.Select(this.Context, linkModelItem);
}
}
//For flowchart reacting to ModelItem changes we are concerned of the following scenarios:
//1. FlowElements being deleted from the Flowchart.Nodes collection or Flowswitch cases being deleted from ItemsCollection
//2. FlowElements being added to the Flowchart.Nodes collection or Flowswitch cases being added from ItemsCollection
//3. Properties being changed in FlowStep(Next), FlowDecision(True, false), FlowSwitch(Default) (Any of the flowelemnet should be present in the elements collection).
//4. Flowswitch cases being added/remove via Cases.Dicitionary
void ModelTreeManager_EditingScopeCompleted(object sender, EditingScopeEventArgs e)
{
Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
foreach (Change change in e.EditingScope.Changes)
{
//Case 1, 2.
if (change is CollectionChange)
{
CollectionChange collectionChange = change as CollectionChange;
if (collectionChange.Collection.Equals(this.ModelItem.Properties["Nodes"].Collection))
{
if (collectionChange.Operation == CollectionChange.OperationType.Delete)
{
this.DeleteShapeVisual(this.flowNodeToUIElement[collectionChange.Item]);
}
else
{
this.AddFlowElementsToDesigner(new List<ModelItem> { collectionChange.Item });
//An editing scope change references the ModelItem.
//Hence in case of multiple changes to the same modelItem within the same EditingScope, we will see all the changes on the ModelItem for each change.
//Eg. Suppose following two changes are in the same editing scope: 1. Add ModelItem item1 to Collection, 2. Change a property on this MI, item1.Prop1
//In this case, EditingScope.Changes.Count will be 2.
//Since an EditingScope change keeps a reference to the ModelItem changed, when we process the first change, the second change would already be reflected on the ModelItem.
//Hence, while processing CollectionChange for item1, item1.Prop1 will already reflect the new value.
//Also there will be another change notifying the change in item1.Prop1.
//AddFlowElementsToDesigner() method, walks through the properties of a newly added item and creates any links if required.
//This is necessary for Paste scenario where we want to create links between Items added to the Nodes Collection.
//Because of this behavior of AddFlowElementsToDesigner(), before reacting to a property change for adding a link, we will always verify that the link does not already exists.
}
}
if (collectionChange.Collection.Parent != null && collectionChange.Collection.Parent.Parent != null &&
this.ModelItem.Properties["Nodes"].Collection.Contains(collectionChange.Collection.Parent.Parent) &&
collectionChange.Collection.Parent.Parent.ItemType.IsGenericType &&
collectionChange.Collection.Parent.Parent.ItemType.GetGenericTypeDefinition() == typeof(FlowSwitch<>))
{
ModelItem item = collectionChange.Item;
string caseName = GenericFlowSwitchHelper.GetString(item.Properties["Key"].ComputedValue, item.Properties["Key"].PropertyType);
Connector connector = this.GetLinkOnCanvas(collectionChange.Collection.Parent.Parent,
item.Properties["Value"].Value, GenericFlowSwitchHelper.FlowSwitchCasesKeyIdentifier + caseName);
if (collectionChange.Operation == CollectionChange.OperationType.Delete)
{
if (connector != null)
{
this.DeleteLinkVisual(connector);
}
}
else if (collectionChange.Operation == CollectionChange.OperationType.Insert)
{
if (connector == null)
{
//Prepending GenericFlowSwitchHelper.FlowSwitchCasesKeyIdentifier to differentiate between the FlowSwitch's Property Default and key Default.
connector = this.CreatePropertyLink(collectionChange.Collection.Parent.Parent,
item.Properties["Value"].Value,
GenericFlowSwitchHelper.FlowSwitchCasesKeyIdentifier + caseName);
Fx.Assert(connector != null, "Link not created");
this.panel.Children.Add(connector);
}
else
{
RefreshFlowSwitchLinkModelItem(/* flowSwitchModelItem = */ collectionChange.Collection.Parent.Parent, connector, false);
}
}
}
}
else if (change is DictionaryChange)
{
// case 4
DictionaryChange dictionaryChange = change as DictionaryChange;
if (dictionaryChange.Dictionary.Parent != null &&
this.ModelItem.Properties["Nodes"].Collection.Contains(dictionaryChange.Dictionary.Parent) &&
dictionaryChange.Dictionary.Parent.ItemType.IsGenericType &&
dictionaryChange.Dictionary.Parent.ItemType.GetGenericTypeDefinition() == typeof(FlowSwitch<>))
{
ModelItem flowSwitchModelItem = dictionaryChange.Dictionary.Parent;
ModelItem caseTargetModelItem = dictionaryChange.Value;
string caseName = GenericFlowSwitchHelper.GetString(dictionaryChange.Key == null ? null : dictionaryChange.Key.GetCurrentValue(), dictionaryChange.Key == null ? null : dictionaryChange.Key.ItemType);
string caseNameInModelItem = GenericFlowSwitchHelper.FlowSwitchCasesKeyIdentifier + caseName;
Connector connector = this.GetLinkOnCanvas(
flowSwitchModelItem,
caseTargetModelItem,
caseNameInModelItem);
if (dictionaryChange.Operation == DictionaryChange.OperationType.Delete)
{
if (connector != null)
{
this.DeleteLinkVisual(connector);
}
}
else if (dictionaryChange.Operation == DictionaryChange.OperationType.Insert)
{
if (connector == null)
{
connector = this.CreatePropertyLink(
flowSwitchModelItem,
caseTargetModelItem,
caseNameInModelItem);
this.panel.Children.Add(connector);
}
}
}
}
//Case 3.
else if (change is PropertyChange)
{
PropertyChange propertyChange = change as PropertyChange;
if (this.ModelItem.Properties["Nodes"].Collection.Contains(propertyChange.Owner)
|| (propertyChange.PropertyName == "StartNode" && propertyChange.Owner == this.ModelItem))
{
if (propertyChange.OldValue != null
&& IsFlowNode(propertyChange.OldValue))
{
Connector link = GetLinkOnCanvas(propertyChange.Owner, propertyChange.OldValue, propertyChange.PropertyName);
//Debug.Assert(link != null, "Link not found on designer");
if (link != null)
{
this.DeleteLinkVisual(link);
}
}
if (propertyChange.NewValue != null
&& IsFlowNode(propertyChange.NewValue))
{
Connector oldLink = GetLinkOnCanvas(propertyChange.Owner, propertyChange.NewValue, propertyChange.PropertyName);
//If this connector has already been added don't add again.
if (oldLink == null)
{
Connector link = CreatePropertyLink(propertyChange.Owner, propertyChange.NewValue, propertyChange.PropertyName);
Fx.Assert(link != null, "Link not created");
this.panel.Children.Add(link);
}
else
{
if (GenericFlowSwitchHelper.IsGenericFlowSwitch(propertyChange.Owner.ItemType))
{
this.RefreshFlowSwitchLinkModelItem(/* flowSwitchModelItem = */ propertyChange.Owner, oldLink, true);
}
}
}
//handling for the case where the FlowStep.Action changes:
//Explicitly adding a check for FlowStep, because other FlowNodes have properties of type Activity, which we don't want to react to.
//AddFlowElementsToDesigner() will add the links originating out of the shape that is changing.
//We have to take care of refreshing the links coming into the shape that is changing.
if (typeof(FlowStep).IsAssignableFrom(propertyChange.Owner.ItemType))
{
List<Connector> oldIncomingConnectors = new List<Connector>();
if (propertyChange.OldValue != null && IsFlowStepAction(propertyChange.OldValue))
{
UIElement oldShape = this.flowNodeToUIElement[propertyChange.Owner];
oldIncomingConnectors = this.GetInComingConnectors(oldShape);
this.DeleteShapeVisual(oldShape);
}
if (propertyChange.NewValue != null && IsFlowStepAction(propertyChange.NewValue))
{
this.AddFlowElementsToDesigner(new List<ModelItem> { propertyChange.Owner });
foreach (Connector oldConnector in oldIncomingConnectors)
{
Connector newConnector = CreateLink(FreeFormPanel.GetSourceConnectionPoint(oldConnector),
this.flowNodeToUIElement[propertyChange.Owner], FlowchartDesigner.GetLinkModelItem(oldConnector));
this.panel.Children.Add(newConnector);
}
}
}
}
}
}
}
void AddFlowElementsToDesigner(IList<ModelItem> flowElementMICollection, bool addConnectorAfterLoaded = false)
{
Queue<ModelItem> flowElementsToProcess = new Queue<ModelItem>();
List<UIElement> viewsAdded = new List<UIElement>();
foreach (ModelItem model in flowElementMICollection)
{
ModelItem itemOnCanvas = GetCorrespondingElementOnCanvas(model);
if (!this.modelElement.ContainsKey(itemOnCanvas))
{
flowElementsToProcess.Enqueue(model);
viewsAdded.Add(ProcessAndGetModelView(itemOnCanvas));
}
else if (!this.panel.Children.Contains(this.modelElement[itemOnCanvas]))
{
flowElementsToProcess.Enqueue(model);
viewsAdded.Add(this.modelElement[itemOnCanvas]);
}
}
ModelItem startNodeModelItem = null;
List<Tuple<UIElement, UIElement, ModelItem>> elem2elemConnections = new List<Tuple<UIElement, UIElement, ModelItem>>();
List<Tuple<ConnectionPoint, UIElement, ModelItem>> point2elemConnections = new List<Tuple<ConnectionPoint, UIElement, ModelItem>>();
while (flowElementsToProcess.Count > 0)
{
ModelItem currentMI = flowElementsToProcess.Dequeue();
//Create links for the current FlowNode.
//First of all check if this is connected to the start node.
if (this.ModelItem.Properties["StartNode"].Value != null
&& this.ModelItem.Properties["StartNode"].Value.Equals(currentMI))
{
startNodeModelItem = currentMI;
}
if (typeof(FlowStep).IsAssignableFrom(currentMI.ItemType))
{
ModelItem linkDest = currentMI.Properties["Next"].Value;
if (linkDest != null)
{
ModelItem src = GetCorrespondingElementOnCanvas(currentMI);
ModelItem dest = GetCorrespondingElementOnCanvas(linkDest);
if (!modelElement.ContainsKey(dest))
{
viewsAdded.Add(ProcessAndGetModelView(dest));
flowElementsToProcess.Enqueue(linkDest);
}
elem2elemConnections.Add(Tuple.Create(modelElement[src], modelElement[dest], currentMI));
}
}
else if (typeof(FlowDecision).IsAssignableFrom(currentMI.ItemType))
{
ModelItem trueDest = currentMI.Properties["True"].Value;
ModelItem falseDest = currentMI.Properties["False"].Value;
if (trueDest != null)
{
ConnectionPoint srcConnectionPoint = FlowchartDesigner.GetTrueConnectionPoint(modelElement[currentMI]);
ModelItem trueDestOnCanvas = GetCorrespondingElementOnCanvas(trueDest);
if (!modelElement.ContainsKey(trueDestOnCanvas))
{
viewsAdded.Add(ProcessAndGetModelView(trueDestOnCanvas));
flowElementsToProcess.Enqueue(trueDest);
}
point2elemConnections.Add(Tuple.Create(srcConnectionPoint, modelElement[trueDestOnCanvas], currentMI));
}
if (falseDest != null)
{
ConnectionPoint srcConnectionPoint = FlowchartDesigner.GetFalseConnectionPoint(modelElement[currentMI]);
ModelItem falseDestOnCanvas = GetCorrespondingElementOnCanvas(falseDest);
if (!modelElement.ContainsKey(falseDestOnCanvas))
{
viewsAdded.Add(ProcessAndGetModelView(falseDestOnCanvas));
flowElementsToProcess.Enqueue(falseDest);
}
point2elemConnections.Add(Tuple.Create(srcConnectionPoint, modelElement[falseDestOnCanvas], currentMI));
}
}
else if (GenericFlowSwitchHelper.IsGenericFlowSwitch(currentMI.ItemType))
{
IModelTreeItem modelTreeItem = this.ModelItem as IModelTreeItem;
ModelItem defaultCase = currentMI.Properties["Default"].Value;
if (defaultCase != null)
{
ModelItem defaultCaseOnCanvas = GetCorrespondingElementOnCanvas(defaultCase);
if (!modelElement.ContainsKey(defaultCaseOnCanvas))
{
viewsAdded.Add(ProcessAndGetModelView(defaultCaseOnCanvas));
flowElementsToProcess.Enqueue(defaultCase);
}
IFlowSwitchLink link = GenericFlowSwitchHelper.CreateFlowSwitchLink(currentMI.ItemType, currentMI, null, true);
ModelItem linkModelItem = new FakeModelItemImpl(modelTreeItem.ModelTreeManager, link.GetType(), link, null);
link.ModelItem = linkModelItem;
elem2elemConnections.Add(Tuple.Create(modelElement[currentMI], modelElement[defaultCaseOnCanvas], linkModelItem));
}
Type genericType = currentMI.ItemType.GetGenericArguments()[0];
foreach (ModelItem key in GenericFlowSwitchHelper.GetCaseKeys(currentMI.Properties["Cases"]))
{
ModelItem destFlowElementMI = GenericFlowSwitchHelper.GetCaseModelItem(currentMI.Properties["Cases"], (key == null) ? null : key.GetCurrentValue());
IFlowSwitchLink link = GenericFlowSwitchHelper.CreateFlowSwitchLink(currentMI.ItemType, currentMI, (key == null) ? null : key.GetCurrentValue(), false);
ModelItem linkModelItem = new FakeModelItemImpl(modelTreeItem.ModelTreeManager, link.GetType(), link, null);
link.ModelItem = linkModelItem;
ModelItem destModelItem = GetCorrespondingElementOnCanvas(destFlowElementMI);
if (!modelElement.ContainsKey(destModelItem))
{
viewsAdded.Add(ProcessAndGetModelView(destModelItem));
flowElementsToProcess.Enqueue(destFlowElementMI);
}
elem2elemConnections.Add(Tuple.Create(modelElement[currentMI], modelElement[destModelItem], linkModelItem));
}
}
else
{
Fx.Assert(false, "Unknown type of FlowNode");
}
}
if (!this.startNodeAdded)
{
panel.Children.Add(this.StartSymbol);
this.startNodeAdded = true;
}
foreach (UIElement view in viewsAdded)
{
panel.Children.Add(view);
}
// connection between flownode should be create only after all flownodes have been loaded on the canvas
if (addConnectorAfterLoaded)
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
{
if (this.isLoaded)
{
AddConnectorsToPanel(startNodeModelItem, elem2elemConnections, point2elemConnections);
}
}));
}
else
{
AddConnectorsToPanel(startNodeModelItem, elem2elemConnections, point2elemConnections);
}
}
private void AddConnectorsToPanel(
ModelItem startNodeModelItem,
List<Tuple<UIElement, UIElement, ModelItem>> elem2elemConnections,
List<Tuple<ConnectionPoint, UIElement, ModelItem>> point2elemConnections)
{
List<Connector> connectorList = new List<Connector>();
if (startNodeModelItem != null)
{
ModelItem dest = GetCorrespondingElementOnCanvas(startNodeModelItem);
connectorList.Add(CreateLink(this.StartSymbol, modelElement[dest], this.ModelItem));
}
foreach (var connection in elem2elemConnections)
{
connectorList.Add(CreateLink(connection.Item1, connection.Item2, connection.Item3));
}
foreach (var connection in point2elemConnections)
{
connectorList.Add(CreateLink(connection.Item1, connection.Item2, connection.Item3));
}
foreach (Connector connector in connectorList)
{
panel.Children.Add(connector);
}
}
void DeleteLinkVisual(Connector link)
{
ConnectionPoint srcConnectionPoint = FreeFormPanel.GetSourceConnectionPoint(link);
ConnectionPoint destConnectionPoint = FreeFormPanel.GetDestinationConnectionPoint(link);
//Update ConnectionPoints.
srcConnectionPoint.AttachedConnectors.Remove(link);
destConnectionPoint.AttachedConnectors.Remove(link);
this.panel.Children.Remove(link);
}
void DeleteShapeVisual(UIElement deleteShape)
{
//Remove any link visuals attached to this shape. This is required for the scenarios as follows:
//Copy paste two Connected activities into flowchart and undo the paste.
//The property is not removed as a model change. Hence the link visual will remain dangling on the designer.
List<Connector> attachedConnectors = GetAttachedConnectors(deleteShape);
foreach (Connector connector in attachedConnectors)
{
Fx.Assert(this.panel.Children.Contains(connector), "Connector does not exist");
this.DeleteLinkVisual(connector);
}
List<ConnectionPoint> connectionPoints = GetConnectionPoints(deleteShape);
if (connectionPoints.Contains(this.srcConnectionPoint))
{
this.srcConnectionPoint = null;
RemoveAdorner(this.panel, typeof(ConnectorCreationAdorner));
}
ModelItem shapeModelItem = ((VirtualizedContainerService.VirtualizingContainer)deleteShape).ModelItem;
ModelItem flowNodeMI = this.GetFlowElementMI(shapeModelItem);
this.modelElement.Remove(shapeModelItem);
this.flowNodeToUIElement.Remove(flowNodeMI);
deleteShape.MouseEnter -= new MouseEventHandler(ChildElement_MouseEnter);
deleteShape.MouseLeave -= new MouseEventHandler(ChildElement_MouseLeave);
((FrameworkElement)deleteShape).SizeChanged -= new SizeChangedEventHandler(ChildSizeChanged);
this.panel.Children.Remove(deleteShape);
// deselect removed item
if (this.Context != null)
{
HashSet<ModelItem> selectedItems = new HashSet<ModelItem>(this.Context.Items.GetValue<Selection>().SelectedObjects);
if (selectedItems.Contains(shapeModelItem))
{
Selection.Toggle(this.Context, shapeModelItem);
}
}
//Update this.shapeLocations.
object locationOfShape = this.ViewStateService.RetrieveViewState(flowNodeMI, shapeLocation);
if (locationOfShape != null)
{
this.shapeLocations.Remove((Point)locationOfShape);
}
}
Connector CreatePropertyLink(ModelItem srcModelItem, ModelItem propertyValue, string propertyName)
{
Connector newConnector = null;
if (typeof(FlowStep).IsAssignableFrom(srcModelItem.ItemType))
{
ModelItem src = GetCorrespondingElementOnCanvas(srcModelItem);
ModelItem dest = GetCorrespondingElementOnCanvas(propertyValue);
newConnector = CreateLink(modelElement[src], modelElement[dest], srcModelItem);
}
else if (typeof(FlowDecision).IsAssignableFrom(srcModelItem.ItemType))
{
ModelItem dest = GetCorrespondingElementOnCanvas(propertyValue);
ConnectionPoint srcConnPoint;
if (propertyName.Equals("True"))
{
srcConnPoint = FlowchartDesigner.GetTrueConnectionPoint(modelElement[srcModelItem]);
}
else
{
srcConnPoint = FlowchartDesigner.GetFalseConnectionPoint(modelElement[srcModelItem]);
}
newConnector = CreateLink(srcConnPoint, modelElement[dest], srcModelItem);
}
else if (GenericFlowSwitchHelper.IsGenericFlowSwitch(srcModelItem.ItemType))
{
ModelItem dest = GetCorrespondingElementOnCanvas(propertyValue);
IFlowSwitchLink link;
if (propertyName.Equals("Default"))
{
link = GenericFlowSwitchHelper.CreateFlowSwitchLink(srcModelItem.ItemType, srcModelItem, null, true);
}
else
{
Fx.Assert(propertyName.Length >= GenericFlowSwitchHelper.FlowSwitchCasesKeyIdentifier.Length, "Case property names should be prepended by the string GenericFlowSwitchHelper.FlowSwitchCasesKeyIdentifier");
link = GenericFlowSwitchHelper.CreateFlowSwitchLink(srcModelItem.ItemType, srcModelItem, propertyName.Substring(GenericFlowSwitchHelper.FlowSwitchCasesKeyIdentifier.Length), false);
}
IModelTreeItem modelTreeItem = (IModelTreeItem)this.ModelItem;
ModelItem linkModelItem = new FakeModelItemImpl(modelTreeItem.ModelTreeManager, link.GetType(), link, null);
link.ModelItem = linkModelItem;
newConnector = CreateLink(modelElement[srcModelItem], modelElement[dest], linkModelItem);
}
else // FlowStart
{
ModelItem dest = GetCorrespondingElementOnCanvas(propertyValue);
newConnector = CreateLink(this.StartSymbol, modelElement[dest], this.ModelItem);
}
return newConnector;
}
internal Connector GetLinkOnCanvas(ModelItem srcFlowElementModelItem, ModelItem destflowElementModelItem, string propertyName)
{
Connector linkOnCanvas = null;
ModelItem shapeModelItem = null;
List<Connector> outGoingConnectors = null;
if (!srcFlowElementModelItem.Equals(this.ModelItem))
{
shapeModelItem = this.GetCorrespondingElementOnCanvas(srcFlowElementModelItem);
outGoingConnectors = GetOutGoingConnectors(this.modelElement[shapeModelItem]);
}
else // Must be startNode
{
outGoingConnectors = GetOutGoingConnectors(this.StartSymbol);
}
foreach (Connector connector in outGoingConnectors)
{
ModelItem connectorDestModelItem = ((VirtualizedContainerService.VirtualizingContainer)FreeFormPanel.GetDestinationConnectionPoint(connector).ParentDesigner).ModelItem;
ModelItem connectorDestFlowElementMI = this.GetFlowElementMI(connectorDestModelItem);
//Following condition checks if the destination for current connector is equal to the destination passed in.
if (destflowElementModelItem != null && destflowElementModelItem.Equals(connectorDestFlowElementMI))
{
if (GenericFlowSwitchHelper.IsGenericFlowSwitch(srcFlowElementModelItem.ItemType))
{
ModelItem linkModelItem = FlowchartDesigner.GetLinkModelItem(connector);
if (linkModelItem.Properties["IsDefaultCase"].Value.GetCurrentValue().Equals(true) && propertyName.Equals("Default"))
{
linkOnCanvas = connector;
break;
}
else
{
ModelItem connectorCaseMI = linkModelItem.Properties["Case"].Value;
if (linkModelItem.Properties["IsDefaultCase"].Value.GetCurrentValue().Equals(false))
{
string caseName = connectorCaseMI == null ? null : GenericFlowSwitchHelper.GetString(connectorCaseMI.GetCurrentValue(), connectorCaseMI.ItemType);
if (connectorCaseMI != null && caseName.Equals(propertyName.Substring(GenericFlowSwitchHelper.FlowSwitchCasesKeyIdentifier.Length)))
{
linkOnCanvas = connector;
break;
}
else if (connectorCaseMI == null)
{
if (GenericFlowSwitchHelper.FlowSwitchNullCaseKeyIdentifier.Equals(propertyName.Substring(GenericFlowSwitchHelper.FlowSwitchCasesKeyIdentifier.Length)))
{
linkOnCanvas = connector;
break;
}
}
}
}
}
else if (typeof(FlowDecision).IsAssignableFrom(srcFlowElementModelItem.ItemType))
{
ConnectionPoint trueConnPoint = FlowchartDesigner.GetTrueConnectionPoint(this.modelElement[shapeModelItem]);
ConnectionPoint falseConnPoint = FlowchartDesigner.GetFalseConnectionPoint(this.modelElement[shapeModelItem]);
ConnectionPoint connectorSrcConnPoint = FreeFormPanel.GetSourceConnectionPoint(connector);
if ((propertyName.Equals("True") && connectorSrcConnPoint.Equals(trueConnPoint))
|| (propertyName.Equals("False") && connectorSrcConnPoint.Equals(falseConnPoint)))
{
linkOnCanvas = connector;
break;
}
}
else //FlowStep case.
{
linkOnCanvas = connector;
break;
}
}
}
return linkOnCanvas;
}
//Finally the call to CreateLink ends in calling this overloaded method.
Connector CreateLink(ConnectionPoint sourceConnectionPoint, ConnectionPoint destConnectionPoint, ModelItem linkModelItem)
{
Fx.Assert(sourceConnectionPoint != null, "sourceConnectionPoint is null.");
Fx.Assert(destConnectionPoint != null, "destinationConnectionPoint is null.");
Connector newConnector = null;
if (destConnectionPoint.PointType != ConnectionPointKind.Outgoing && sourceConnectionPoint.PointType != ConnectionPointKind.Incoming)
{
newConnector = GetConnectorViewState(sourceConnectionPoint.ParentDesigner, destConnectionPoint.ParentDesigner, linkModelItem, sourceConnectionPoint);
if (newConnector == null)
{
newConnector = GetConnector(linkModelItem, sourceConnectionPoint, destConnectionPoint);
}
else
{
//This is a workaround for CSDMain 139197, if any sectment of a connector is neither vertical nor horizontal, we'll reroute it.
RerouteIfInvalid(newConnector, linkModelItem);
}
Fx.Assert(newConnector != null, "Link could not be created");
}
return newConnector;
}
Connector CreateLink(ConnectionPoint sourceConnectionPoint, UIElement dest, ModelItem linkModelItem)
{
Connector newConnector = null;
ConnectionPoint destConnectionPoint = null;
if (this.srcConnectionPointForAutoConnect != null)
{
Fx.Assert(this.srcConnectionPointForAutoConnect == sourceConnectionPoint, "sourceConnectionPoint should equal to this.srcConnectionPointForAutoConnect");
destConnectionPoint = FlowchartDesigner.GetDestinationConnectionPointForAutoConnect(dest, sourceConnectionPoint);
this.srcConnectionPointForAutoConnect = null;
}
else if (this.srcConnectionPointForAutoSplit == sourceConnectionPoint)
{
destConnectionPoint = this.GetDestinationConnectionPointForAutoSplit(this.srcConnectionPointForAutoSplit, dest);
this.srcConnectionPointForAutoSplit = null;
}
else
{
string errorMessage;
destConnectionPoint = FindBestMatchDestConnectionPoint(sourceConnectionPoint, dest, out errorMessage);
}
if (destConnectionPoint != null)
{
newConnector = CreateLink(sourceConnectionPoint, destConnectionPoint, linkModelItem);
}
return newConnector;
}
Connector CreateLink(UIElement source, UIElement dest, ModelItem linkModelItem)
{
Connector newConnector = null;
ConnectionPoint srcConnPoint = null, destConnPoint = null;
if (this.srcConnectionPointForAutoConnect != null)
{
srcConnPoint = this.srcConnectionPointForAutoConnect;
destConnPoint = FlowchartDesigner.GetDestinationConnectionPointForAutoConnect(dest, srcConnPoint);
this.srcConnectionPointForAutoConnect = null;
}
else if (this.srcConnectionPointForAutoSplit != null && this.srcConnectionPointForAutoSplit.ParentDesigner == source)
{
srcConnPoint = this.srcConnectionPointForAutoSplit;
destConnPoint = this.GetDestinationConnectionPointForAutoSplit(srcConnPoint, dest);
this.srcConnectionPointForAutoSplit = null;
}
else if (this.destConnectionPointForAutoSplit != null && this.destConnectionPointForAutoSplit.ParentDesigner == dest)
{
destConnPoint = this.destConnectionPointForAutoSplit;
srcConnPoint = this.GetSourceConnectionPointForAutoSplit(destConnPoint, source);
this.destConnectionPointForAutoSplit = null;
}
else
{
string errorMessage;
GetSrcDestConnectionPoints(source, dest, out srcConnPoint, out destConnPoint, out errorMessage);
}
if (srcConnPoint != null && destConnPoint != null)
{
newConnector = CreateLink(srcConnPoint, destConnPoint, linkModelItem);
}
return newConnector;
}
[SuppressMessage(FxCop.Category.Usage, FxCop.Rule.ReviewUnusedParameters, Justification = "Existing code")]
void RerouteIfInvalid(Connector connector, ModelItem linkModelItem)
{
if (connector.Points != null)
{
Point[] points = new Point[connector.Points.Count];
connector.Points.CopyTo(points, 0);
if (!ConnectorRouter.AreSegmentsValid(points))
{
Reroute(connector, false);
}
}
}
}
}
|