/// <summary> /// Creates the or update array map. /// </summary> protected virtual void CreateOrUpdateArrayMap(object viewModel, PropertyInfo parentProperty, ref ViewModelMapNode map) { if (map == null) { map = new ViewModelMapArrayNode(); } if (map is ViewModelMapArrayNode) { var itemValues = ((IEnumerable)viewModel).OfType<object>().ToList(); var typedMap = (ViewModelMapArrayNode)map; // if the collection can be compared and updated by some property key, do it, otherwise replace the collection contents; // the synchronization can only occur when there is a key map or when the collection is empty var keyName = ResolveCollectionKeyName(parentProperty); if (!string.IsNullOrEmpty(keyName) && typedMap.Items.Count == typedMap.KeyMap.Count) { SynchronizeCollections(typedMap, itemValues, keyName); } else { // replace collection contents typedMap.Items.Clear(); typedMap.Items.AddRange(itemValues.Select(i => CreateMap(i, null))); typedMap.KeyMap.Clear(); } } else { throw new Exception(string.Format("The viewModel is a collection or array but the {0} was expected.", map.GetType())); } }
/// <summary> /// Synchronizes the collections using specified key property. /// </summary> private void SynchronizeCollections(ViewModelMapArrayNode targetArray, IEnumerable<object> sourceItems, string keyName) { var sourceKeyMap = sourceItems.ToDictionary(i => i.GetType().GetProperty(keyName).GetValue(i).ToString()); var targetKeyMap = targetArray.KeyMap; // detect changes var newItems = sourceKeyMap.Keys.Except(targetKeyMap.Keys).ToList(); var updatedItems = targetKeyMap.Keys.Intersect(sourceKeyMap.Keys).ToList(); var removedItems = targetKeyMap.Keys.Except(sourceKeyMap.Keys).ToList(); // add items foreach (var newItem in newItems) { var item = CreateMap(sourceKeyMap[newItem], null); targetArray.Items.Add(item); targetKeyMap[newItem] = item; } // update items foreach (var updatedItem in updatedItems) { var sourceItem = sourceKeyMap[updatedItem]; var targetItem = targetKeyMap[updatedItem]; CreateOrUpdateMap(sourceItem, null, ref targetItem); } // remove items foreach (var removedItem in removedItems) { var item = targetKeyMap[removedItem]; targetArray.Items.Remove(item); targetKeyMap.Remove(removedItem); } }