File: System\Data\Objects\ObjectViewEntityCollectionData.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="ObjectViewEntityCollectionData.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner       Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Objects.DataClasses;
using System.Data.Objects.Internal;
using System.Diagnostics;
using System.Data.Common;
 
namespace System.Data.Objects
{
    /// <summary>
    /// Manages a binding list constructed from an EntityCollection.
    /// </summary>
    /// <typeparam name="TViewElement">
    /// Type of the elements in the binding list.
    /// </typeparam>
    /// <typeparam name="TItemElement">
    /// Type of element in the underlying EntityCollection.
    /// </typeparam>
    /// <remarks>
    /// The binding list is initialized from the EntityCollection,
    /// and is synchronized with changes made to the EntityCollection membership.
    /// This class always allows additions and removals from the binding list.
    /// </remarks>
    internal sealed class ObjectViewEntityCollectionData<TViewElement, TItemElement> : IObjectViewData<TViewElement>
        where TItemElement : class
        where TViewElement : TItemElement
    {
        private List<TViewElement> _bindingList;
 
        private EntityCollection<TItemElement> _entityCollection;
 
        private readonly bool _canEditItems;
 
        /// <summary>
        /// <b>True</b> if item that was added to binding list but not underlying entity collection
        /// is now being committed to the collection.
        /// Otherwise <b>false</b>.
        /// Used by CommitItemAt and OnCollectionChanged methods to coordinate addition
        /// of new item to underlying entity collection.
        /// </summary>
        private bool _itemCommitPending;
 
        /// <summary>
        /// Construct a new instance of the ObjectViewEntityCollectionData class using the supplied entityCollection.
        /// </summary>
        /// <param name="entityCollection">
        /// EntityCollection used to populate the binding list.
        /// </param>
        internal ObjectViewEntityCollectionData(EntityCollection<TItemElement> entityCollection)
        {
            _entityCollection = entityCollection;
 
            _canEditItems = true;
 
            // Allow deferred loading to occur when initially populating the collection
            _bindingList = new List<TViewElement>(entityCollection.Count);
            foreach (TViewElement entity in entityCollection)
            {
                _bindingList.Add(entity);
            }
        }
 
        #region IObjectViewData<TViewElement> Members
 
        public IList<TViewElement> List
        {
            get { return _bindingList; }
        }
 
        public bool AllowNew
        {
            get { return !_entityCollection.IsReadOnly; }
        }
 
        public bool AllowEdit
        {
            get { return _canEditItems; }
        }
 
        public bool AllowRemove
        {
            get { return !_entityCollection.IsReadOnly; }
        }
 
        public bool FiresEventOnAdd
        {
            get { return true; }
        }
 
        public bool FiresEventOnRemove
        {
            get { return true; }
        }
 
        public bool FiresEventOnClear
        {
            get { return true; }
        }
 
        public void EnsureCanAddNew()
        {
            // nop
        }
 
        public int Add(TViewElement item, bool isAddNew)
        {
            if (isAddNew)
            {
                // Item is added to bindingList, but pending addition to entity collection.
                _bindingList.Add(item);
            }
            else
            {
                _entityCollection.Add(item);
                // OnCollectionChanged will be fired, where the binding list will be updated.
            }
 
            return _bindingList.Count - 1;
        }
 
        public void CommitItemAt(int index)
        {
            TViewElement item = _bindingList[index];
 
            try
            {
                _itemCommitPending = true;
 
                _entityCollection.Add(item);
                // OnCollectionChanged will be fired, where the binding list will be updated.
            }
            finally
            {
                _itemCommitPending = false;
            }
 
        }
 
        public void Clear()
        {
            if (0 < _bindingList.Count)
            {
                List<object> _deletionList = new List<object>();
 
                foreach (object item in _bindingList)
                {
                    _deletionList.Add(item);
                }
 
                _entityCollection.BulkDeleteAll(_deletionList);
                // EntityCollection will fire change event which this instance will use to clean up the binding list.
            }
        }
 
        public bool Remove(TViewElement item, bool isCancelNew)
        {
            bool removed;
 
            if (isCancelNew)
            {
                // Item was previously added to binding list, but not entity collection.
                removed = _bindingList.Remove(item);
            }
            else
            {
                removed = _entityCollection.RemoveInternal(item);
                // OnCollectionChanged will be fired, where the binding list will be updated.
            }
 
            return removed;
        }
 
        public ListChangedEventArgs OnCollectionChanged(object sender, CollectionChangeEventArgs e, ObjectViewListener listener)
        {
            ListChangedEventArgs changeArgs = null;
 
            switch (e.Action)
            {
                case CollectionChangeAction.Remove:
                    // An Entity is being removed from entity collection, remove it from list.
                    if (e.Element is TViewElement)
                    {
                        TViewElement removedItem = (TViewElement)e.Element;
 
                        int oldIndex = _bindingList.IndexOf(removedItem);
                        if (oldIndex != -1)
                        {
                            _bindingList.Remove(removedItem);
 
                            // Unhook from events of removed entity.
                            listener.UnregisterEntityEvents(removedItem);
 
                            changeArgs = new ListChangedEventArgs(ListChangedType.ItemDeleted, oldIndex /* newIndex*/, -1 /* oldIndex*/);
                        }
                    }
                    break;
 
                case CollectionChangeAction.Add:
                    // Add the entity to our list.
                    if (e.Element is TViewElement)
                    {
                        // Do not process Add events that fire as a result of committing an item to the entity collection.
                        if (!_itemCommitPending)
                        {
                            TViewElement addedItem = (TViewElement)e.Element;
 
                            _bindingList.Add(addedItem);
 
                            // Register to its events.
                            listener.RegisterEntityEvents(addedItem);
 
                            changeArgs = new ListChangedEventArgs(ListChangedType.ItemAdded, _bindingList.Count - 1 /* newIndex*/, -1 /* oldIndex*/);
                        }
                    }
                    break;
 
                case CollectionChangeAction.Refresh:
                    foreach (TViewElement entity in _bindingList)
                    {
                        listener.UnregisterEntityEvents(entity);
                    }
 
                    _bindingList.Clear();
 
                    foreach(TViewElement entity in _entityCollection.GetInternalEnumerable())
                    {
                        _bindingList.Add(entity);
 
                        listener.RegisterEntityEvents(entity);
                    }
 
                    changeArgs = new ListChangedEventArgs(ListChangedType.Reset, -1 /*newIndex*/, -1/*oldIndex*/);
                    break;
            }
 
            return changeArgs;
        }
 
        #endregion
    }
}