1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System.Activities.Presentation.Model
7 using System.Collections;
8 using System.Collections.Generic;
9 using System.Collections.Specialized;
10 using System.ComponentModel;
11 using System.Diagnostics;
13 using System.Windows.Markup;
14 using System.Activities.Presentation.Services;
18 // This class provides the implementation for the ModelItemCollection. This provides
19 // a container for the child modelItems for the entries in a collection.
21 class ModelItemCollectionImpl : ModelItemCollection, IModelTreeItem
23 ModelProperty contentProperty;
26 List<ModelItem> modelItems;
27 Dictionary<string, ModelItem> modelPropertyStore;
28 ModelTreeManager modelTreeManager;
29 ModelProperty nameProperty;
30 List<ModelItem> parents;
31 ModelPropertyCollectionImpl properties;
32 List<ModelProperty> sources;
33 ModelTreeItemHelper helper;
34 List<ModelItem> subTreeNodesThatNeedBackLinkPatching;
35 DependencyObject view;
36 ModelItem manuallySetParent;
38 public ModelItemCollectionImpl(ModelTreeManager modelTreeManager, Type itemType, Object instance, ModelItem parent)
40 Fx.Assert(modelTreeManager != null, "modelTreeManager cannot be null");
41 Fx.Assert(itemType != null, "item type cannot be null");
42 Fx.Assert(instance != null, "instance cannot be null");
43 this.itemType = itemType;
44 this.instance = instance;
45 this.modelTreeManager = modelTreeManager;
46 this.parents = new List<ModelItem>(1);
47 this.sources = new List<ModelProperty>(1);
48 this.helper = new ModelTreeItemHelper();
51 this.manuallySetParent = parent;
53 this.modelPropertyStore = new Dictionary<string, ModelItem>();
54 this.subTreeNodesThatNeedBackLinkPatching = new List<ModelItem>();
55 this.modelItems = new List<ModelItem>();
56 UpdateInstance(instance);
59 public override event NotifyCollectionChangedEventHandler CollectionChanged;
61 public override event PropertyChangedEventHandler PropertyChanged;
63 public override AttributeCollection Attributes
67 Fx.Assert(this.itemType != null, "item type cannot be null");
68 return TypeDescriptor.GetAttributes(this.itemType);
72 public override ModelProperty Content
76 if (this.contentProperty == null)
78 Fx.Assert(this.instance != null, "instance cannot be null");
79 ContentPropertyAttribute contentAttribute = TypeDescriptor.GetAttributes(this.instance)[typeof(ContentPropertyAttribute)] as ContentPropertyAttribute;
80 if (contentAttribute != null && !String.IsNullOrEmpty(contentAttribute.Name))
82 this.contentProperty = this.Properties.Find(contentAttribute.Name);
85 return contentProperty;
89 public override int Count
93 Fx.Assert(instance != null, "instance cannot be null");
96 return ((ICollection)instance).Count;
102 public override bool IsReadOnly
106 IList instanceList = instance as IList;
107 Fx.Assert(instanceList != null, "instance should be IList");
108 return instanceList.IsReadOnly;
112 public override Type ItemType
116 return this.itemType;
120 public override string Name
125 if ((this.NameProperty != null) && (this.NameProperty.Value != null))
127 name = (string)this.NameProperty.Value.GetCurrentValue();
133 this.NameProperty.SetValue(value);
137 public override ModelItem Parent
141 return (this.Parents.Count() > 0) ? this.Parents.First() : null;
146 public override ModelPropertyCollection Properties
150 if (this.properties == null)
152 properties = new ModelPropertyCollectionImpl(this);
158 public override ModelItem Root
162 return this.modelTreeManager.Root;
166 public override ModelProperty Source
170 return (this.sources.Count > 0) ? this.sources.First() : null;
174 public override DependencyObject View
182 public override IEnumerable<ModelItem> Parents
186 if (this.manuallySetParent != null)
188 List<ModelItem> list = new List<ModelItem>();
189 list.Add(this.manuallySetParent);
190 return list.Concat(this.parents).Concat(
191 from source in this.sources
192 select source.Parent);
195 return this.parents.Concat(
196 from source in this.sources
197 select source.Parent);
201 public override IEnumerable<ModelProperty> Sources
209 internal List<ModelItem> Items
217 internal Dictionary<string, ModelItem> ModelPropertyStore
221 return modelPropertyStore;
225 protected ModelProperty NameProperty
229 if (this.nameProperty == null)
231 Fx.Assert(this.instance != null, "instance cannot be null");
232 RuntimeNamePropertyAttribute runtimeNamePropertyAttribute = TypeDescriptor.GetAttributes(this.instance)[typeof(RuntimeNamePropertyAttribute)] as RuntimeNamePropertyAttribute;
233 if (runtimeNamePropertyAttribute != null && !String.IsNullOrEmpty(runtimeNamePropertyAttribute.Name))
235 this.nameProperty = this.Properties.Find(runtimeNamePropertyAttribute.Name);
242 ModelItem IModelTreeItem.ModelItem
250 Dictionary<string, ModelItem> IModelTreeItem.ModelPropertyStore
254 return modelPropertyStore;
259 ModelTreeManager IModelTreeItem.ModelTreeManager
263 return modelTreeManager;
267 public override ModelItem this[int index]
271 return this.modelItems[index];
275 this.Insert(index, value);
279 public void SetCurrentView(DependencyObject view)
284 public override ModelItem Add(object value)
286 ModelItem item = value as ModelItem;
289 item = this.modelTreeManager.WrapAsModelItem(value);
293 throw FxTrace.Exception.AsError(new ArgumentNullException("value"));
299 public override void Add(ModelItem item)
303 throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
305 this.modelTreeManager.CollectionAdd(this, item);
309 public override ModelEditingScope BeginEdit(string description, bool shouldApplyChangesImmediately)
311 return ModelItemHelper.ModelItemBeginEdit(this.modelTreeManager, description, shouldApplyChangesImmediately);
314 public override ModelEditingScope BeginEdit(bool shouldApplyChangesImmediately)
316 return this.BeginEdit(null, shouldApplyChangesImmediately);
319 public override ModelEditingScope BeginEdit(string description)
321 return this.BeginEdit(description, false);
324 public override ModelEditingScope BeginEdit()
326 return this.BeginEdit(null);
329 public override void Clear()
331 if (!this.IsReadOnly && (this.modelItems.Count > 0))
333 this.modelTreeManager.CollectionClear(this);
337 public override bool Contains(object value)
339 ModelItem item = value as ModelItem;
342 return ((IList)instance).Contains(value);
344 return Contains(item);
347 public override bool Contains(ModelItem item)
349 return this.Items.Contains(item);
352 public override void CopyTo(ModelItem[] array, int arrayIndex)
356 throw FxTrace.Exception.AsError(new ArgumentNullException("array"));
358 if (this.Items != null)
360 this.Items.CopyTo(array, arrayIndex);
364 public override object GetCurrentValue()
366 return this.instance;
369 public override IEnumerator<ModelItem> GetEnumerator()
371 foreach (ModelItem modelItem in modelItems)
373 yield return modelItem;
377 void IModelTreeItem.OnPropertyChanged(string propertyName)
379 if (PropertyChanged != null)
381 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
385 IEnumerable<ModelItem> IModelTreeItem.ItemBackPointers
387 get { return this.parents; }
390 List<BackPointer> IModelTreeItem.ExtraPropertyBackPointers
392 get { return this.helper.ExtraPropertyBackPointers; }
395 void IModelTreeItem.SetParent(ModelItem dataModelItem)
397 if (this.manuallySetParent == dataModelItem)
399 this.manuallySetParent = null;
402 if (!this.parents.Contains(dataModelItem))
404 this.parents.Add(dataModelItem);
408 void IModelTreeItem.SetSource(ModelProperty property)
410 if (!this.sources.Contains(property))
412 // also check if the same parent.property is in the list as a different instance of oldModelProperty
413 ModelProperty foundProperty = this.sources.Find((modelProperty) => modelProperty.Name.Equals(property.Name) && property.Parent == modelProperty.Parent);
414 if (foundProperty == null)
416 this.sources.Add(property);
421 public override int IndexOf(ModelItem item)
425 throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
427 return this.modelItems.IndexOf(item);
430 public override ModelItem Insert(int index, object value)
432 ModelItem item = value as ModelItem;
435 item = this.modelTreeManager.WrapAsModelItem(value);
439 throw FxTrace.Exception.AsError(new ArgumentNullException("value"));
445 public override void Insert(int index, ModelItem item)
449 throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
451 this.modelTreeManager.CollectionInsert(this, index, item);
454 public override void Move(int fromIndex, int toIndex)
456 if (fromIndex > this.Items.Count)
458 throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("fromIndex"));
460 if (toIndex > this.Items.Count)
462 throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("toIndex"));
464 ModelItem movingItem = this.Items[fromIndex];
465 this.RemoveAt(fromIndex);
466 this.Insert(toIndex, movingItem);
469 public override bool Remove(object value)
471 ModelItem item = value as ModelItem;
474 Fx.Assert(this.Items != null, "Items collection is null when trying to iterate over it");
475 foreach (ModelItem childItem in this.Items)
477 if (childItem.GetCurrentValue() == value)
491 public override bool Remove(ModelItem item)
493 this.modelTreeManager.CollectionRemove(this, item);
497 public override void RemoveAt(int index)
499 if (index >= this.Count)
501 throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("index"));
503 this.modelTreeManager.CollectionRemoveAt(this, index);
508 internal void AddCore(ModelItem item)
510 Fx.Assert(instance is IList, "instance should be IList");
511 Fx.Assert(instance != null, "instance should not be null");
513 IList instanceList = (IList)instance;
514 instanceList.Add(item.GetCurrentValue());
515 bool wasInCollection = this.modelItems.Contains(item);
516 this.modelItems.Add(item);
517 if (!wasInCollection)
519 this.modelTreeManager.OnItemEdgeAdded(this, item);
522 if (this.PropertyChanged != null)
524 this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
526 if (this.CollectionChanged != null)
528 this.CollectionChanged(this, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Add, item, this.Count - 1));
534 internal void ClearCore()
536 Fx.Assert(instance is IList, " Instance needs to be Ilist for clear to work");
537 Fx.Assert(instance != null, "Instance should not be null");
539 IList instanceList = (IList)instance;
540 instanceList.Clear();
542 List<ModelItem> modelItemsRemoved = new List<ModelItem>(this.modelItems);
543 this.modelItems.Clear();
545 foreach (ModelItem item in modelItemsRemoved.Distinct())
549 this.modelTreeManager.OnItemEdgeRemoved(this, item);
553 if (this.PropertyChanged != null)
555 this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
560 internal void InsertCore(int index, ModelItem item)
562 Fx.Assert(instance is IList, "instance needs to be IList");
563 Fx.Assert(instance != null, "instance should not be null");
564 IList instanceList = (IList)instance;
565 instanceList.Insert(index, item.GetCurrentValue());
567 bool wasInCollection = this.modelItems.Contains(item);
569 this.modelItems.Insert(index, item);
571 if (!wasInCollection)
573 this.modelTreeManager.OnItemEdgeAdded(this, item);
576 if (this.PropertyChanged != null)
578 this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
580 if (this.CollectionChanged != null)
582 this.CollectionChanged(this, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Add, item, index));
586 internal void RemoveCore(ModelItem item)
589 Fx.Assert(instance is IList, "Instance needs to be IList for remove to work");
590 Fx.Assert(instance != null, "instance should not be null");
592 IList instanceList = (IList)instance;
593 int index = instanceList.IndexOf(item.GetCurrentValue());
594 instanceList.Remove(item.GetCurrentValue());
595 this.modelItems.Remove(item);
597 if (!this.modelItems.Contains(item))
599 this.modelTreeManager.OnItemEdgeRemoved(this, item);
602 if (this.PropertyChanged != null)
604 this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
607 if (this.CollectionChanged != null)
609 this.CollectionChanged(this, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Remove, item, index));
613 internal void RemoveAtCore(int index)
615 Fx.Assert(instance is IList, "Instance needs to be IList for remove to work");
616 Fx.Assert(instance != null, "instance should not be null");
618 IList instanceList = (IList)instance;
619 ModelItem item = this.modelItems[index];
620 instanceList.RemoveAt(index);
621 this.modelItems.RemoveAt(index);
622 if (!this.modelItems.Contains(item))
624 this.modelTreeManager.OnItemEdgeRemoved(this, item);
627 if (this.PropertyChanged != null)
629 this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
632 if (this.CollectionChanged != null)
634 this.CollectionChanged(this, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Remove, item, index));
638 void UpdateInstance(object instance)
640 this.instance = instance;
641 IEnumerable instanceCollection = this.instance as IEnumerable;
642 if (instanceCollection != null)
644 foreach (object item in instanceCollection)
646 ModelItem modelItem = this.modelTreeManager.WrapAsModelItem(item);
647 bool wasInCollection = item != null && this.modelItems.Contains(item);
648 this.modelItems.Add(modelItem);
649 if (item != null && !wasInCollection)
651 this.modelTreeManager.OnItemEdgeAdded(this, modelItem);
657 void IModelTreeItem.RemoveParent(ModelItem oldParent)
659 if (this.manuallySetParent == oldParent)
661 this.manuallySetParent = null;
664 if (this.parents.Contains(oldParent))
666 this.parents.Remove(oldParent);
670 void IModelTreeItem.RemoveSource(ModelProperty oldModelProperty)
672 if (this.sources.Contains(oldModelProperty))
674 this.sources.Remove(oldModelProperty);
678 ((IModelTreeItem)this).RemoveSource(oldModelProperty.Parent, oldModelProperty.Name);
682 void IModelTreeItem.RemoveSource(ModelItem parent, string propertyName)
684 // also check if the same parent.property is in the list as a different instance of oldModelProperty
685 ModelProperty foundProperty = this.sources.FirstOrDefault<ModelProperty>((modelProperty) => modelProperty.Name.Equals(propertyName) && modelProperty.Parent == parent);
686 if (foundProperty != null)
688 this.sources.Remove(foundProperty);
692 this.helper.RemoveExtraPropertyBackPointer(parent, propertyName);