internal /*?? protected */ virtual void ChangeModelState(ModelChangedEventArgs e, bool undo) { if ((int)e.Change > (int)ModelChange.Property) { ModelHelper.Error((IDiagramModel)this, "ChangeModelState did not handle ModelChange." + e.Change.ToString()); } }
/// <summary> /// Raise the <see cref="PropertyChanged"/> event. /// </summary> /// <param name="e"></param> public virtual void OnPropertyChanged(ModelChangedEventArgs e) { if (this.PropertyChanged != null) { this.PropertyChanged(this, e); } }
/// <summary> /// This is called during undo or redo to effect state changes to this model. /// </summary> /// <param name="e">an edit describing the change to be performed</param> /// <param name="undo">true if undoing; false if redoing</param> /// <remarks> /// <para> /// This is called by <see cref="ChangeModel"/>. /// You will want to override this method to handle properties that you /// have added to your derived model class. /// </para> /// <para> /// By default this uses reflection to set the <see cref="PropertyChangedEventArgs.PropertyName"/> /// to the <see cref="ModelChangedEventArgs.OldValue"/> or the /// <see cref="ModelChangedEventArgs.NewValue"/>, depending on the value of <paramref name="undo"/>. /// </para> /// <para> /// If you override this method, remember to call the base method for all /// cases that your override method does not handle. /// </para> /// </remarks> protected virtual void ChangeModelValue(ModelChangedEventArgs e, bool undo) { if (e == null) { return; } if (e.PropertyName == "Name") { this.Name = (String)e.GetValue(undo); } else if (e.PropertyName == "DataFormat") { this.DataFormat = (String)e.GetValue(undo); } else if (e.PropertyName == "Modifiable") { this.Modifiable = (bool)e.GetValue(undo); } else if (ModelHelper.SetProperty(e.PropertyName, this, e.GetValue(undo))) { return; // successful set of model property } else { ModelHelper.Error((IDiagramModel)this, "Override ChangeModelValue to handle ModelChangedEventArgs of a model property: " + e.ToString()); } }
/// <summary> /// This is called during an Undo or a Redo to actually make state /// changes to this model or to this model's data. /// </summary> /// <param name="e">an edit describing the change to be performed</param> /// <param name="undo">true if undoing; false if redoing</param> /// <remarks> /// <para> /// When <paramref name="e"/>'s <see cref="ModelChangedEventArgs.Change"/> /// value is <see cref="ModelChange.Property"/>, /// this calls <see cref="ChangeModelValue"/> /// if the <see cref="ModelChangedEventArgs.Data"/> is this model, /// or else it calls <see cref="ChangeDataValue"/>. /// </para> /// <para> /// This method handles all other <see cref="ModelChange"/> cases, /// since they are all predefined. /// </para> /// </remarks> public void ChangeModel(ModelChangedEventArgs e, bool undo) { if (e == null) { return; } bool old = this.IsChangingModel; //ModelHelper.Trace((undo ? "undo: " : "redo: ") + e.ToString()); try { this.IsChangingModel = true; if (e.Change == ModelChange.Property) { Object data = e.Data; if (data == null) { data = this; } if (data == this) // changes to programmer defined properties on the model { ChangeModelValue(e, undo); } else // changes to some data inside the model { ChangeDataValue(e, undo); } } else { ChangeModelState(e, undo); } } finally { this.IsChangingModel = old; } }
/// <summary> /// Create an <see cref="IUndoableEdit"/> for a <see cref="IDiagramModel.Changed"/> event. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> /// <remarks> /// This calls <see cref="SkipEvent"/> if for some reason we should ignore /// the <paramref name="e"/>. /// This then creates a <see cref="ModelChangedEventArgs"/> and adds it to the /// <see cref="CurrentEdit"/>, a <see cref="UndoManager.CompoundEdit"/> which it allocates /// if needed. /// This method always ignores all Changed events while we are performing an /// <see cref="Undo"/> or <see cref="Redo"/>. /// </remarks> public virtual void HandleModelChanged(Object sender, ModelChangedEventArgs e) { // changes caused by performing an undo or redo should be ignored! if (this.IsUndoingRedoing) { return; } if (!SkipEvent(e)) { CompoundEdit cedit = this.CurrentEdit; //ModelHelper.Trace(this.TransactionLevel, e.ToString()); if (cedit == null) { cedit = new CompoundEdit(); this.CurrentEdit = cedit; } // make a copy of the event to save as an edit in the list ModelChangedEventArgs edit = new ModelChangedEventArgs(e); cedit.Edits.Add(edit); if (this.ChecksTransactionLevel && this.TransactionLevel <= 0) { ModelHelper.Trace("Change not within a transaction: " + edit.ToString()); } } }
/// <summary> /// Raise a <see cref="Changed"/> event, given before and after values for a particular property. /// </summary> /// <param name="propname">a property name</param> /// <param name="data">the object whose property has just changed</param> /// <param name="oldval">the previous value for the property</param> /// <param name="newval">the new value for the property</param> /// <remarks> /// This is the mostly commonly used way to notify about changes to the model or to the model's data. /// </remarks> /// <seealso cref="RaiseChanged(ModelChangedEventArgs)"/> /// <seealso cref="RaisePropertyChanged(String, Object, Object, Object, Object, Object)"/> public void RaisePropertyChanged(String propname, Object data, Object oldval, Object newval) { ModelChangedEventArgs e = new ModelChangedEventArgs(propname, data, oldval, newval); e.Model = (IDiagramModel)this; e.Change = ModelChange.Property; OnChanged(e); }
/// <summary> /// This predicate is responsible for deciding if a <see cref="ModelChangedEventArgs"/> /// is not interesting enough to be recorded. /// </summary> /// <param name="evt"></param> /// <returns>normally false, which causes the given event to be remembered; /// but true for negative valued enumerations of <see cref="ModelChange"/>.</returns> /// <remarks> /// </remarks> protected virtual bool SkipEvent(ModelChangedEventArgs evt) { if (evt == null) { return(true); } return((int)evt.Change < (int)ModelChange.Property); }
/// <summary> /// This method implements the <see cref="IChangeDataValue"/> interface, /// used to perform state changes for undo and redo. /// </summary> /// <param name="e">an edit describing the change to be performed</param> /// <param name="undo">true if undoing; false if redoing</param> /// <remarks> /// Unless you override this method to explicitly handle each property that you define, /// this implementation uses reflection to set the property. /// </remarks> public virtual void ChangeDataValue(ModelChangedEventArgs e, bool undo) { if (e == null) { return; } if (e.PropertyName == "Location") { this.Location = (Point)e.GetValue(undo); } else if (e.PropertyName == "Text") { this.Text = (String)e.GetValue(undo); } else if (e.PropertyName == "Key") { this.Key = (NodeKey)e.GetValue(undo); } else if (e.PropertyName == "FromKeys") { this.FromKeys = (IList <NodeKey>)e.GetValue(undo); } else if (e.PropertyName == "ToKeys") { this.ToKeys = (IList <NodeKey>)e.GetValue(undo); } else if (e.PropertyName == "IsSubGraph") { this.IsSubGraph = (bool)e.GetValue(undo); } else if (e.PropertyName == "IsSubGraphExpanded") { this.IsSubGraphExpanded = (bool)e.GetValue(undo); } else if (e.PropertyName == "WasSubGraphExpanded") { this.WasSubGraphExpanded = (bool)e.GetValue(undo); } else if (e.PropertyName == "SubGraphKey") { this.SubGraphKey = (NodeKey)e.GetValue(undo); } else if (e.PropertyName == "MemberKeys") { this.MemberKeys = (IList <NodeKey>)e.GetValue(undo); } else if (e.PropertyName == "Category") { this.Category = (String)e.GetValue(undo); } else if (e.Change == ModelChange.Property) { if (!ModelHelper.SetProperty(e.PropertyName, e.Data, e.GetValue(undo))) { ModelHelper.Error("ERROR: Unrecognized property name: " + e.PropertyName != null ? e.PropertyName : "(noname)" + " in GraphModelNodeData.ChangeDataValue"); } } }
private void RaiseChanged(IDiagramModel model, ModelChange change, Object data, Object oldval, Object newval) { ModelChangedEventArgs e = new ModelChangedEventArgs(); e.Model = model; e.Change = change; e.Data = data; e.OldValue = oldval; e.NewValue = newval; model.RaiseChanged(e); }
internal void RaiseModelChanged(ModelChange change, Object data, Object oldval, Object newval) { ModelChangedEventArgs e = new ModelChangedEventArgs(); e.Model = (IDiagramModel)this; e.Change = change; e.Data = data; e.OldValue = oldval; e.NewValue = newval; OnChanged(e); }
/// <summary> /// This is basically a "copy constructor", making a copy of the given <see cref="ModelChangedEventArgs"/>. /// </summary> /// <param name="e"></param> public ModelChangedEventArgs(ModelChangedEventArgs e) : base(e.PropertyName) { // "copy constructor" if (e != null) { this.Model = e.Model; this.Change = e.Change; this.Data = e.Data; this.OldValue = e.OldValue; this.OldParam = e.OldParam; this.NewValue = e.NewValue; this.NewParam = e.NewParam; } //if (this.Model != null) { // this.Model.CopyOldValueForUndo(this); //?? optimize memory usage // this.Model.CopyNewValueForRedo(this); //} }
/// <summary> /// This is basically a "copy constructor", making a copy of the given <see cref="ModelChangedEventArgs"/>. /// </summary> /// <param name="e"></param> public ModelChangedEventArgs(ModelChangedEventArgs e) : base(e.PropertyName) // "copy constructor" { if (e != null) { this.Model = e.Model; this.Change = e.Change; this.Data = e.Data; this.OldValue = e.OldValue; this.OldParam = e.OldParam; this.NewValue = e.NewValue; this.NewParam = e.NewParam; } //if (this.Model != null) { // this.Model.CopyOldValueForUndo(this); //?? optimize memory usage // this.Model.CopyNewValueForRedo(this); //} }
/// <summary> /// This is called during undo or redo to effect state changes to model data. /// </summary> /// <param name="e">an edit describing the change to be performed</param> /// <param name="undo">true if undoing; false if redoing</param> /// <remarks> /// <para> /// This is called by <see cref="ChangeModel"/>. /// You will want to override this method to handle properties that you /// have added to your model data classes. /// Or you can have your data classes implement <see cref="IChangeDataValue"/> /// to achieve the same effect. /// </para> /// <para> /// By default this just calls <see cref="IChangeDataValue.ChangeDataValue"/> /// if the <see cref="ModelChangedEventArgs.Data"/> implements <see cref="IChangeDataValue"/>. /// Otherwise this uses reflection to set the <see cref="PropertyChangedEventArgs.PropertyName"/> /// to the <see cref="ModelChangedEventArgs.OldValue"/> or the /// <see cref="ModelChangedEventArgs.NewValue"/>, depending on the value of <paramref name="undo"/>. /// </para> /// <para> /// If you override this method, remember to call the base method for all /// cases that your override method does not handle. /// </para> /// </remarks> protected virtual void ChangeDataValue(ModelChangedEventArgs e, bool undo) { if (e == null) { return; } Object data = e.Data; IChangeDataValue changeable = data as IChangeDataValue; if (changeable != null) { changeable.ChangeDataValue(e, undo); } else if (ModelHelper.SetProperty(e.PropertyName, data, e.GetValue(undo))) { return; // successful set of data property } else { ModelHelper.Error((IDiagramModel)this, "Override ChangeDataValue to handle ModelChangedEventArgs.Data, or have data implement IChangeDataValue: " + data.ToString()); } }
/// <summary> /// Given an <see cref="IUndoableEdit"/> return an edited object /// that represents what was modified. /// </summary> /// <param name="edit"> /// an <see cref="IUndoableEdit"/>, /// usually either a <see cref="ModelChangedEventArgs"/> or a <see cref="CompoundEdit"/> /// </param> /// <returns> /// typically a <see cref="Node"/> or a <see cref="Link"/>, /// but this may be null if there is no such object, /// perhaps because a model property was modified, /// or because there were no real edits in the argument <paramref name="edit"/>. /// </returns> public virtual Object FindPrimaryObject(IUndoableEdit edit) { ModelChangedEventArgs ea = edit as ModelChangedEventArgs; if (ea != null) { return(ea.Data); } CompoundEdit ce = edit as CompoundEdit; if (ce != null) { foreach (IUndoableEdit e in ce.Edits) { Object data = FindPrimaryObject(e); if (data != null) { return(data); } } } return(null); }
/// <summary> /// Raises the <see cref="Changed"/> event. /// </summary> /// <param name="e">an edit describing the change that just happened</param> /// <remarks> /// If you override this method, be sure to call the base method first. /// </remarks> protected virtual void OnChanged(ModelChangedEventArgs e) { if (e == null) { return; } if (_ChangedEvent != null) { _ChangedEvent(this, e); } if (!this.SkipsUndoManager && !this.Initializing) { UndoManager um = this.UndoManager; if (um != null) { um.HandleModelChanged(this, e); } if (((int)e.Change) >= (int)ModelChange.Property) { this.IsModified = true; } } }
/// <summary> /// This is called during undo or redo to effect state changes to model data. /// </summary> /// <param name="e">an edit describing the change to be performed</param> /// <param name="undo">true if undoing; false if redoing</param> /// <remarks> /// <para> /// This is called by <see cref="ChangeModel"/>. /// You will want to override this method to handle properties that you /// have added to your model data classes. /// Or you can have your data classes implement <see cref="IChangeDataValue"/> /// to achieve the same effect. /// </para> /// <para> /// By default this just calls <see cref="IChangeDataValue.ChangeDataValue"/> /// if the <see cref="ModelChangedEventArgs.Data"/> implements <see cref="IChangeDataValue"/>. /// Otherwise this uses reflection to set the <see cref="PropertyChangedEventArgs.PropertyName"/> /// to the <see cref="ModelChangedEventArgs.OldValue"/> or the /// <see cref="ModelChangedEventArgs.NewValue"/>, depending on the value of <paramref name="undo"/>. /// </para> /// <para> /// If you override this method, remember to call the base method for all /// cases that your override method does not handle. /// </para> /// </remarks> protected virtual void ChangeDataValue(ModelChangedEventArgs e, bool undo) { if (e == null) return; Object data = e.Data; IChangeDataValue changeable = data as IChangeDataValue; if (changeable != null) { changeable.ChangeDataValue(e, undo); } else if (ModelHelper.SetProperty(e.PropertyName, data, e.GetValue(undo))) { return; // successful set of data property } else { ModelHelper.Error((IDiagramModel)this, "Override ChangeDataValue to handle ModelChangedEventArgs.Data, or have data implement IChangeDataValue: " + data.ToString()); } }
/// <summary> /// This is called during undo or redo to effect state changes to this model. /// </summary> /// <param name="e">an edit describing the change to be performed</param> /// <param name="undo">true if undoing; false if redoing</param> /// <remarks> /// <para> /// This is called by <see cref="ChangeModel"/>. /// You will want to override this method to handle properties that you /// have added to your derived model class. /// </para> /// <para> /// By default this uses reflection to set the <see cref="PropertyChangedEventArgs.PropertyName"/> /// to the <see cref="ModelChangedEventArgs.OldValue"/> or the /// <see cref="ModelChangedEventArgs.NewValue"/>, depending on the value of <paramref name="undo"/>. /// </para> /// <para> /// If you override this method, remember to call the base method for all /// cases that your override method does not handle. /// </para> /// </remarks> protected virtual void ChangeModelValue(ModelChangedEventArgs e, bool undo) { if (e == null) return; if (e.PropertyName == "Name") { this.Name = (String)e.GetValue(undo); } else if (e.PropertyName == "DataFormat") { this.DataFormat = (String)e.GetValue(undo); } else if (e.PropertyName == "Modifiable") { this.Modifiable = (bool)e.GetValue(undo); } else if (ModelHelper.SetProperty(e.PropertyName, this, e.GetValue(undo))) { return; // successful set of model property } else { ModelHelper.Error((IDiagramModel)this, "Override ChangeModelValue to handle ModelChangedEventArgs of a model property: " + e.ToString()); } }
/// <summary> /// This is called during an Undo or a Redo to actually make state /// changes to this model or to this model's data. /// </summary> /// <param name="e">an edit describing the change to be performed</param> /// <param name="undo">true if undoing; false if redoing</param> /// <remarks> /// <para> /// When <paramref name="e"/>'s <see cref="ModelChangedEventArgs.Change"/> /// value is <see cref="ModelChange.Property"/>, /// this calls <see cref="ChangeModelValue"/> /// if the <see cref="ModelChangedEventArgs.Data"/> is this model, /// or else it calls <see cref="ChangeDataValue"/>. /// </para> /// <para> /// This method handles all other <see cref="ModelChange"/> cases, /// since they are all predefined. /// </para> /// </remarks> public void ChangeModel(ModelChangedEventArgs e, bool undo) { if (e == null) return; bool old = this.IsChangingModel; //ModelHelper.Trace((undo ? "undo: " : "redo: ") + e.ToString()); try { this.IsChangingModel = true; if (e.Change == ModelChange.Property) { Object data = e.Data; if (data == null) data = this; if (data == this) { // changes to programmer defined properties on the model ChangeModelValue(e, undo); } else { // changes to some data inside the model ChangeDataValue(e, undo); } } else { ChangeModelState(e, undo); } } finally { this.IsChangingModel = old; } }
internal void RaiseModelChanged(ModelChange change, Object data, Object oldval, Object oldparam, Object newval, Object newparam) { ModelChangedEventArgs e = new ModelChangedEventArgs(); e.Model = (IDiagramModel)this; e.Change = change; e.Data = data; e.OldValue = oldval; e.OldParam = oldparam; e.NewValue = newval; e.NewParam = newparam; OnChanged(e); }
/// <summary> /// Raise a <see cref="Changed"/> event for a property change. /// </summary> /// <param name="propname">a property name</param> /// <param name="data">the object whose property has just changed</param> /// <param name="oldval">the previous value for the property</param> /// <param name="oldparam">additional information for the old value</param> /// <param name="newval">the new value for the property</param> /// <param name="newparam">additional information for the new value</param> /// <remarks> /// This is not used as frequently as <see cref="RaisePropertyChanged(String, Object, Object, Object)"/>. /// Typically the parameter values are used as indexes into the <paramref name="data"/>, /// to identify the particular value that was changed. /// </remarks> /// <seealso cref="RaiseChanged(ModelChangedEventArgs)"/> /// <seealso cref="RaisePropertyChanged(String, Object, Object, Object)"/> public void RaisePropertyChanged(String propname, Object data, Object oldval, Object oldparam, Object newval, Object newparam) { ModelChangedEventArgs e = new ModelChangedEventArgs(propname, data, oldval, newval); e.Model = (IDiagramModel)this; e.Change = ModelChange.Property; e.OldParam = oldparam; e.NewParam = newparam; OnChanged(e); }
/// <summary> /// Raise a <see cref="Changed"/> event, given a <see cref="ModelChangedEventArgs"/>. /// </summary> /// <param name="e">an edit describing the change that just happened</param> /// <remarks> /// This just calls <see cref="OnChanged"/>. /// This method is public because it is part of the implementation of <see cref="IDiagramModel"/>. /// </remarks> public void RaiseChanged(ModelChangedEventArgs e) { OnChanged(e); }
/// <summary> /// This predicate is responsible for deciding if a <see cref="ModelChangedEventArgs"/> /// is not interesting enough to be recorded. /// </summary> /// <param name="evt"></param> /// <returns>normally false, which causes the given event to be remembered; /// but true for negative valued enumerations of <see cref="ModelChange"/>.</returns> /// <remarks> /// </remarks> protected virtual bool SkipEvent(ModelChangedEventArgs evt) { if (evt == null) return true; return (int)evt.Change < (int)ModelChange.Property; }
/// <summary> /// This is called for each <see cref="Northwoods.GoXam.Model.IDiagramModel.Changed"/> event. /// </summary> /// <param name="e"></param> /// <remarks> /// <para> /// The implementation of this method and the methods that it calls should not modify the model. /// </para> /// <para> /// For small changes such as the addition or removal of node data from the model, /// this calls the <see cref="AddNodeForData"/> or <see cref="RemoveNodeForData"/> method. /// </para> /// <para> /// For changes in link relationships in the model, this calls /// <see cref="AddLinkForData(Object, Northwoods.GoXam.Model.IDiagramModel)"/>, /// <see cref="AddLinkForData(Object, Object, Northwoods.GoXam.Model.IDiagramModel)"/>, /// <see cref="RemoveLinkForData(Object, Northwoods.GoXam.Model.IDiagramModel)"/>, or /// <see cref="RemoveLinkForData(Object, Object, Northwoods.GoXam.Model.IDiagramModel)"/>. /// </para> /// <para> /// For more wholescale changes, such as a change in the <see cref="Northwoods.GoXam.Model.IDiagramModel.NodesSource"/>, /// this will call <see cref="RebuildNodeElements"/> to discard all existing nodes and links and reconstruct /// them using the appropriate (and perhaps changed) data templates. /// For widespread changes only involving links, this will call <see cref="RebuildLinkElements"/>. /// </para> /// </remarks> public virtual void OnModelChanged(ModelChangedEventArgs e) { if (e == null) return; if (e.Change == ModelChange.Property) { if (this.UpdatesRouteDataPoints && e.PropertyName == "Points") { Link link = FindLinkForData(e.Data, this.Diagram.Model); if (link != null && !this.IsUpdatingRouteDataPoints) { this.IsUpdatingRouteDataPoints = true; IList<Point> pts = e.NewValue as IList<Point>; if (pts != null && pts.Count() > 1) { link.Route.Points = pts; } else { link.Route.ClearPoints(); } this.IsUpdatingRouteDataPoints = false; } } return; } VerifyAccess(); switch (e.Change) { // data properties case ModelChange.ReplacedReference: if (FindNodeForData(e.OldValue, e.Model) != null) { RemoveNodeForData(e.OldValue, e.Model); } if (FindLinkForData(e.OldValue, e.Model) != null) { RemoveLinkForData(e.OldValue, e.Model); } break; // model contents and relationships case ModelChange.AddedNode: AddNodeForData(e.Data, e.Model); break; case ModelChange.RemovingNode: // called before node data is actually removed from the model, // when relationships (e.g. with containing group) are known; // but won't be called if Model.NodesSource is changed directly, // but will be called if someone calls Model.RemoveNode/DeleteNode RemoveNodeForData(e.Data, e.Model); break; case ModelChange.RemovedNode: // called after node data is actually removed from the model, // but old node data relationships might already be lost RemoveNodeForData(e.Data, e.Model); break; case ModelChange.ChangedParentNodeKey: { IDiagramModel model = e.Model; if (model != null) { Object child = e.Data; if (e.OldValue != null) RemoveLinkForData(model.FindNodeByKey(e.OldValue), child, model); if (e.NewValue != null) AddLinkForData(model.FindNodeByKey(e.NewValue), child, model); } break; } case ModelChange.AddedFromNodeKey: { IDiagramModel model = e.Model; if (model != null) { AddLinkForData(model.FindNodeByKey(e.NewValue), e.Data, model); } break; } case ModelChange.RemovedFromNodeKey: { IDiagramModel model = e.Model; if (model != null) { RemoveLinkForData(model.FindNodeByKey(e.OldValue), e.Data, model); } break; } case ModelChange.AddedChildNodeKey: case ModelChange.AddedToNodeKey: { IDiagramModel model = e.Model; if (model != null) { AddLinkForData(e.Data, model.FindNodeByKey(e.NewValue), model); } break; } case ModelChange.RemovedChildNodeKey: case ModelChange.RemovedToNodeKey: { IDiagramModel model = e.Model; if (model != null) { RemoveLinkForData(e.Data, model.FindNodeByKey(e.OldValue), model); } break; } case ModelChange.ChangedFromNodeKeys: { Object node = e.Data; if (node != null) { IDiagramModel model = e.Model; if (model != null) { System.Collections.IEnumerable oldneighborkeys = e.OldValue as System.Collections.IEnumerable; System.Collections.IEnumerable newneighborkeys = e.NewValue as System.Collections.IEnumerable; if (oldneighborkeys != null) { foreach (Object n in oldneighborkeys) { if (newneighborkeys == null || !ContainsKey(newneighborkeys, n)) { RemoveLinkForData(model.FindNodeByKey(n), node, model); } } } if (newneighborkeys != null) { foreach (Object n in newneighborkeys) { if (oldneighborkeys == null || !ContainsKey(oldneighborkeys, n)) { AddLinkForData(model.FindNodeByKey(n), node, model); } } } } } break; } case ModelChange.ChangedChildNodeKeys: case ModelChange.ChangedToNodeKeys: { Object node = e.Data; if (node != null) { IDiagramModel model = e.Model; if (model != null) { System.Collections.IEnumerable oldneighborkeys = e.OldValue as System.Collections.IEnumerable; System.Collections.IEnumerable newneighborkeys = e.NewValue as System.Collections.IEnumerable; if (oldneighborkeys != null && model != null) { foreach (Object n in oldneighborkeys) { if (newneighborkeys == null || !ContainsKey(newneighborkeys, n)) { RemoveLinkForData(node, model.FindNodeByKey(n), model); } } } if (newneighborkeys != null && model != null) { foreach (Object n in newneighborkeys) { if (oldneighborkeys == null || !ContainsKey(oldneighborkeys, n)) { AddLinkForData(node, model.FindNodeByKey(n), model); } } } } } break; } case ModelChange.ChangedGroupNodeKey: { IDiagramModel model = e.Model; if (model != null) { Node oldsg = FindNodeForData(model.FindNodeByKey(e.OldValue), model); if (oldsg != null) { oldsg.InvalidateRelationships("GroupNodeChanged"); } Node newsg = FindNodeForData(model.FindNodeByKey(e.NewValue), model); if (newsg != null) { newsg.InvalidateRelationships("GroupNodeChanged"); newsg.SortZOrder(); } Node node = FindNodeForData(e.Data, model); if (node != null) { foreach (Link l in node.LinksConnected) UpdateCachedMembership(model, l); if (oldsg != null) OnMemberRemoved(oldsg, node); else if (newsg != null) // and oldsg == null InvalidateDiagramLayout(node, LayoutChange.NodeRemoved); if (newsg != null) OnMemberAdded(newsg, node); else if (oldsg != null) // and newsg == null InvalidateDiagramLayout(node, LayoutChange.NodeAdded); } } break; } case ModelChange.ChangedMemberNodeKeys: { Object node = e.Data; if (node != null) { IDiagramModel model = e.Model; if (model != null) { Node groupnode = FindNodeForData(node, model); System.Collections.IEnumerable oldmemberkeys = e.OldValue as System.Collections.IEnumerable; System.Collections.IEnumerable newmemberkeys = e.NewValue as System.Collections.IEnumerable; if (oldmemberkeys != null && model != null) { foreach (Object mk in oldmemberkeys) { if (newmemberkeys == null || !ContainsKey(newmemberkeys, mk)) { Node oldmember = FindNodeForData(model.FindNodeByKey(mk), model); if (oldmember != null) { oldmember.InvalidateRelationships("MemberNodeChanged"); foreach (Link l in oldmember.LinksConnected) UpdateCachedMembership(model, l); if (groupnode != null) OnMemberRemoved(groupnode, oldmember); else InvalidateDiagramLayout(oldmember, LayoutChange.NodeRemoved); } } } } if (newmemberkeys != null && model != null) { foreach (Object n in newmemberkeys) { if (oldmemberkeys == null || !ContainsKey(oldmemberkeys, n)) { Node newmember = FindNodeForData(model.FindNodeByKey(n), model); if (newmember != null) { newmember.InvalidateRelationships("MemberNodeChanged"); foreach (Link l in newmember.LinksConnected) UpdateCachedMembership(model, l); newmember.SortZOrder(); if (groupnode != null) OnMemberAdded(groupnode, newmember); else InvalidateDiagramLayout(newmember, LayoutChange.NodeAdded); } } } } } } break; } case ModelChange.AddedMemberNodeKey: { Object nkey = e.NewValue; IDiagramModel model = e.Model; if (model != null) { Node newmember = FindNodeForData(model.FindNodeByKey(nkey), model); if (newmember != null) { newmember.InvalidateRelationships("MemberNodeAdded"); foreach (Link l in newmember.LinksConnected) UpdateCachedMembership(model, l); newmember.SortZOrder(); Node groupnode = FindNodeForData(e.Data, model); if (groupnode != null) OnMemberAdded(groupnode, newmember); else InvalidateDiagramLayout(newmember, LayoutChange.NodeAdded); } } break; } case ModelChange.RemovedMemberNodeKey: { Object okey = e.OldValue; IDiagramModel model = e.Model; if (model != null) { Node oldmember = FindNodeForData(model.FindNodeByKey(okey), model); if (oldmember != null) { oldmember.InvalidateRelationships("MemberNodeRemoved"); foreach (Link l in oldmember.LinksConnected) UpdateCachedMembership(model, l); Node groupnode = FindNodeForData(e.Data, model); if (groupnode != null) OnMemberRemoved(groupnode, oldmember); else InvalidateDiagramLayout(oldmember, LayoutChange.NodeRemoved); } } break; } case ModelChange.AddedLink: AddLinkForData(e.Data, e.Model); break; case ModelChange.RemovingLink: RemoveLinkForData(e.Data, e.Model); break; case ModelChange.RemovedLink: RemoveLinkForData(e.Data, e.Model); break; case ModelChange.ChangedLinkFromPort: case ModelChange.ChangedLinkToPort: { Link link = FindLinkForData(e.Data, e.Model); if (link != null) { if (e.NewValue != null) { // don't invalidate the route when disconnecting, only when connecting or reconnecting link.Route.InvalidateRoute(); } // reset the data binding, in case of bindings such as // Stroke="{Binding Path=Link.FromNode.Data.Brush}" PartBinding data = new PartBinding(link, e.Data); link.Content = data; link.DataContext = data; } break; } case ModelChange.ChangedLinkGroupNodeKey: { Link link = FindLinkForData(e.Data, e.Model); if (link != null) link.InvalidateRelationships("LinkGroupChanged"); IDiagramModel model = e.Model; if (model != null) { Node oldsg = FindNodeForData(model.FindNodeByKey(e.OldValue), model); if (oldsg != null) oldsg.InvalidateRelationships("LinkGroupChanged"); Node newsg = FindNodeForData(model.FindNodeByKey(e.NewValue), model); if (newsg != null) { Group sg = newsg as Group; if (sg != null && link != null) { bool vis = sg.IsExpandedSubGraph; if (link.Visible != vis) link.Visible = vis; } newsg.InvalidateRelationships("LinkGroupChanged"); } } break; } case ModelChange.ChangedLinkLabelKey: { Link link = FindLinkForData(e.Data, e.Model); if (link != null) link.InvalidateRelationships("LinkLabelChanged"); break; } case ModelChange.ChangedNodeKey: { Node n = FindNodeForData(e.Data, e.Model); if (n != null) { n.InvalidateRelationships("NodeKeyChanged"); } break; } case ModelChange.ChangedNodeCategory: { Object nodedata = e.Data; IDiagramModel model = e.Model; Node node = FindNodeForData(nodedata, model); if (node != null) { bool wasselected = node.IsSelected; RemoveNodeForData(nodedata, model); AddNodeForData(nodedata, model); if (wasselected) { Node newnode = FindNodeForData(nodedata, model); if (newnode != null) newnode.IsSelected = wasselected; } } break; } case ModelChange.ChangedLinkCategory: { Object linkdata = e.Data; IDiagramModel model = e.Model; Link link = FindLinkForData(linkdata, model); if (link != null) { bool wasselected = link.IsSelected; RemoveLinkForData(linkdata, model); AddLinkForData(linkdata, model); if (wasselected) { Link newlink = FindLinkForData(linkdata, model); if (newlink != null) newlink.IsSelected = wasselected; } } break; } case ModelChange.InvalidateRelationships: { Node node = FindNodeForData(e.Data, e.Model); if (node != null) { node.Remeasure(); Diagram.InvokeLater(this.Diagram, () => { node.InvalidateRelationships("ChangedNodeGeometry"); }); } break; } // model discovery case ModelChange.ChangedNodesSource: { Diagram diagram = this.Diagram; IDiagramModel model = e.Model; if (diagram != null && model != null && diagram.NodesSource != model.NodesSource) { diagram.NodesSource = model.NodesSource; } RebuildNodeElements(); // also calls RebuildLinkElements if (diagram != null && model != null && !model.IsChangingModel) { diagram.RelayoutDiagram(); } break; } case ModelChange.ChangedNodeKeyPath: case ModelChange.ChangedNodeCategoryPath: case ModelChange.ChangedNodeIsGroupPath: case ModelChange.ChangedGroupNodePath: case ModelChange.ChangedMemberNodesPath: case ModelChange.ChangedNodeIsLinkLabelPath: { RebuildNodeElements(); // also calls RebuildLinkElements Diagram diagram = this.Diagram; IDiagramModel model = e.Model; if (diagram != null && model != null && !model.IsChangingModel) { diagram.RelayoutDiagram(); } break; } case ModelChange.ChangedLinksSource: { Diagram diagram = this.Diagram; IDiagramModel model = e.Model; if (diagram != null && model != null) { ILinksModel lmodel = model as ILinksModel; if (lmodel != null && diagram.LinksSource != lmodel.LinksSource) { diagram.LinksSource = lmodel.LinksSource; } } RebuildLinkElements(); if (diagram != null && model != null && !model.IsChangingModel) { diagram.RelayoutDiagram(); } break; } case ModelChange.ChangedLinkFromPath: case ModelChange.ChangedLinkToPath: case ModelChange.ChangedFromNodesPath: case ModelChange.ChangedToNodesPath: case ModelChange.ChangedLinkLabelNodePath: { RebuildLinkElements(); Diagram diagram = this.Diagram; IDiagramModel model = e.Model; if (diagram != null && model != null && !model.IsChangingModel) { diagram.RelayoutDiagram(); } break; } //case ModelChange.ChangedParentPortParameterPath: //case ModelChange.ChangedChildPortParameterPath: case ModelChange.ChangedLinkFromParameterPath: case ModelChange.ChangedLinkToParameterPath: case ModelChange.ChangedLinkCategoryPath: RebuildLinkElements(); break; case ModelChange.StartedTransaction: //?? produce routed event break; case ModelChange.CommittedTransaction: { //?? produce routed event String reason = e.Data as String; if (reason != "Layout" && reason != "DelayedRouting") { // check for this case to avoid infinite loop Diagram diagram = this.Diagram; if (diagram == null) break; IDiagramModel model = diagram.Model; if (model != null) model.ClearUnresolvedReferences(); DiagramPanel panel = diagram.Panel; if (panel != null) panel.UpdateScrollTransform(); LayoutManager laymgr = diagram.LayoutManager; if (laymgr != null) laymgr.InvokeLayoutDiagram("CommittedTransaction"); diagram.UpdateCommands(); } break; } case ModelChange.RolledBackTransaction: { //?? produce routed event String reason = e.Data as String; if (reason != "Layout" && reason != "DelayedRouting") { // check for this case to avoid infinite loop Diagram diagram = this.Diagram; if (diagram == null) break; IDiagramModel model = diagram.Model; if (model != null) model.ClearUnresolvedReferences(); diagram.UpdateCommands(); } break; } case ModelChange.StartingUndo: case ModelChange.StartingRedo: break; case ModelChange.FinishedUndo: case ModelChange.FinishedRedo: { //?? produce routed event Diagram diagram = this.Diagram; if (diagram == null) break; DiagramPanel panel = diagram.Panel; if (panel == null) break; // force everything to be remeasured foreach (UIElement elt in panel.Children) { NodeLayer nlay = elt as NodeLayer; if (nlay != null) { if (nlay.IsTemporary) continue; foreach (Node n in nlay.Nodes) { n.Remeasure(); } } else { LinkLayer llay = elt as LinkLayer; if (llay != null) { if (llay.IsTemporary) continue; foreach (Link l in llay.Links) { l.Remeasure(); } } } } panel.UpdateScrollTransform(); panel.InvokeUpdateDiagramBounds("Undo/Redo"); diagram.UpdateCommands(); break; } // model state case ModelChange.ChangedName: case ModelChange.ChangedDataFormat: case ModelChange.ChangedModifiable: case ModelChange.ChangedValidCycle: case ModelChange.ChangedValidUnconnectedLinks: { Diagram diagram = this.Diagram; if (diagram == null) break; diagram.UpdateCommands(); break; } case ModelChange.ChangedCopyingGroupCopiesMembers: case ModelChange.ChangedCopyingLinkCopiesLabel: case ModelChange.ChangedRemovingGroupRemovesMembers: case ModelChange.ChangedRemovingLinkRemovesLabel: break; default: Diagram.Error("Diagram did not handle Model change: " + e.Change.ToString()); break; } }
private void ObservedPanel_AnimatingChanged(object sender, ModelChangedEventArgs e) { if (sender is DiagramPanel) { this.IsAnimatingScrolling = (bool)e.NewValue; UpdateBox(); } else if (sender is LayoutManager) { this.IsAnimatingLayout = (bool)e.NewValue; // UpdateNodesAndLinks when no longer IsAnimating if (!this.IsAnimatingLayout) { UpdateNodesAndLinks(); } } }
/// <summary> /// Raises the <see cref="Changed"/> event. /// </summary> /// <param name="e">an edit describing the change that just happened</param> /// <remarks> /// If you override this method, be sure to call the base method first. /// </remarks> protected virtual void OnChanged(ModelChangedEventArgs e) { if (e == null) return; if (_ChangedEvent != null) _ChangedEvent(this, e); if (!this.SkipsUndoManager && !this.Initializing) { UndoManager um = this.UndoManager; if (um != null) um.HandleModelChanged(this, e); if (((int)e.Change) >= (int)ModelChange.Property) this.IsModified = true; } }
/// <summary> /// Create an <see cref="IUndoableEdit"/> for a <see cref="IDiagramModel.Changed"/> event. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> /// <remarks> /// This calls <see cref="SkipEvent"/> if for some reason we should ignore /// the <paramref name="e"/>. /// This then creates a <see cref="ModelChangedEventArgs"/> and adds it to the /// <see cref="CurrentEdit"/>, a <see cref="UndoManager.CompoundEdit"/> which it allocates /// if needed. /// This method always ignores all Changed events while we are performing an /// <see cref="Undo"/> or <see cref="Redo"/>. /// </remarks> public virtual void HandleModelChanged(Object sender, ModelChangedEventArgs e) { // changes caused by performing an undo or redo should be ignored! if (this.IsUndoingRedoing) return; if (!SkipEvent(e)) { CompoundEdit cedit = this.CurrentEdit; //ModelHelper.Trace(this.TransactionLevel, e.ToString()); if (cedit == null) { cedit = new CompoundEdit(); this.CurrentEdit = cedit; } // make a copy of the event to save as an edit in the list ModelChangedEventArgs edit = new ModelChangedEventArgs(e); cedit.Edits.Add(edit); if (this.ChecksTransactionLevel && this.TransactionLevel <= 0) { ModelHelper.Trace("Change not within a transaction: " + edit.ToString()); } } }