private void notifyPropertyChanged_PropertyChanging(object sender, PropertyChangingEventArgs e) { if (!TrackChanges) { return; } if (ExcludedTypes.Contains(sender.GetType())) { return; } var memento = new NotifyPropertyChangeMemento(); memento.RememberOldValue(sender, e); var key = new PropertyChangeKey { Sender = sender, PropertyName = e.PropertyName }; if (propertyChangeMementos.ContainsKey(key)) { throw new InvalidOperationException(string.Format("Property {0} of {1} is already being changed", e.PropertyName, sender)); } propertyChangeMementos[key] = memento; }
private void notifyPropertyChanged_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (!TrackChanges) { return; } if (ExcludedTypes.Contains(sender.GetType())) { return; } NotifyPropertyChangeMemento memento; var key = new PropertyChangeKey { Sender = sender, PropertyName = e.PropertyName }; propertyChangeMementos.TryGetValue(key, out memento); if (memento == null) { throw new NotSupportedException("PropertyChanged received without PropertyChanging"); } memento.RememberNewValue(sender, e); propertyChangeMementos.Remove(key); // check if we need to add memento to current compound memento or to undoRedoManager if (e.PropertyName == "IsEditing") { var editable = (IEditableObject)memento.Instance; if (editable.IsEditing) // BeginEdit() { if (currentEditableObjectMemento != null) { editableObjectMementos.Push(currentEditableObjectMemento); } currentEditableObjectMemento = new CompoundMemento { Name = editable.CurrentEditAction.Name }; AddMemento(memento); } else // EndEdit() { if (currentEditableObjectMemento == null) { throw new InvalidOperationException("Unexpected end edit call before begin edit"); } AddMemento(memento); if (editableObjectMementos.Count == 0) { log.DebugFormat("saving undo for edit action {0}", currentEditableObjectMemento.Name); AddNewMementoCallback(currentEditableObjectMemento); currentEditableObjectMemento = null; } else { // pull previous editable object memento and continue with it as current compound memento var previousEditableObjectMemento = editableObjectMementos.Pop(); previousEditableObjectMemento.ChildMementos.Add(currentEditableObjectMemento); currentEditableObjectMemento = previousEditableObjectMemento; } } } else { if (currentEditableObjectMemento != null) { log.DebugFormat("adding property cange to edit action {0}.{1}: {2} -> {3}", sender.GetType().Name, e.PropertyName, memento.OldValue ?? "null", memento.NewValue ?? "null"); } else { log.DebugFormat("saving undo for property cange {0}.{1}: {2} -> {3}", sender.GetType().Name, e.PropertyName, memento.OldValue ?? "null", memento.NewValue ?? "null"); } AddMemento(memento); } }
private void ObservablePropertyChanged(object sender, PropertyChangedEventArgs e) { if (!TrackChanges) { return; } ThrowIfFromDifferentThread(); if (ExcludedTypes.Contains(sender.GetType())) { return; } if (!handlingDisconnected) { //we got an event through the normal path, so we're sure this object is still connected UnsubscribeDisconnected(sender); } // if virtual property was added - try to handle it if(virtualPropertyCalls.Contains(e.PropertyName) && CheckVirtualPropertyNames(sender, e.PropertyName)) { virtualPropertyCalls.Remove(e.PropertyName); return; // will be handled in second call (in derived class setter) } NotifyPropertyChangeMemento memento; var key = new PropertyChangeKey(sender, e.PropertyName); propertyChangeMementos.TryGetValue(key, out memento); if (memento == null) { // check if it is not something from disconnected objects if (propertyChangeMementosDisconnected.ContainsKey(key)) { propertyChangeMementosDisconnected.Remove(key); return; } throw new NotSupportedException("PropertyChanged received without PropertyChanging"); } memento.RememberNewValue(sender, e); propertyChangeMementos.Remove(key); if (!handlingDisconnected && propertyChangeMementosDisconnected.ContainsKey(key)) { propertyChangeMementosDisconnected.Remove(key); } // exceptional situation, same object! lastPropertyChangeMemento = memento; if (!TypeUtils.IsAggregationProperty(sender, e.PropertyName)) { if (memento.NewValue != null && ReferenceEquals(memento.OldValue, memento.NewValue)) { UnsubscribeDisconnected(memento.OldValue); } UnsubscribeDisconnected(memento.NewValue); } // check if we need to add memento to current compound memento or to undoRedoManager if (e.PropertyName == "IsEditing") { EditableObjectIsEditingChanged(memento); } else { LogPropertyChanged(sender, e, memento); } }
private void ObservablePropertyChanging(object sender, PropertyChangingEventArgs e) { if (!TrackChanges) { return; } pendingIncrementEventCallCountSender = null; ThrowIfFromDifferentThread(); if (ExcludedTypes.Contains(sender.GetType())) { return; } if (!handlingDisconnected) { //we got an event through the normal path, so we're sure this object is still connected UnsubscribeDisconnected(sender); } FixCancelledCollectionChangingEvents(); var key = new PropertyChangeKey(sender, e.PropertyName); NotifyPropertyChangeMemento existingMemento; propertyChangeMementos.TryGetValue(key, out existingMemento); if (existingMemento != null) { // check if property is virtual, if so - show tip how to fix it if (CheckVirtualPropertyNames(sender, e.PropertyName)) { // ignore and add property to the virtual property list so that it can be handled in changed handler virtualPropertyCalls.Add(e.PropertyName); return; } } if (restoringMemento != null) { if (!IsPropertyChangeEventPartOfRestore(sender, e.PropertyName)) { if (e.PropertyName != "IsEditing") { throw new InvalidOperationException("Side-effect code detected (code which changes object tree in setters or in event handlers) which is not marked with [EditAction]"); } } } if (eventCascadeCallLevel > 1 && !handlingDisconnected) { if (eventCascadeCallLevel < expectedEventCascadeCallLevel) { expectedEventCascadeCallLevel = eventCascadeCallLevel; } // we must be in a side-effect if (expectedEventCascadeCallLevel != eventCascadeCallLevel) { LogCascadeLevels(); throw new InvalidOperationException("Side-effect code detected (code which changes object tree in setters or in event handlers) which is not marked with [EditAction]"); } } if (existingMemento != null) { if(existingMemento.LastEventSenderIsDisconnected || handlingDisconnected) { propertyChangeMementosDisconnected[key] = existingMemento; return; // ignore } var oldMemento = propertyChangeMementos[key]; LogMementoSenders(oldMemento); throw new InvalidOperationException(string.Format("Property {0} of {1} is already being changed", e.PropertyName, sender)); } var memento = (NotifyPropertyChangeMemento)mementoStack.Peek(); memento.RememberOldValue(sender, e); propertyChangeMementos[key] = memento; // debugging memento.LastEventSender = EventSettings.LastEventBubbler; memento.LastEventSenderIsDisconnected = handlingDisconnected; if (!handlingDisconnected && !TypeUtils.IsAggregationProperty(sender, e.PropertyName)) { if (restoringMemento == null) { HandleIfTransactional(memento.OldValue, true); } SubscribeDisconnected(memento.OldValue); // listen to changes in disconnected objects } }