1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System.Activities.Presentation.Model
7 using System.Activities.Expressions;
8 using System.Activities.Presentation;
9 using System.Activities.Presentation.Hosting;
10 using System.Activities.Presentation.Internal.PropertyEditing;
11 using System.Activities.Presentation.Services;
12 using System.Activities.Presentation.Validation;
13 using System.Activities.Presentation.View;
14 using System.Collections;
15 using System.Collections.Generic;
16 using System.ComponentModel;
17 using System.Diagnostics;
18 using System.Diagnostics.CodeAnalysis;
22 using Microsoft.Activities.Presentation.Xaml;
24 // This class manages the model tree, provides the root model item and the modelservice
25 // This also provides syncing the model tree with the xaml text
26 // The model service is publishes on the editing context passed to the constructor.
28 [Fx.Tag.XamlVisible(false)]
29 public class ModelTreeManager
31 internal ModelServiceImpl modelService;
32 EditingContext context;
33 // The value of this dictionary is a WeakReference to ModelItem.
34 // This need to be a WeakReference because if the ModelItem has a strong reference, it
35 // will have a strong reference to the underlying object instance as well.
36 WeakKeyDictionary<object, WeakReference> objectMap;
38 ImmediateEditingScope immediateEditingScope;
39 Stack<ModelEditingScope> editingScopes;
40 int redoUndoInProgressCount = 0;
41 FeatureManager featureManager;
42 ModelGraphManager graphManager;
44 public ModelTreeManager(EditingContext context)
48 throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
50 this.context = context;
51 // We want to check reference equality for keys in ObjectMap.
52 // If the user overrides Equals method for their class we still want to use referential equality.
53 objectMap = new WeakKeyDictionary<object, WeakReference>(ObjectReferenceEqualityComparer<object>.Default);
54 editingScopes = new Stack<ModelEditingScope>();
55 this.graphManager = new ModelGraphManager(this);
58 public event EventHandler<EditingScopeEventArgs> EditingScopeCompleted;
60 // Private event only for EditingScope.
61 private event EventHandler<ModelItemsRemovedEventArgs> ModelItemsRemoved;
63 private event EventHandler<ModelItemsAddedEventArgs> ModelItemsAdded;
73 internal EditingContext Context
81 FeatureManager FeatureManager
85 if (this.featureManager == null)
87 this.featureManager = this.context.Services.GetService<FeatureManager>();
89 return this.featureManager;
93 internal bool RedoUndoInProgress
97 return this.redoUndoInProgressCount > 0;
101 internal void StartTracking()
103 redoUndoInProgressCount--;
106 internal void StopTracking()
108 redoUndoInProgressCount++;
111 public ModelItem CreateModelItem(ModelItem parent, object instance)
113 if (instance == null)
115 throw FxTrace.Exception.AsError(new ArgumentNullException("instance"));
119 Type instanceType = instance.GetType();
120 object[] result = new object[2] { false, false };
122 Type[] interfaces = instanceType.FindInterfaces(ModelTreeManager.CheckInterface, result);
124 bool isList = (bool)result[0];
125 bool isDictionary = (bool)result[1];
129 foreach (Type type in interfaces)
131 if (type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
133 // To expose one more property, a collection of MutableKeyValuePairs, to the model tree.
134 TypeDescriptor.AddProvider(new DictionaryTypeDescriptionProvider(instanceType), instance);
139 ModelItemDictionary modelItem = new ModelItemDictionaryImpl(this, instance.GetType(), instance, parent);
144 ModelItemCollectionImpl modelItem = new ModelItemCollectionImpl(this, instance.GetType(), instance, parent);
149 retval = new ModelItemImpl(this, instance.GetType(), instance, parent);
151 if (!((instance is ValueType) || (instance is string)))
154 // ValueType do not have a concept of shared reference, they are always copied.
155 // strings are immutatable, therefore the risk of making all shared string references to different
156 // string ModelItems is low.
158 // To special case string is because underlying OM are sharing string objects for DisplayName across
159 // Different activity object instances. These shared references is causing memory leak because of bugs.
161 // We will need to fix these issues in Beta2.
163 objectMap[instance] = new WeakReference(retval);
166 if (this.FeatureManager != null)
168 this.FeatureManager.InitializeFeature(instance.GetType());
173 static bool CheckInterface(Type type, object result)
175 object[] values = (object[])result;
176 if (typeof(IList).IsAssignableFrom(type))
181 if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IList<>))
186 if (typeof(IDictionary).IsAssignableFrom(type))
191 if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
199 public void Load(object rootInstance)
201 if (rootInstance == null)
203 throw FxTrace.Exception.AsError(new ArgumentNullException("rootInstance"));
207 ModelItem oldRoot = this.rootItem;
208 this.rootItem = WrapAsModelItem(rootInstance);
209 this.graphManager.OnRootChanged(oldRoot, this.rootItem);
210 if (this.modelService == null)
212 this.modelService = new ModelServiceImpl(this);
213 this.context.Services.Publish<ModelService>(modelService);
217 // This methods clears the value of a property , if the property is
218 // a reference type then its set to null, if its a value type the
219 // property value is reset to the default. this also clears the sub ModelItem corresponding
220 // to the old value from the parent ModelItem's modelPropertyStore.
221 internal void ClearValue(ModelPropertyImpl modelProperty)
223 Fx.Assert(modelProperty != null, "modelProperty should not be null");
224 Fx.Assert(modelProperty.Parent is IModelTreeItem, "modelProperty.Parent should be an IModelTreeItem");
225 ModelItem newValueModelItem = null;
226 newValueModelItem = WrapAsModelItem(modelProperty.DefaultValue);
227 PropertyChange propertyChange = new PropertyChange()
229 Owner = modelProperty.Parent,
230 PropertyName = modelProperty.Name,
231 OldValue = modelProperty.Value,
232 NewValue = newValueModelItem,
233 ModelTreeManager = this
235 AddToCurrentEditingScope(propertyChange);
238 internal void CollectionAdd(ModelItemCollectionImpl dataModelItemCollection, ModelItem item)
240 CollectionInsert(dataModelItemCollection, -1, item);
243 internal void CollectionInsert(ModelItemCollectionImpl dataModelItemCollection, int index, ModelItem item)
245 Fx.Assert(dataModelItemCollection != null, "collection should not be null");
246 CollectionChange change = new CollectionChange()
248 Collection = dataModelItemCollection,
251 ModelTreeManager = this,
252 Operation = CollectionChange.OperationType.Insert
254 AddToCurrentEditingScope(change);
257 internal void CollectionClear(ModelItemCollectionImpl modelItemCollectionImpl)
259 Fx.Assert(modelItemCollectionImpl != null, "collection should not be null");
260 Fx.Assert(this.modelService != null, "modelService should not be null");
261 List<ModelItem> removedItems = new List<ModelItem>();
262 removedItems.AddRange(modelItemCollectionImpl);
263 using (ModelEditingScope editingScope = CreateEditingScope(SR.CollectionClearEditingScopeDescription))
265 foreach (ModelItem modelItem in removedItems)
267 this.CollectionRemove(modelItemCollectionImpl, modelItem);
269 editingScope.Complete();
271 this.modelService.OnModelItemsRemoved(removedItems);
274 internal void NotifyCollectionInsert(ModelItem item, ModelChangeInfo changeInfo)
276 this.modelService.OnModelItemAdded(item, changeInfo);
279 internal void CollectionRemove(ModelItemCollectionImpl dataModelItemCollection, ModelItem item)
281 CollectionRemove(dataModelItemCollection, item, -1);
284 internal void CollectionRemoveAt(ModelItemCollectionImpl dataModelItemCollection, int index)
286 ModelItem item = dataModelItemCollection[index];
287 CollectionRemove(dataModelItemCollection, item, index);
290 private void CollectionRemove(ModelItemCollectionImpl dataModelItemCollection, ModelItem item, int index)
292 Fx.Assert(dataModelItemCollection != null, "collection should not be null");
293 CollectionChange change = new CollectionChange()
295 Collection = dataModelItemCollection,
298 ModelTreeManager = this,
299 Operation = CollectionChange.OperationType.Delete
301 AddToCurrentEditingScope(change);
304 internal void NotifyCollectionRemove(ModelItem item, ModelChangeInfo changeInfo)
306 this.modelService.OnModelItemRemoved(item, changeInfo);
309 internal void DictionaryClear(ModelItemDictionaryImpl modelDictionary)
311 Fx.Assert(modelDictionary != null, "dictionary should not be null");
312 Fx.Assert(this.modelService != null, "modelService should not be null");
313 ModelItem[] keys = modelDictionary.Keys.ToArray<ModelItem>();
315 using (ModelEditingScope editingScope = CreateEditingScope(SR.DictionaryClearEditingScopeDescription))
317 foreach (ModelItem key in keys)
319 this.DictionaryRemove(modelDictionary, key);
321 editingScope.Complete();
325 internal void DictionaryEdit(ModelItemDictionaryImpl dataModelItemDictionary, ModelItem key, ModelItem newValue, ModelItem oldValue)
327 Fx.Assert(dataModelItemDictionary != null, "dictionary should not be null");
328 Fx.Assert(this.modelService != null, "modelService should not be null");
329 DictionaryEditChange change = new DictionaryEditChange()
331 Dictionary = dataModelItemDictionary,
335 ModelTreeManager = this
337 AddToCurrentEditingScope(change);
340 internal void DictionaryAdd(ModelItemDictionaryImpl dataModelItemDictionary, ModelItem key, ModelItem value)
342 Fx.Assert(dataModelItemDictionary != null, "dictionary should not be null");
343 Fx.Assert(this.modelService != null, "modelService should not be null");
344 DictionaryChange change = new DictionaryChange()
346 Dictionary = dataModelItemDictionary,
349 Operation = DictionaryChange.OperationType.Insert,
350 ModelTreeManager = this
352 AddToCurrentEditingScope(change);
355 internal void OnPropertyEdgeAdded(string propertyName, ModelItem from, ModelItem to)
357 this.graphManager.OnPropertyEdgeAdded(propertyName, from, to);
360 internal void OnItemEdgeAdded(ModelItem from, ModelItem to)
362 this.graphManager.OnItemEdgeAdded(from, to);
365 internal void OnPropertyEdgeRemoved(string propertyName, ModelItem from, ModelItem to)
367 this.graphManager.OnPropertyEdgeRemoved(propertyName, from, to);
370 internal void OnItemEdgeRemoved(ModelItem from, ModelItem to)
372 this.graphManager.OnItemEdgeRemoved(from, to);
375 internal void DictionaryRemove(ModelItemDictionaryImpl dataModelItemDictionary, ModelItem key)
377 Fx.Assert(dataModelItemDictionary != null, "dictionary should not be null");
378 Fx.Assert(this.modelService != null, "modelService should not be null");
379 ModelItem value = dataModelItemDictionary[key];
380 DictionaryChange change = new DictionaryChange()
382 Dictionary = dataModelItemDictionary,
385 Operation = DictionaryChange.OperationType.Delete,
386 ModelTreeManager = this
388 AddToCurrentEditingScope(change);
392 internal static IEnumerable<ModelItem> Find(ModelItem startingItem, Predicate<ModelItem> matcher, bool skipCollapsedAndUnrootable)
394 Fx.Assert(startingItem != null, "starting item should not be null");
395 Fx.Assert(matcher != null, "matching predicate should not be null");
396 WorkflowViewService viewService = startingItem.GetEditingContext().Services.GetService<ViewService>() as WorkflowViewService;
397 if (skipCollapsedAndUnrootable)
399 Fx.Assert(viewService != null, "ViewService must be available in order to skip exploring ModelItems whose views are collapsed.");
402 Predicate<ModelItem> shouldSearchThroughProperties = (currentModelItem) => (!skipCollapsedAndUnrootable)
403 || (!typeof(WorkflowViewElement).IsAssignableFrom(viewService.GetDesignerType(currentModelItem.ItemType)))
404 || (ViewUtilities.IsViewExpanded(currentModelItem, startingItem.GetEditingContext()) && viewService.ShouldAppearOnBreadCrumb(currentModelItem, true));
406 List<ModelItem> foundItems = new List<ModelItem>();
407 Queue<ModelItem> modelItems = new Queue<ModelItem>();
408 modelItems.Enqueue(startingItem);
409 HashSet<ModelItem> alreadyVisited = new HashSet<ModelItem>();
410 while (modelItems.Count > 0)
412 ModelItem currentModelItem = modelItems.Dequeue();
413 if (currentModelItem == null)
418 if (matcher(currentModelItem))
420 foundItems.Add(currentModelItem);
423 List<ModelItem> neighbors = GetNeighbors(currentModelItem, shouldSearchThroughProperties);
425 foreach (ModelItem neighbor in neighbors)
427 if (!alreadyVisited.Contains(neighbor))
429 alreadyVisited.Add(neighbor);
430 modelItems.Enqueue(neighbor);
438 private static List<ModelItem> GetNeighbors(ModelItem currentModelItem, Predicate<ModelItem> extraShouldSearchThroughProperties)
440 List<ModelItem> neighbors = new List<ModelItem>();
442 // do not search through Type and its derivatives
443 if (typeof(Type).IsAssignableFrom(currentModelItem.ItemType))
448 ModelItemCollection collection = currentModelItem as ModelItemCollection;
449 if (collection != null)
451 foreach (ModelItem modelItem in collection)
453 if (modelItem != null)
455 neighbors.Add(modelItem);
461 ModelItemDictionary dictionary = currentModelItem as ModelItemDictionary;
462 if (dictionary != null)
464 foreach (KeyValuePair<ModelItem, ModelItem> kvp in dictionary)
466 ModelItem miKey = kvp.Key;
469 neighbors.Add(miKey);
472 ModelItem miValue = kvp.Value;
475 neighbors.Add(miValue);
481 if (extraShouldSearchThroughProperties(currentModelItem))
483 ModelPropertyCollection modelProperties = currentModelItem.Properties;
484 foreach (ModelProperty property in modelProperties)
486 if (currentModelItem is ModelItemDictionary && string.Equals(property.Name, "ItemsCollection"))
488 // Don't search the item collection since we already search the items above.
492 // we don't want to even try to get the value for a value type property
493 // because that will create a new ModelItem every time.
495 // System.Type has properties that throw when we try to get value
496 // we don't want to expand system.type further during a search.
497 if (typeof(Type).IsAssignableFrom(property.PropertyType) || property.PropertyType.IsValueType)
504 if (property.Value != null)
506 neighbors.Add(property.Value);
515 internal static ModelItem FindFirst(ModelItem startingItem, Predicate<ModelItem> matcher)
517 return FindFirst(startingItem, matcher, (m) => true);
520 internal static ModelItem FindFirst(ModelItem startingItem, Predicate<ModelItem> matcher, Predicate<ModelItem> extraShouldSearchThroughProperties)
522 Fx.Assert(startingItem != null, "starting item should not be null");
523 Fx.Assert(matcher != null, "matching predicate should not be null");
524 Fx.Assert(extraShouldSearchThroughProperties != null, "extraShouldSearchThroughProperties should not be null");
525 ModelItem foundItem = null;
526 Queue<ModelItem> modelItems = new Queue<ModelItem>();
527 modelItems.Enqueue(startingItem);
528 HashSet<ModelItem> alreadyVisited = new HashSet<ModelItem>();
529 while (modelItems.Count > 0)
531 ModelItem currentModelItem = modelItems.Dequeue();
532 if (currentModelItem == null)
537 if (matcher(currentModelItem))
539 foundItem = currentModelItem;
543 List<ModelItem> neighbors = GetNeighbors(currentModelItem, extraShouldSearchThroughProperties);
545 foreach (ModelItem neighbor in neighbors)
547 if (!alreadyVisited.Contains(neighbor))
549 alreadyVisited.Add(neighbor);
550 modelItems.Enqueue(neighbor);
557 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
558 Justification = "If the property getter threw here we dont want to crash, we just dont want to wrap that property value")]
559 [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
560 Justification = "If the property getter threw here we dont want to crash, we just dont want to wrap that property value")]
561 internal ModelItem GetValue(ModelPropertyImpl dataModelProperty)
563 Fx.Assert(dataModelProperty != null, "modelproperty should not be null");
564 Fx.Assert(dataModelProperty.Parent is IModelTreeItem, "modelproperty.Parent should be an IModelTreeItem");
565 IModelTreeItem parent = (IModelTreeItem)dataModelProperty.Parent;
568 // always reevaluate attached properties. the cache in attached properties case is only to remember the old value.
569 if (!dataModelProperty.IsAttached && parent.ModelPropertyStore.ContainsKey(dataModelProperty.Name))
571 value = parent.ModelPropertyStore[dataModelProperty.Name];
573 // Create a ModelItem on demand for the value of the property.
578 value = WrapAsModelItem(dataModelProperty.PropertyDescriptor.GetValue(parent.ModelItem.GetCurrentValue()));
580 catch (System.Exception)
582 // GetValue throws an exception if Value is not available
587 if (!dataModelProperty.IsAttached)
589 parent.ModelPropertyStore.Add(dataModelProperty.Name, value);
590 this.graphManager.OnPropertyEdgeAdded(dataModelProperty.Name, (ModelItem)parent, value);
597 internal ModelItem SetValue(ModelPropertyImpl modelProperty, object value)
599 Fx.Assert(modelProperty != null, "modelProperty should not be null");
600 ModelItem newValueModelItem = null;
602 RefreshPropertiesAttribute refreshPropertiesAttribute = ExtensibilityAccessor.GetAttribute<RefreshPropertiesAttribute>(modelProperty.Attributes);
603 if (refreshPropertiesAttribute != null && refreshPropertiesAttribute.RefreshProperties == RefreshProperties.All)
605 Dictionary<string, ModelItem> modelPropertyStore = ((IModelTreeItem)modelProperty.Parent).ModelPropertyStore;
606 Dictionary<string, ModelItem> removedModelItems = new Dictionary<string, ModelItem>(modelPropertyStore);
607 modelPropertyStore.Clear();
609 foreach (KeyValuePair<string, ModelItem> kvp in removedModelItems)
611 if (kvp.Value != null)
613 this.OnPropertyEdgeRemoved(kvp.Key, modelProperty.Parent, kvp.Value);
618 if (value is ModelItem)
620 newValueModelItem = (ModelItem)value;
624 newValueModelItem = WrapAsModelItem(value);
626 // dont do deferred updates for attached properties
627 if (modelProperty.IsAttached)
629 modelProperty.SetValueCore(newValueModelItem);
633 PropertyChange propertyChange = new PropertyChange()
635 Owner = modelProperty.Parent,
636 PropertyName = modelProperty.Name,
637 OldValue = modelProperty.Value,
638 NewValue = newValueModelItem,
639 ModelTreeManager = this
641 AddToCurrentEditingScope(propertyChange);
645 return newValueModelItem;
648 internal void AddToCurrentEditingScope(Change change)
650 EditingScope editingScope;
651 if (editingScopes.Count > 0)
653 editingScope = (EditingScope)editingScopes.Peek();
654 // Automatic generated change during apply changes of Redo/Undo should be ignored.
655 if (!RedoUndoInProgress)
657 editingScope.Changes.Add(change);
662 //edit operation without editingscope create an editing scope and complete it immediately.
663 editingScope = CreateEditingScope(change.Description);
664 editingScope.Changes.Add(change);
667 editingScope.Complete();
671 editingScope.Revert();
678 internal bool CanCreateImmediateEditingScope()
680 return this.editingScopes.Count == 0 && !this.Context.Services.GetService<UndoEngine>().IsBookmarkInPlace;
683 internal EditingScope CreateEditingScope(string description, bool shouldApplyChangesImmediately)
685 EditingScope editingScope = null;
686 EditingScope outerScope = editingScopes.Count > 0 ? (EditingScope)editingScopes.Peek() : null;
687 if (shouldApplyChangesImmediately)
689 // shouldApplyChangesImmediately won't have any effect if outer scope exists
690 if (outerScope != null)
694 this.immediateEditingScope = context.Services.GetRequiredService<UndoEngine>().CreateImmediateEditingScope(description, this);
695 editingScope = this.immediateEditingScope;
696 // ImmediateEditingScope should not be pushed onto the editingScopes,
697 // otherwise it will become delay applied instead of immediate applied.
701 editingScope = new EditingScope(this, outerScope);
702 editingScopes.Push(editingScope);
704 editingScope.Description = description;
708 internal EditingScope CreateEditingScope(string description)
710 return this.CreateEditingScope(description, false);
713 internal void NotifyPropertyChange(ModelPropertyImpl dataModelProperty, ModelChangeInfo changeInfo)
715 modelService.OnModelPropertyChanged(dataModelProperty, changeInfo);
718 internal void SyncModelAndText()
720 // Place holder for xaml generation ModelTreeManager now is instance only.
723 internal ModelItem WrapAsModelItem(object instance)
725 ModelItem modelItem = GetModelItem(instance);
727 if (null != instance && null == modelItem)
729 modelItem = CreateModelItem(null, instance);
734 internal ModelItem GetModelItem(object instance)
736 return this.GetModelItem(instance, false);
739 public ModelItem GetModelItem(object instance, bool shouldExpandModelTree)
741 if (instance == null)
746 ModelItem modelItem = null;
747 WeakReference mappedModelItem = null;
748 objectMap.TryGetValue(instance, out mappedModelItem);
749 if (mappedModelItem != null)
751 modelItem = (ModelItem)mappedModelItem.Target;
754 if (modelItem == null && shouldExpandModelTree)
756 if (instance is ValueType || instance is string)
761 modelItem = FindFirst(this.Root, (m) => { return m.GetCurrentValue() == instance; });
767 internal void RegisterModelTreeChangeEvents(EditingScope editingScope)
769 this.ModelItemsAdded += editingScope.EditingScope_ModelItemsAdded;
770 this.ModelItemsRemoved += editingScope.EditingScope_ModelItemsRemoved;
773 internal void UnregisterModelTreeChangeEvents(EditingScope editingScope)
775 this.ModelItemsAdded -= editingScope.EditingScope_ModelItemsAdded;
776 this.ModelItemsRemoved -= editingScope.EditingScope_ModelItemsRemoved;
779 // The method should be called when an EditingScope completed. But if the EditingScope has an outer EditingScope,
780 // Changes are not applied, so itemsAdded and itemsRemoved won't update until the outer EditingScope.Complete()
781 internal void OnEditingScopeCompleted(EditingScope modelEditingScopeImpl)
783 if (editingScopes.Contains(modelEditingScopeImpl))
788 if (editingScopes.Count == 0 && this.immediateEditingScope != null)
790 // if immediateEditingScope is in place and last nested normal editing scope completes,
791 // we copy the information from the nested normal editing scope to the immediateEditingScope
792 // and put the Changes into undo engine, so that when immediateEditingScope completes the undo unit
793 // generated by nested editing scope will be collected as one undo unit
794 if (this.immediateEditingScope != modelEditingScopeImpl)
796 UndoEngine undoEngine = this.Context.Services.GetService<UndoEngine>();
798 this.immediateEditingScope.Changes.AddRange(modelEditingScopeImpl.Changes);
799 this.immediateEditingScope.HasModelChanges = this.immediateEditingScope.HasModelChanges || modelEditingScopeImpl.HasModelChanges;
800 this.immediateEditingScope.HasEffectiveChanges = this.immediateEditingScope.HasEffectiveChanges || modelEditingScopeImpl.HasEffectiveChanges;
801 this.immediateEditingScope.HandleModelItemsAdded(modelEditingScopeImpl.ItemsAdded);
802 this.immediateEditingScope.HandleModelItemsRemoved(modelEditingScopeImpl.ItemsRemoved);
804 if (modelEditingScopeImpl.HasEffectiveChanges)
806 if (!this.RedoUndoInProgress && !modelEditingScopeImpl.SuppressUndo && undoEngine != null)
808 undoEngine.AddUndoUnit(new EditingScopeUndoUnit(this.Context, this, modelEditingScopeImpl));
814 if (editingScopes.Count == 0 && !(modelEditingScopeImpl is ImmediateEditingScope) && modelEditingScopeImpl.HasModelChanges)
816 if (!modelEditingScopeImpl.SuppressValidationOnComplete)
818 ValidationService validationService = this.Context.Services.GetService<ValidationService>();
819 if (validationService != null)
821 validationService.ValidateWorkflow(ValidationReason.ModelChange);
826 if (this.immediateEditingScope == modelEditingScopeImpl)
828 this.immediateEditingScope = null;
831 // if the outer most scope completed notify listeners
832 if (this.EditingScopeCompleted != null && editingScopes.Count == 0 && this.immediateEditingScope == null)
834 this.EditingScopeCompleted(this, new EditingScopeEventArgs() { EditingScope = modelEditingScopeImpl });
837 Fx.Assert(editingScopes.Count == 0 || (modelEditingScopeImpl.ItemsAdded.Count == 0 && modelEditingScopeImpl.ItemsRemoved.Count == 0), "Inner editing scope shouldn't have changes applied.");
839 this.graphManager.VerifyBackPointers();
843 internal bool CanEditingScopeComplete(EditingScope modelEditingScopeImpl)
845 ReadOnlyState readOnlyState = this.Context.Items.GetValue<ReadOnlyState>();
846 return (modelEditingScopeImpl == editingScopes.Peek()) && (readOnlyState == null || !readOnlyState.IsReadOnly);
849 internal void OnEditingScopeReverted(EditingScope modelEditingScopeImpl)
851 if (editingScopes.Contains(modelEditingScopeImpl))
856 if (this.immediateEditingScope == modelEditingScopeImpl)
858 this.immediateEditingScope = null;
862 internal static IList<ModelItem> DepthFirstSearch(ModelItem currentItem, Predicate<Type> filter, Predicate<ModelItem> shouldTraverseSubTree, bool preOrder)
864 IList<ModelItem> foundItems = new List<ModelItem>();
865 HashSet<ModelItem> alreadyVisitedItems = new HashSet<ModelItem>();
866 RecursiveDepthFirstSearch(currentItem, filter, shouldTraverseSubTree, foundItems, alreadyVisitedItems, preOrder);
870 private static void RecursiveDepthFirstSearch(ModelItem currentItem, Predicate<Type> filter, Predicate<ModelItem> shouldTraverseSubTree, IList<ModelItem> foundItems, HashSet<ModelItem> alreadyVisitedItems, bool preOrder)
872 if (currentItem == null)
877 if (typeof(Type).IsAssignableFrom(currentItem.ItemType))
882 if (currentItem.ItemType.IsGenericType && currentItem.ItemType.GetGenericTypeDefinition() == typeof(Action<>))
887 if (!shouldTraverseSubTree(currentItem))
894 if (filter(currentItem.ItemType))
896 foundItems.Add(currentItem);
900 alreadyVisitedItems.Add(currentItem);
902 List<ModelItem> neighbors = GetNeighbors(currentItem, (m) => true);
903 foreach (ModelItem neighbor in neighbors)
905 if (!alreadyVisitedItems.Contains(neighbor))
907 RecursiveDepthFirstSearch(neighbor, filter, shouldTraverseSubTree, foundItems, alreadyVisitedItems, preOrder);
913 if (filter(currentItem.ItemType))
915 foundItems.Add(currentItem);
920 private void OnModelItemsAdded(IEnumerable<ModelItem> addedModelItems)
922 Fx.Assert(addedModelItems != null, "addedModelItems should not be null.");
923 EventHandler<ModelItemsAddedEventArgs> tempModelItemsAdded = this.ModelItemsAdded;
924 if (tempModelItemsAdded != null)
926 tempModelItemsAdded(this, new ModelItemsAddedEventArgs(addedModelItems));
930 private void OnModelItemsRemoved(IEnumerable<ModelItem> removedModelItems)
932 Fx.Assert(removedModelItems != null, "removedModelItems should not be null.");
933 EventHandler<ModelItemsRemovedEventArgs> tempModelItemsRemoved = this.ModelItemsRemoved;
934 if (tempModelItemsRemoved != null)
936 tempModelItemsRemoved(this, new ModelItemsRemovedEventArgs(removedModelItems));
940 internal class ModelGraphManager : GraphManager<ModelItem, Edge, BackPointer>
942 private ModelTreeManager modelTreeManager;
944 public ModelGraphManager(ModelTreeManager modelTreeManager)
946 Fx.Assert(modelTreeManager != null, "modelTreeManager should not be null");
948 this.modelTreeManager = modelTreeManager;
951 public void OnPropertyEdgeAdded(string propertyName, ModelItem from, ModelItem to)
953 base.OnEdgeAdded(new Edge(propertyName, from, to));
956 public void OnItemEdgeAdded(ModelItem from, ModelItem to)
958 base.OnEdgeAdded(new Edge(from, to));
961 public void OnPropertyEdgeRemoved(string propertyName, ModelItem from, ModelItem to)
963 base.OnEdgeRemoved(new Edge(propertyName, from, to));
966 public void OnItemEdgeRemoved(ModelItem from, ModelItem to)
968 base.OnEdgeRemoved(new Edge(from, to));
971 public new void OnRootChanged(ModelItem oldRoot, ModelItem newRoot)
973 base.OnRootChanged(oldRoot, newRoot);
976 protected override ModelItem Root
978 get { return this.modelTreeManager.Root; }
981 protected override IEnumerable<ModelItem> GetVertices()
983 foreach (WeakReference weakReference in this.modelTreeManager.objectMap.Values)
985 ModelItem modelItem = weakReference.Target as ModelItem;
986 if (modelItem != null)
988 yield return modelItem;
993 // This method will not expand any ModelItem
994 protected override IEnumerable<Edge> GetOutEdges(ModelItem vertex)
996 Fx.Assert(vertex != null, "vertex should not be null");
998 List<Edge> edges = new List<Edge>();
1000 foreach (KeyValuePair<string, ModelItem> kvp in ((IModelTreeItem)vertex).ModelPropertyStore)
1002 if (kvp.Value != null)
1004 edges.Add(new Edge(kvp.Key, vertex, kvp.Value));
1008 ModelItemCollection collection = vertex as ModelItemCollection;
1009 if (collection != null)
1011 foreach (ModelItem modelItem in collection.Distinct())
1013 if (modelItem != null)
1015 edges.Add(new Edge(vertex, modelItem));
1020 ModelItemDictionaryImpl dictionary = vertex as ModelItemDictionaryImpl;
1021 if (dictionary != null)
1023 List<ModelItem> items = new List<ModelItem>(dictionary.Keys);
1024 items.AddRange(dictionary.Values);
1025 items.Add(dictionary.updateKeySavedValue);
1026 foreach (ModelItem modelItem in items.Distinct())
1028 if (modelItem != null)
1030 edges.Add(new Edge(vertex, modelItem));
1038 protected override IEnumerable<BackPointer> GetBackPointers(ModelItem vertex)
1040 List<BackPointer> backPointers = new List<BackPointer>();
1042 foreach (ModelItem parent in ((IModelTreeItem)vertex).ItemBackPointers)
1044 backPointers.Add(new BackPointer(vertex, parent));
1047 foreach (ModelProperty property in vertex.Sources)
1049 backPointers.Add(new BackPointer(property.Name, vertex, property.Parent));
1052 foreach (BackPointer backPointer in ((IModelTreeItem)vertex).ExtraPropertyBackPointers)
1054 backPointers.Add(backPointer);
1057 return backPointers;
1060 protected override ModelItem GetDestinationVertexFromEdge(Edge edge)
1062 return edge.DestinationVertex;
1065 protected override ModelItem GetSourceVertexFromEdge(Edge edge)
1067 return edge.SourceVertex;
1070 protected override ModelItem GetDestinationVertexFromBackPointer(BackPointer backPointer)
1072 return backPointer.DestinationVertex;
1075 protected override void RemoveAssociatedBackPointer(Edge edge)
1077 if (edge.LinkType == LinkType.Property)
1079 ModelProperty modelProperty = edge.SourceVertex.Properties[edge.PropertyName];
1080 if (modelProperty != null)
1082 ((IModelTreeItem)edge.DestinationVertex).RemoveSource(modelProperty);
1086 // in case of custom type descriptor, it may return a list of ModelProperties different from IModelTreeItem.ModelPropertyStore
1087 // manually manipulate IModelTreeItem.Sources to remove back pointer
1088 ((IModelTreeItem)edge.DestinationVertex).RemoveSource(edge.SourceVertex, edge.PropertyName);
1093 Fx.Assert(edge.LinkType == LinkType.Item, "unknown LinkType");
1094 ((IModelTreeItem)edge.DestinationVertex).RemoveParent(edge.SourceVertex);
1098 protected override void AddAssociatedBackPointer(Edge edge)
1100 if (edge.LinkType == LinkType.Property)
1102 ModelProperty modelProperty = edge.SourceVertex.Properties[edge.PropertyName];
1103 if (modelProperty != null)
1105 ((IModelTreeItem)edge.DestinationVertex).SetSource(modelProperty);
1109 ((IModelTreeItem)edge.DestinationVertex).ExtraPropertyBackPointers.Add(new BackPointer(edge.PropertyName, edge.DestinationVertex, edge.SourceVertex));
1114 Fx.Assert(edge.LinkType == LinkType.Item, "unknown LinkType");
1115 ((IModelTreeItem)edge.DestinationVertex).SetParent(edge.SourceVertex);
1119 protected override bool HasBackPointer(Edge edge)
1121 ModelItem from = edge.SourceVertex;
1123 if (edge.LinkType == LinkType.Property)
1125 foreach (ModelProperty p in edge.DestinationVertex.Sources)
1127 if (p.Parent == from && p.Name == edge.PropertyName)
1133 foreach (BackPointer bp in ((IModelTreeItem)edge.DestinationVertex).ExtraPropertyBackPointers)
1135 if (bp.DestinationVertex == from && bp.PropertyName == edge.PropertyName)
1145 Fx.Assert(edge.LinkType == LinkType.Item, "unknown LinkType");
1146 return ((IModelTreeItem)edge.DestinationVertex).ItemBackPointers.Contains(from);
1150 protected override bool HasAssociatedEdge(BackPointer backPointer)
1152 if (backPointer.LinkType == LinkType.Property)
1154 return ((IModelTreeItem)backPointer.DestinationVertex).ModelPropertyStore[backPointer.PropertyName] == backPointer.SourceVertex;
1158 Fx.Assert(backPointer.LinkType == LinkType.Item, "unknown LinkType");
1160 ModelItemCollection collection = backPointer.DestinationVertex as ModelItemCollection;
1161 if (collection != null)
1163 return collection.Contains(backPointer.SourceVertex);
1166 ModelItemDictionary dictionary = (ModelItemDictionary)backPointer.DestinationVertex;
1167 return dictionary.Keys.Concat(dictionary.Values).Contains(backPointer.SourceVertex);
1171 protected override void OnVerticesBecameReachable(IEnumerable<ModelItem> reachableVertices)
1173 Fx.Assert(reachableVertices != null, "reachableVertices should not be null");
1174 this.modelTreeManager.OnModelItemsAdded(reachableVertices);
1177 protected override void OnVerticesBecameUnreachable(IEnumerable<ModelItem> unreachableVertices)
1179 Fx.Assert(unreachableVertices != null, "unreachableVertices should not be null");
1180 this.modelTreeManager.OnModelItemsRemoved(unreachableVertices);
1184 class DictionaryTypeDescriptionProvider : TypeDescriptionProvider
1187 public DictionaryTypeDescriptionProvider(Type type)
1188 : base(TypeDescriptor.GetProvider(type))
1193 public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
1195 ICustomTypeDescriptor defaultDescriptor = base.GetTypeDescriptor(objectType, instance);
1196 return new DictionaryTypeDescriptor(defaultDescriptor, this.type);
1200 class DictionaryTypeDescriptor : CustomTypeDescriptor
1204 public DictionaryTypeDescriptor(ICustomTypeDescriptor parent, Type type)
1210 // Expose one more property, a collection of MutableKeyValuePairs, described by ItemsCollectionPropertyDescriptor
1211 public override PropertyDescriptorCollection GetProperties()
1213 return new PropertyDescriptorCollection(base.GetProperties().Cast<PropertyDescriptor>()
1214 .Union(new PropertyDescriptor[] { new ItemsCollectionPropertyDescriptor(type) }).ToArray());
1217 // Expose one more property, a collection of MutableKeyValuePairs, described by ItemsCollectionPropertyDescriptor
1218 public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
1220 return new PropertyDescriptorCollection(base.GetProperties(attributes).Cast<PropertyDescriptor>()
1221 .Union(new PropertyDescriptor[] { new ItemsCollectionPropertyDescriptor(type) }).ToArray());
1225 class ItemsCollectionPropertyDescriptor : PropertyDescriptor
1227 Type dictionaryType;
1228 Type[] genericArguments;
1233 internal ItemsCollectionPropertyDescriptor(Type type)
1234 : base("ItemsCollection", null)
1236 this.dictionaryType = type;
1239 Type[] GenericArguments
1243 if (this.genericArguments == null)
1245 object[] result = new object[2] { false, false };
1246 Type[] interfaces = this.ComponentType.FindInterfaces(ModelTreeManager.CheckInterface, result);
1247 foreach (Type type in interfaces)
1249 if (type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
1251 this.genericArguments = type.GetGenericArguments();
1252 Fx.Assert(this.genericArguments.Length == 2, "this.genericArguments.Length should be = 2");
1253 return this.genericArguments;
1256 Debug.Fail("Cannot find generic arguments for IDictionary<,>.");
1258 return this.genericArguments;
1262 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This is intended for use through reflection")]
1267 if (this.kvpairType == null)
1269 this.kvpairType = typeof(KeyValuePair<,>).MakeGenericType(this.GenericArguments);
1271 return this.kvpairType;
1275 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This is intended for use through reflection")]
1280 if (this.itemType == null)
1282 this.itemType = typeof(ModelItemKeyValuePair<,>).MakeGenericType(this.GenericArguments);
1284 return this.itemType;
1288 public override Type ComponentType
1290 get { return this.dictionaryType; }
1293 public override bool IsReadOnly
1301 public override Type PropertyType
1305 if (this.propertyType == null)
1307 this.propertyType = typeof(DictionaryItemsCollection<,>).MakeGenericType(this.GenericArguments);
1309 return this.propertyType;
1313 public override bool IsBrowsable
1321 public override bool CanResetValue(object component)
1326 public override object GetValue(object component)
1328 return Activator.CreateInstance(this.PropertyType, new object[] { component });
1331 public override void ResetValue(object component)
1333 Debug.Fail("ResetValue is not implemented.");
1334 throw FxTrace.Exception.AsError(new NotImplementedException());
1337 public override void SetValue(object component, object value)
1339 Debug.Fail("SetValue is not implemented.");
1340 throw FxTrace.Exception.AsError(new NotImplementedException());
1343 public override bool ShouldSerializeValue(object component)