1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Model
6 using System.Activities.Presentation.Services;
7 using System.Collections;
8 using System.Collections.Generic;
9 using System.Collections.Specialized;
10 using System.ComponentModel;
11 using System.Diagnostics;
12 using System.Diagnostics.CodeAnalysis;
13 using System.Globalization;
15 using System.Reflection;
17 using System.Runtime.Collections;
19 using System.Windows.Markup;
21 internal class ModelItemDictionaryImpl : ModelItemDictionary, IModelTreeItem, ICustomTypeDescriptor
23 ModelProperty contentProperty;
24 DictionaryWrapper instance;
26 private NullableKeyDictionary<ModelItem, ModelItem> modelItems;
27 internal ModelItem updateKeySavedValue;
28 Dictionary<string, ModelItem> modelPropertyStore;
29 ModelTreeManager modelTreeManager;
30 ModelProperty nameProperty;
31 List<ModelItem> parents;
32 ModelPropertyCollectionImpl properties;
33 List<ModelProperty> sources;
34 ModelTreeItemHelper helper;
35 List<ModelItem> subTreeNodesThatNeedBackLinkPatching;
36 DependencyObject view;
37 ModelItem manuallySetParent;
39 [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "This is internal code with no derived class")]
40 public ModelItemDictionaryImpl(ModelTreeManager modelTreeManager, Type itemType, Object instance, ModelItem parent)
42 Fx.Assert(modelTreeManager != null, "modelTreeManager cannot be null");
43 Fx.Assert(itemType != null, "item type cannot be null");
44 Fx.Assert(instance != null, "instance cannot be null");
45 this.itemType = itemType;
46 this.instance = new DictionaryWrapper(instance);
47 this.modelTreeManager = modelTreeManager;
48 this.parents = new List<ModelItem>(1);
49 this.sources = new List<ModelProperty>(1);
50 this.helper = new ModelTreeItemHelper();
53 this.manuallySetParent = parent;
55 this.modelPropertyStore = new Dictionary<string, ModelItem>();
56 this.subTreeNodesThatNeedBackLinkPatching = new List<ModelItem>();
57 this.modelItems = new NullableKeyDictionary<ModelItem, ModelItem>();
61 if (ItemsCollectionObject != null)
63 ItemsCollectionModelItemCollection.CollectionChanged += new NotifyCollectionChangedEventHandler(itemsCollection_CollectionChanged);
64 this.ItemsCollectionObject.ModelDictionary = this;
68 Type itemsCollectionKVPType = null;
69 Type ItemsCollectionKVPType
73 if (itemsCollectionKVPType == null)
75 if (ItemsCollectionModelItemCollection != null)
77 Type itemsCollectionType = ItemsCollectionModelItemCollection.ItemType;
78 Type[] genericArguments = itemsCollectionType.GetGenericArguments();
79 this.itemsCollectionKVPType = typeof(ModelItemKeyValuePair<,>).MakeGenericType(genericArguments);
82 return itemsCollectionKVPType;
86 ModelItemCollection ItemsCollectionModelItemCollection
90 return this.Properties["ItemsCollection"].Collection;
94 IItemsCollection ItemsCollectionObject
98 IItemsCollection itemsCollectionObject = null;
99 if (this.ItemsCollectionModelItemCollection != null)
101 itemsCollectionObject = ItemsCollectionModelItemCollection.GetCurrentValue() as IItemsCollection;
103 return itemsCollectionObject;
107 private bool EditInProgress { get; set; }
109 internal void UpdateValue(object keyObj, object valueObj)
111 ModelItem key = null;
112 bool keyFound = this.KeyAsModelItem(keyObj, false, out key);
113 Fx.Assert(keyFound, "The key should already exist in the current dictionary");
114 ModelItem value = this.WrapObject(valueObj);
115 this.EditCore(key, value, false);
118 internal void UpdateKey(object oldKeyObj, object newKeyObj)
120 if (oldKeyObj != newKeyObj)
122 ModelItem newKey = null;
123 this.KeyAsModelItem(newKeyObj, true, out newKey);
124 ModelItem oldKey = null;
125 bool oldKeyFound = this.KeyAsModelItem(oldKeyObj, false, out oldKey);
126 Fx.Assert(oldKeyFound, "The old key should already exist in the current dictionary");
130 this.EditInProgress = true;
131 Fx.Assert(this.instance != null, "instance should not be null");
133 bool wasNewKeyInKeysOrValuesCollection = newKey != null && this.IsInKeysOrValuesCollection(newKey);
135 ModelItem value = this.modelItems[oldKey];
136 this.modelItems.Remove(oldKey);
138 this.updateKeySavedValue = value;
140 if (oldKey != null && !this.IsInKeysOrValuesCollection(oldKey))
142 this.modelTreeManager.OnItemEdgeRemoved(this, oldKey);
145 this.updateKeySavedValue = null;
146 this.modelItems[newKey] = value;
148 if (newKey != null && !wasNewKeyInKeysOrValuesCollection)
150 this.modelTreeManager.OnItemEdgeAdded(this, newKey);
153 if (null != this.CollectionChanged)
155 this.CollectionChanged(this,
156 new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,
157 new KeyValuePair<ModelItem, ModelItem>(newKey, value),
158 new KeyValuePair<ModelItem, ModelItem>(oldKey, value)));
163 this.EditInProgress = false;
168 void itemsCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
170 //If we're in editing, then we don't trigger an update
176 if (e.Action == NotifyCollectionChangedAction.Add)
178 foreach (ModelItem item in e.NewItems)
180 ModelItem key = item.Properties["Key"] == null ? null : item.Properties["Key"].Value;
181 ModelItem value = item.Properties["Value"] == null ? null : item.Properties["Value"].Value;
182 this.AddCore(key, value, false);
185 else if (e.Action == NotifyCollectionChangedAction.Remove)
187 foreach (ModelItem item in e.OldItems)
189 object keyObject = item.Properties["Key"].Value == null ? null : item.Properties["Key"].Value.GetCurrentValue();
190 ModelItem key = null;
191 bool keyFound = KeyAsModelItem(keyObject, false, out key);
192 Fx.Assert(keyFound, "Key should exist in the current dictionary");
193 this.RemoveCore(key, false);
196 else if (e.Action == NotifyCollectionChangedAction.Replace)
198 Fx.Assert(e.NewItems != null && e.OldItems != null && e.NewItems.Count == e.OldItems.Count,
199 "there must be equal number of old and new items");
201 foreach (ModelItem item in e.NewItems)
203 object keyObject = item.Properties["Key"].Value == null ? null : item.Properties["Key"].Value.GetCurrentValue();
204 ModelItem key = null;
205 bool keyFound = KeyAsModelItem(keyObject, false, out key);
206 Fx.Assert(keyFound, "Key should exist in the current dictionary");
207 ModelItem value = item.Properties["Value"] == null ? null : item.Properties["Value"].Value;
208 this.EditCore(key, value, false);
211 else if (e.Action == NotifyCollectionChangedAction.Reset)
214 if (this.CollectionChanged != null)
216 this.CollectionChanged(this,
217 new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
220 //note we do not handle NotifyCollectionChangedAction.Move as we don't expect it nor can Dictionary do a move operation
223 public override event NotifyCollectionChangedEventHandler CollectionChanged;
225 public override event PropertyChangedEventHandler PropertyChanged;
227 public override int Count
229 get { return this.instance.Count; }
232 public override bool IsReadOnly
234 get { return this.instance.IsReadOnly; }
237 public override ICollection<ModelItem> Keys
239 get { return this.modelItems.Keys; }
242 public override ICollection<ModelItem> Values
244 get { return this.modelItems.Values; }
247 public override AttributeCollection Attributes
251 Fx.Assert(null != this.itemType, "ItemType cannot be null!");
252 return TypeDescriptor.GetAttributes(this.itemType);
256 public override ModelProperty Content
260 if (this.contentProperty == null)
262 Fx.Assert(this.instance != null, "instance cannot be null");
263 ContentPropertyAttribute contentAttribute = TypeDescriptor.GetAttributes(this.instance.Value)[typeof(ContentPropertyAttribute)] as ContentPropertyAttribute;
264 if (contentAttribute != null && !String.IsNullOrEmpty(contentAttribute.Name))
266 this.contentProperty = this.Properties.Find(contentAttribute.Name);
269 return contentProperty;
273 public override Type ItemType
275 get { return this.itemType; }
278 public override string Name
283 if ((this.NameProperty != null) && (this.NameProperty.Value != null))
285 name = (string)this.NameProperty.Value.GetCurrentValue();
291 if (null != this.NameProperty)
293 this.NameProperty.SetValue(value);
298 public override ModelItem Parent
302 return (this.Parents.Count() > 0) ? this.Parents.First() : null;
307 public override ModelItem Root
309 get { return this.modelTreeManager.Root; }
312 public override ModelPropertyCollection Properties
316 if (this.properties == null)
318 this.properties = new ModelPropertyCollectionImpl(this);
320 return this.properties;
324 public override ModelProperty Source
328 return (this.sources.Count > 0) ? this.sources.First() : null;
332 public override DependencyObject View
334 get { return this.view; }
337 public ModelItem ModelItem
342 public Dictionary<string, ModelItem> ModelPropertyStore
344 get { return this.modelPropertyStore; }
347 public ModelTreeManager ModelTreeManager
349 get { return this.modelTreeManager; }
353 public override IEnumerable<ModelItem> Parents
357 if (this.manuallySetParent != null)
359 List<ModelItem> list = new List<ModelItem>();
360 list.Add(this.manuallySetParent);
361 return list.Concat(this.parents).Concat(
362 from source in this.sources
363 select source.Parent);
366 return this.parents.Concat(
367 from source in this.sources
368 select source.Parent);
372 public override IEnumerable<ModelProperty> Sources
380 protected ModelProperty NameProperty
384 if (this.nameProperty == null)
386 Fx.Assert(this.instance != null, "instance cannot be null");
387 RuntimeNamePropertyAttribute runtimeNamePropertyAttribute = TypeDescriptor.GetAttributes(this.instance.Value)[typeof(RuntimeNamePropertyAttribute)] as RuntimeNamePropertyAttribute;
388 if (runtimeNamePropertyAttribute != null && !String.IsNullOrEmpty(runtimeNamePropertyAttribute.Name))
390 this.nameProperty = this.Properties.Find(runtimeNamePropertyAttribute.Name);
394 this.nameProperty = this.Properties.FirstOrDefault<ModelProperty>(p => (0 == string.Compare(p.Name, "Name", StringComparison.OrdinalIgnoreCase)));
401 public override ModelItem this[ModelItem key]
405 return this.modelItems[key];
409 if (this.instance.IsReadOnly)
411 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CollectionIsReadOnly));
414 ModelItem oldValue = null;
415 if (this.modelItems.TryGetValue(key, out oldValue))
417 this.modelTreeManager.DictionaryEdit(this, key, value, oldValue);
421 this.modelTreeManager.DictionaryAdd(this, key, value);
426 public override ModelItem this[object key]
430 ModelItem keyItem = null;
431 bool keyFound = this.KeyAsModelItem(key, false, out keyItem);
434 throw FxTrace.Exception.AsError(new KeyNotFoundException(key.ToString()));
437 return this[keyItem];
441 if (this.instance.IsReadOnly)
443 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CollectionIsReadOnly));
446 ModelItem keyItem = null;
447 this.KeyAsModelItem(key, true, out keyItem);
448 this[keyItem] = value;
452 public override void Add(ModelItem key, ModelItem value)
454 if (this.instance.IsReadOnly)
456 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CollectionIsReadOnly));
458 this.modelTreeManager.DictionaryAdd(this, key, value);
461 public override ModelItem Add(object key, object value)
463 if (this.instance.IsReadOnly)
465 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CollectionIsReadOnly));
467 ModelItem keyModelItem = key as ModelItem ?? this.WrapObject(key);
468 ModelItem valueModelItem = value as ModelItem ?? this.WrapObject(value);
469 this.Add(keyModelItem, valueModelItem);
470 return valueModelItem;
473 public override void Clear()
475 this.modelTreeManager.DictionaryClear(this);
478 public override bool ContainsKey(ModelItem key)
480 return this.modelItems.Keys.Contains<ModelItem>(key);
483 public override bool ContainsKey(object key)
485 ModelItem keyItem = key as ModelItem;
489 return this.ContainsKey(keyItem);
492 return this.KeyAsModelItem(key, false, out keyItem);
495 public override IEnumerator<KeyValuePair<ModelItem, ModelItem>> GetEnumerator()
497 return this.modelItems.GetEnumerator();
500 public override bool Remove(ModelItem key)
502 this.modelTreeManager.DictionaryRemove(this, key);
506 public override bool Remove(object key)
508 ModelItem keyItem = null;
509 if (!this.KeyAsModelItem(key, false, out keyItem))
514 return this.Remove(keyItem);
517 public override bool TryGetValue(ModelItem key, out ModelItem value)
519 return this.modelItems.TryGetValue(key, out value);
522 public override bool TryGetValue(object key, out ModelItem value)
524 ModelItem keyItem = null;
525 if (!this.KeyAsModelItem(key, false, out keyItem))
531 return this.TryGetValue(keyItem, out value);
534 public override ModelEditingScope BeginEdit(string description, bool shouldApplyChangesImmediately)
536 return ModelItemHelper.ModelItemBeginEdit(this.modelTreeManager, description, shouldApplyChangesImmediately);
539 public override ModelEditingScope BeginEdit(bool shouldApplyChangesImmediately)
541 return this.BeginEdit(null, shouldApplyChangesImmediately);
544 public override ModelEditingScope BeginEdit(string description)
546 return this.BeginEdit(description, false);
549 public override ModelEditingScope BeginEdit()
551 return this.BeginEdit(null);
554 public override object GetCurrentValue()
556 return this.instance.Value;
559 #region IModelTreeItem Members
561 public void OnPropertyChanged(string propertyName)
563 if (null != this.PropertyChanged)
565 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
569 void IModelTreeItem.SetParent(ModelItem dataModelItem)
571 if (this.manuallySetParent == dataModelItem)
573 this.manuallySetParent = null;
576 if (!this.parents.Contains(dataModelItem))
578 this.parents.Add(dataModelItem);
582 void IModelTreeItem.SetSource(ModelProperty property)
584 if (!this.sources.Contains(property))
586 // also check if the same parent.property is in the list as a different instance of oldModelProperty
587 ModelProperty foundProperty = this.sources.Find((modelProperty) => modelProperty.Name.Equals(property.Name) && property.Parent == modelProperty.Parent);
588 if (foundProperty == null)
590 this.sources.Add(property);
595 public void SetCurrentView(DependencyObject view)
602 void IModelTreeItem.RemoveParent(ModelItem oldParent)
604 if (this.manuallySetParent == oldParent)
606 this.manuallySetParent = null;
609 if (this.parents.Contains(oldParent))
611 this.parents.Remove(oldParent);
615 IEnumerable<ModelItem> IModelTreeItem.ItemBackPointers
617 get { return this.parents; }
620 List<BackPointer> IModelTreeItem.ExtraPropertyBackPointers
622 get { return this.helper.ExtraPropertyBackPointers; }
625 void IModelTreeItem.RemoveSource(ModelProperty oldModelProperty)
627 if (this.sources.Contains(oldModelProperty))
629 this.sources.Remove(oldModelProperty);
633 ((IModelTreeItem)this).RemoveSource(oldModelProperty.Parent, oldModelProperty.Name);
637 void IModelTreeItem.RemoveSource(ModelItem parent, string propertyName)
639 // also check if the same parent.property is in the list as a different instance of oldModelProperty
640 ModelProperty foundProperty = this.sources.FirstOrDefault<ModelProperty>((modelProperty) => modelProperty.Name.Equals(propertyName) && modelProperty.Parent == parent);
641 if (foundProperty != null)
643 this.sources.Remove(foundProperty);
647 this.helper.RemoveExtraPropertyBackPointer(parent, propertyName);
651 internal void EditCore(ModelItem key, ModelItem value)
653 this.EditCore(key, value, true);
656 private void EditCore(ModelItem key, ModelItem value, bool updateInstance)
660 ModelItem oldValue = this.modelItems[key];
661 this.EditInProgress = true;
662 Fx.Assert(this.instance != null, "instance should not be null");
664 bool wasValueInKeysOrValuesCollection = this.IsInKeysOrValuesCollection(value);
668 this.instance[(key == null) ? null : key.GetCurrentValue()] = null != value ? value.GetCurrentValue() : null;
669 //this also makes sure ItemsCollectionModelItemCollection is not null
670 if (ItemsCollectionObject != null)
674 ItemsCollectionObject.ShouldUpdateDictionary = false;
676 foreach (ModelItem item in ItemsCollectionModelItemCollection)
678 ModelItem keyInCollection = item.Properties["Key"].Value;
679 bool found = (key == keyInCollection);
681 if (!found && key != null && keyInCollection != null)
683 object keyValue = key.GetCurrentValue();
685 // ValueType do not share ModelItem, a ModelItem is always created for a ValueType
686 // ModelTreeManager always create a ModelItem even for the same string
687 // So, we compare object instance instead of ModelItem for above cases.
688 if (keyValue is ValueType || keyValue is string)
690 found = keyValue.Equals(keyInCollection.GetCurrentValue());
696 ModelPropertyImpl valueImpl = item.Properties["Value"] as ModelPropertyImpl;
697 if (valueImpl != null)
699 valueImpl.SetValueCore(value);
706 ItemsCollectionObject.ShouldUpdateDictionary = true;
711 this.modelItems[key] = null;
712 if (oldValue != null && !this.IsInKeysOrValuesCollection(oldValue))
714 this.modelTreeManager.OnItemEdgeRemoved(this, oldValue);
717 this.modelItems[key] = value;
718 if (value != null && !wasValueInKeysOrValuesCollection)
720 this.modelTreeManager.OnItemEdgeAdded(this, value);
723 if (null != this.CollectionChanged)
725 this.CollectionChanged(this,
726 new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,
727 new KeyValuePair<ModelItem, ModelItem>(key, value),
728 new KeyValuePair<ModelItem, ModelItem>(key, oldValue)));
733 this.EditInProgress = false;
737 internal void AddCore(ModelItem key, ModelItem value)
739 this.AddCore(key, value, true);
742 private void AddCore(ModelItem key, ModelItem value, bool updateInstance)
746 this.EditInProgress = true;
747 Fx.Assert(this.instance != null, "instance should not be null");
749 bool wasKeyInKeysOrValuesCollection = key != null && this.IsInKeysOrValuesCollection(key);
750 bool wasValueInKeysOrValuesCollection = value != null && this.IsInKeysOrValuesCollection(value);
754 //no need to [....] if the ItemsCollection is not DictionaryItemsCollection wrapped by ModelItemCollectionImpl
755 ModelItemCollectionImpl itemsCollectionImpl = this.ItemsCollectionModelItemCollection as ModelItemCollectionImpl;
756 if (ItemsCollectionObject != null && itemsCollectionImpl != null)
760 ItemsCollectionObject.ShouldUpdateDictionary = false;
761 object mutableKVPair = Activator.CreateInstance(this.ItemsCollectionKVPType, new object[] { key == null ? null : key.GetCurrentValue(), value != null ? value.GetCurrentValue() : null });
762 ModelItem mutableKVPairItem = this.modelTreeManager.WrapAsModelItem(mutableKVPair);
764 itemsCollectionImpl.AddCore(mutableKVPairItem);
768 ItemsCollectionObject.ShouldUpdateDictionary = true;
771 this.instance.Add(key == null ? null : key.GetCurrentValue(), null != value ? value.GetCurrentValue() : null);
774 this.modelItems.Add(key, value);
776 if (key != null && !wasKeyInKeysOrValuesCollection)
778 this.modelTreeManager.OnItemEdgeAdded(this, key);
781 if (value != null && !wasValueInKeysOrValuesCollection && value != key)
783 this.modelTreeManager.OnItemEdgeAdded(this, value);
786 if (null != this.CollectionChanged)
788 this.CollectionChanged(this,
789 new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,
790 new KeyValuePair<ModelItem, ModelItem>(key, value)));
795 this.EditInProgress = false;
799 internal void ClearCore()
801 this.ClearCore(true);
804 private void ClearCore(bool updateInstance)
808 this.EditInProgress = true;
809 Fx.Assert(this.instance != null, "instance should not be null");
810 IList removed = this.modelItems.ToList<KeyValuePair<ModelItem, ModelItem>>();
813 //no need to [....] if the ItemsCollection is not DictionaryItemsCollection wrapped by ModelItemCollectionImpl
814 ModelItemCollectionImpl itemsCollectionImpl = this.ItemsCollectionModelItemCollection as ModelItemCollectionImpl;
815 if (ItemsCollectionObject != null && itemsCollectionImpl != null)
819 ItemsCollectionObject.ShouldUpdateDictionary = false;
820 itemsCollectionImpl.ClearCore();
824 ItemsCollectionObject.ShouldUpdateDictionary = true;
827 this.instance.Clear();
829 List<ModelItem> removedItems = new List<ModelItem>(this.modelItems.Keys.Concat(this.modelItems.Values).Distinct());
830 this.modelItems.Clear();
832 foreach (ModelItem item in removedItems.Distinct())
836 this.modelTreeManager.OnItemEdgeRemoved(this, item);
840 if (null != this.CollectionChanged)
842 this.CollectionChanged(this,
843 new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
848 this.EditInProgress = false;
852 internal void RemoveCore(ModelItem key)
854 this.RemoveCore(key, true);
857 private bool IsInKeysOrValuesCollection(ModelItem modelItem)
859 foreach (ModelItem item in this.modelItems.Keys)
861 if (item == modelItem)
867 foreach (ModelItem item in this.modelItems.Values)
869 if (item == modelItem)
878 private void RemoveCore(ModelItem key, bool updateInstance)
882 this.EditInProgress = true;
883 Fx.Assert(this.instance != null, "instance should not be null");
884 ModelItem value = this.modelItems[key];
885 this.modelItems.Remove(key);
887 if (key != null && !this.IsInKeysOrValuesCollection(key))
889 this.modelTreeManager.OnItemEdgeRemoved(this, key);
892 if (value != null && !this.IsInKeysOrValuesCollection(value) && value != key)
894 this.modelTreeManager.OnItemEdgeRemoved(this, value);
899 ModelItemCollectionImpl itemsCollectionImpl = ItemsCollectionModelItemCollection as ModelItemCollectionImpl;
900 if (ItemsCollectionObject != null && itemsCollectionImpl != null)
904 ItemsCollectionObject.ShouldUpdateDictionary = false;
906 ModelItem itemToBeRemoved = null;
907 foreach (ModelItem item in itemsCollectionImpl)
909 ModelItem keyInCollection = item.Properties["Key"].Value;
911 if (key == keyInCollection)
913 itemToBeRemoved = item;
917 if (key != null && keyInCollection != null)
919 object keyValue = key.GetCurrentValue();
921 // ValueType do not share ModelItem, a ModelItem is always created for a ValueType
922 // ModelTreeManager always create a ModelItem even for the same string
923 // So, we compare object instance instead of ModelItem for above cases.
924 if (keyValue is ValueType || keyValue is string)
926 if (keyValue.Equals(keyInCollection.GetCurrentValue()))
928 itemToBeRemoved = item;
935 if (itemToBeRemoved != null)
937 itemsCollectionImpl.RemoveCore(itemToBeRemoved);
942 ItemsCollectionObject.ShouldUpdateDictionary = true;
945 this.instance.Remove(key == null ? null : key.GetCurrentValue());
947 if (null != this.CollectionChanged)
949 this.CollectionChanged(this,
950 new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new KeyValuePair<ModelItem, ModelItem>(key, value)));
955 this.EditInProgress = false;
959 void UpdateInstance()
961 IEnumerator dictionaryEnumerator = this.instance.GetEnumerator();
962 while (dictionaryEnumerator.MoveNext())
964 object current = dictionaryEnumerator.Current;
966 object keyObject = instance.GetKeyFromCurrent(current);
967 ModelItem key = (keyObject == null) ? null : this.WrapObject(keyObject);
968 ModelItem value = this.WrapObject(instance.GetValueFromCurrent(current));
970 bool wasKeyInKeysOrValuesCollection = key != null && this.IsInKeysOrValuesCollection(key);
971 bool wasValueInKeysOrValuesCollection = value != null && this.IsInKeysOrValuesCollection(value);
973 this.modelItems.Add(key, value);
975 if (key != null && !wasKeyInKeysOrValuesCollection)
977 this.modelTreeManager.OnItemEdgeAdded(this, key);
980 if (value != null && !wasValueInKeysOrValuesCollection && value != key)
982 this.modelTreeManager.OnItemEdgeAdded(this, value);
987 ModelItem WrapObject(object value)
989 return this.ModelTreeManager.WrapAsModelItem(value);
992 // return true if the key already exist, false otherwise.
993 private bool KeyAsModelItem(object value, bool createNew, out ModelItem keyModelItem)
995 keyModelItem = value as ModelItem;
996 if (keyModelItem != null)
1002 keyModelItem = this.modelItems.Keys.SingleOrDefault<ModelItem>(p =>
1004 if ((p == null && value == null) || (p != null && object.Equals(p.GetCurrentValue(), value)))
1012 if (createNew && keyModelItem == null)
1014 keyModelItem = WrapObject(value);
1020 sealed class DictionaryWrapper
1023 bool isDictionary = false;
1024 PropertyInfo isReadOnlyProperty;
1025 PropertyInfo countProperty;
1026 PropertyInfo indexingProperty;
1027 MethodInfo addMethod;
1028 MethodInfo removeMethod;
1029 MethodInfo clearMethod;
1030 MethodInfo getEnumeratorMethod;
1031 PropertyInfo keyProperty;
1032 PropertyInfo valueProperty;
1034 public DictionaryWrapper(object instance)
1036 this.instance = instance;
1037 if (instance is IDictionary)
1039 this.isDictionary = true;
1040 Type keyValuePairType = typeof(KeyValuePair<object, object>);
1044 Type instanceType = instance.GetType();
1045 instanceType.FindInterfaces(this.GetDictionaryInterface, null);
1051 get { return this.instance; }
1054 public bool IsReadOnly
1058 return (this.isDictionary ? ((IDictionary)instance).IsReadOnly : (bool)this.isReadOnlyProperty.GetValue(this.instance, null));
1066 return (this.isDictionary ? ((IDictionary)instance).Count : (int)this.countProperty.GetValue(this.instance, null));
1070 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This is intended for use through reflection")]
1071 public object this[object key]
1075 if (this.isDictionary)
1077 return ((IDictionary)instance)[key];
1081 return this.indexingProperty.GetValue(this.instance, new object[] { key });
1086 if (this.isDictionary)
1088 ((IDictionary)instance)[key] = value;
1092 this.indexingProperty.SetValue(this.instance, value, new object[] { key });
1097 public object GetKeyFromCurrent(object keyValuePair)
1101 return ((DictionaryEntry)keyValuePair).Key;
1105 return this.keyProperty.GetValue(keyValuePair, null);
1109 public object GetValueFromCurrent(object keyValuePair)
1113 return ((DictionaryEntry)keyValuePair).Value;
1117 return this.valueProperty.GetValue(keyValuePair, null);
1122 bool GetDictionaryInterface(Type type, object dummy)
1124 if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
1126 this.addMethod = type.GetMethod("Add");
1127 this.removeMethod = type.GetMethod("Remove");
1128 this.indexingProperty = type.GetProperty("Item");
1131 if (type.IsGenericType &&
1132 type.GetGenericArguments()[0].IsGenericType &&
1133 type.GetGenericArguments()[0].GetGenericTypeDefinition() == typeof(KeyValuePair<,>) &&
1134 type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
1136 Type keyValuePairType = type.GetGenericArguments()[0];
1137 this.keyProperty = keyValuePairType.GetProperty("Key");
1138 this.valueProperty = keyValuePairType.GetProperty("Value");
1139 this.getEnumeratorMethod = type.GetMethod("GetEnumerator");
1142 if (type.IsGenericType &&
1143 type.GetGenericArguments()[0].IsGenericType &&
1144 type.GetGenericArguments()[0].GetGenericTypeDefinition() == typeof(KeyValuePair<,>) &&
1145 type.GetGenericTypeDefinition() == typeof(ICollection<>))
1147 this.isReadOnlyProperty = type.GetProperty("IsReadOnly");
1148 this.countProperty = type.GetProperty("Count");
1149 this.clearMethod = type.GetMethod("Clear");
1155 public void Add(object key, object value)
1157 if (this.isDictionary)
1159 ((IDictionary)instance).Add(key, value);
1163 this.addMethod.Invoke(this.instance, new object[] { key, value });
1169 if (this.isDictionary)
1171 ((IDictionary)instance).Clear();
1175 this.clearMethod.Invoke(this.instance, null);
1179 public IEnumerator GetEnumerator()
1181 if (this.isDictionary)
1183 return ((IDictionary)instance).GetEnumerator();
1187 return (IEnumerator)this.getEnumeratorMethod.Invoke(this.instance, null);
1191 public void Remove(object key)
1193 if (this.isDictionary)
1195 ((IDictionary)instance).Remove(key);
1199 this.removeMethod.Invoke(this.instance, new object[] { key });
1206 AttributeCollection ICustomTypeDescriptor.GetAttributes()
1208 return this.Attributes;
1211 string ICustomTypeDescriptor.GetClassName()
1213 return TypeDescriptor.GetClassName(this);
1216 string ICustomTypeDescriptor.GetComponentName()
1218 return TypeDescriptor.GetComponentName(this);
1221 TypeConverter ICustomTypeDescriptor.GetConverter()
1223 return ModelUtilities.GetConverter(this);
1226 EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
1228 // we dont support events;
1232 PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
1234 return ModelUtilities.GetDefaultProperty(this);
1237 object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
1239 // we dont support editors
1243 EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
1245 // we dont support events;
1249 EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
1251 // we dont support events;
1255 PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
1257 return ModelUtilities.WrapProperties(this);
1260 PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
1262 // get model properties
1263 List<PropertyDescriptor> properties = new List<PropertyDescriptor>();
1266 foreach (PropertyDescriptor modelPropertyDescriptor in ModelUtilities.WrapProperties(this))
1268 properties.Add(modelPropertyDescriptor);
1271 // try to see if there are pseudo builtin properties for this type.
1272 AttachedPropertiesService AttachedPropertiesService = this.modelTreeManager.Context.Services.GetService<AttachedPropertiesService>();
1273 if (AttachedPropertiesService != null)
1275 foreach (AttachedProperty AttachedProperty in AttachedPropertiesService.GetAttachedProperties(this.itemType))
1277 properties.Add(new AttachedPropertyDescriptor(AttachedProperty, this));
1280 return new PropertyDescriptorCollection(properties.ToArray(), true);
1283 object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)