private void OnEditingScopeCompleted(object sender, EditingScopeEventArgs e) { Fx.Assert(e.EditingScope != null, "e.EditingScope should not be null."); foreach (ModelItem removedModelItem in e.EditingScope.ItemsRemoved) { DeleteModelItem(removedModelItem); } }
/// <summary> /// Handle edit scope completion event. This happens when the XML editor buffer decides to update /// it's XDocument parse tree. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void OnUnderlyingEditCompleted(object sender, EditingScopeEventArgs e) { if (e.EditingScopeUserState != this && !synchronizing) { BufferDirty = true; dirtyTime = Environment.TickCount; } }
private void ModelTreeManager_EditingScopeCompleted(object sender, EditingScopeEventArgs e) { if (e.EditingScope.HasEffectiveChanges) { foreach (var change in e.EditingScope.Changes) { var s = change.Description; Debug.WriteLine(s); IsDirty = true; } } }
void OnEditingScopeCompleted(object sender, EditingScopeEventArgs e) { if (e.EditingScope.HasEffectiveChanges) { NotifyModelChanged(); // The undo unit of an ImmediateEditingScope is added into undo engine in ImmediateEditingScope.Complete // so we only handle non ImmediateEditingScope here if (!this.modelTreeManager.RedoUndoInProgress && !(e.EditingScope is ImmediateEditingScope) && undoEngine != null && !e.EditingScope.SuppressUndo) { undoEngine.AddUndoUnit(new EditingScopeUndoUnit(this.Context, this.modelTreeManager, e.EditingScope)); } } }
// All the connectors are directly contained by the outmost editor. This is because connectors can go across states. void OnEditingScopeCompleted(object sender, EditingScopeEventArgs e) { Debug.Assert(this.IsOutmostStateContainerEditor(), "Only the outmost editor should listen to the EditingScopeCompleted events of the model tree."); if (this.transitionModelItemsAdded.Count > 0) { // We need to wait until after the state visuals are updated this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => { foreach (ModelItem transition in this.transitionModelItemsAdded) { ModelItem srcStateModelItem = StateContainerEditor.GetParentStateModelItemForTransition(transition); this.AddTransitionVisual(transition); } this.transitionModelItemsAdded.Clear(); })); } if (this.initialStateChanged) { Debug.Assert(this.ModelItem.ItemType == typeof(StateMachine), "Only StateMachine should have initial state"); Debug.Assert(this.initialNode != null, "Initial node should not be null"); // Remove the old link if (GetAttachedConnectors(this.initialNode).Count > 0) { this.RemoveConnectorOnOutmostEditor(GetAttachedConnectors(this.initialNode)[0]); } // Add the new link if the new initial state is not null ModelItem initialStateModelItem = this.ModelItem.Properties[StateMachineDesigner.InitialStatePropertyName].Value; if (initialStateModelItem != null) { // We need to wait until after the state visuals are updated this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => { this.AddInitialNodeConnector(); })); } this.initialStateChanged = false; } }
// All the connectors are directly contained by the statemachine editor. This is because connectors can go across states. void OnEditingScopeCompleted(object sender, EditingScopeEventArgs e) { foreach (ModelItem item in e.EditingScope.ItemsRemoved) { modelItemToGuid.Remove(item); } if (ShouldSuppressAddingConnectorsWhenAddingStateVisuals(e.EditingScope)) { this.suppressAddingConnectorsWhenAddingStateVisuals = true; } else { this.suppressAddingConnectorsWhenAddingStateVisuals = false; } foreach (Change change in e.EditingScope.Changes) { if (change is PropertyChange) { PropertyChange propertyChange = change as PropertyChange; if (propertyChange.Owner.ItemType == typeof(Transition) && propertyChange.PropertyName == TransitionDesigner.ToPropertyName && propertyChange.NewValue != propertyChange.OldValue && !this.transitionModelItemsRemoved.Contains(propertyChange.Owner) && !this.transitionModelItemsAdded.Contains(propertyChange.Owner) && this.modelItemToUIElement.ContainsKey(propertyChange.NewValue)) { if (propertyChange.OldValue != null) { Connector connector = this.GetConnectorInStateMachine(propertyChange.Owner); if (connector != null) { this.Remove(connector); } } if (propertyChange.NewValue != null) { this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => { if (this.populated) { this.AddTransitionVisual(propertyChange.Owner); } })); } } } } if (!IsTransitionReordering(e.EditingScope)) { if (this.transitionModelItemsAdded.Count > 0) { // We need to wait until after the state visuals are updated this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => { if (this.populated) { foreach (ModelItem transition in this.transitionModelItemsAdded) { if (transition.Properties[TransitionDesigner.ToPropertyName].Value != null) { this.AddTransitionVisual(transition); } } } this.transitionModelItemsAdded.Clear(); })); } if (this.transitionModelItemsRemoved.Count > 0) { foreach (ModelItem transition in this.transitionModelItemsRemoved) { Connector connector = this.GetConnectorInStateMachine(transition); if (connector != null) { this.Remove(connector); } } this.transitionModelItemsRemoved.Clear(); } } if (this.initialStateChanged) { Fx.Assert(this.ModelItem.ItemType == typeof(StateMachine), "Only StateMachine should have initial state"); Fx.Assert(this.initialNode != null, "Initial node should not be null"); // Remove the old link if (GetAttachedConnectors(this.initialNode).Count > 0) { this.Remove(GetAttachedConnectors(this.initialNode)[0]); } // Add the new link if the new initial state is not null ModelItem initialStateModelItem = this.ModelItem.Properties[StateMachineDesigner.InitialStatePropertyName].Value; if (initialStateModelItem != null) { // We need to wait until after the state visuals are updated this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => { if (this.populated) { this.AddInitialNodeConnector(GetStateView(initialStateModelItem)); } })); } this.initialStateChanged = false; } }
//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); } } } } } } }