//---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//---------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using EGC = ExtendedGrid.Microsoft.Windows.Controls;
using MS.Internal;

namespace ExtendedGrid.Microsoft.Windows.Controls
{
    /// <summary>
    ///     A DataGrid control that displays data in rows and columns and allows
    ///     for the entering and editing of data.
    /// </summary>
    public class DataGrid : MultiSelector
    {
        #region Constructors

        /// <summary>
        ///     Instantiates global information.
        /// </summary>
        static DataGrid()
        {
            Type ownerType = typeof(EGC.DataGrid);

            DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(typeof(EGC.DataGrid)));
            FrameworkElementFactory dataGridRowPresenterFactory = new FrameworkElementFactory(typeof(EGC.DataGridRowsPresenter));
            dataGridRowPresenterFactory.SetValue(FrameworkElement.NameProperty, ItemsPanelPartName);
            ItemsPanelProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new ItemsPanelTemplate(dataGridRowPresenterFactory)));
            VirtualizingStackPanel.IsVirtualizingProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(true, null, new CoerceValueCallback(OnCoerceIsVirtualizingProperty)));
            VirtualizingStackPanel.VirtualizationModeProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(VirtualizationMode.Recycling));
            ItemContainerStyleProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceItemContainerStyle)));
            ItemContainerStyleSelectorProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceItemContainerStyleSelector)));
            ItemsSourceProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata((PropertyChangedCallback)null, OnCoerceItemsSourceProperty));
            AlternationCountProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(0, null, new CoerceValueCallback(OnCoerceAlternationCount)));
            IsEnabledProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnIsEnabledChanged)));
            IsSynchronizedWithCurrentItemProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceIsSynchronizedWithCurrentItem)));
            IsTabStopProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(false));
            KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(KeyboardNavigationMode.Contained));
            KeyboardNavigation.ControlTabNavigationProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(KeyboardNavigationMode.Once));

            CommandManager.RegisterClassInputBinding(ownerType, new InputBinding(BeginEditCommand, new KeyGesture(Key.F2)));
            CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(BeginEditCommand, new ExecutedRoutedEventHandler(OnExecutedBeginEdit), new CanExecuteRoutedEventHandler(OnCanExecuteBeginEdit)));

            CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(CommitEditCommand, new ExecutedRoutedEventHandler(OnExecutedCommitEdit), new CanExecuteRoutedEventHandler(OnCanExecuteCommitEdit)));

            CommandManager.RegisterClassInputBinding(ownerType, new InputBinding(CancelEditCommand, new KeyGesture(Key.Escape)));
            CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(CancelEditCommand, new ExecutedRoutedEventHandler(OnExecutedCancelEdit), new CanExecuteRoutedEventHandler(OnCanExecuteCancelEdit)));

            CommandManager.RegisterClassInputBinding(ownerType, new InputBinding(SelectAllCommand, EGC.DataGridHelper.CreateFromResourceStrings(SR.Get(SRID.DataGrid_SelectAllKey), SR.Get(SRID.DataGrid_SelectAllKeyDisplayString))));
            CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(SelectAllCommand, new ExecutedRoutedEventHandler(OnExecutedSelectAll), new CanExecuteRoutedEventHandler(OnCanExecuteSelectAll)));

            CommandManager.RegisterClassInputBinding(ownerType, new InputBinding(DeleteCommand, new KeyGesture(Key.Delete)));
            CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(DeleteCommand, new ExecutedRoutedEventHandler(OnExecutedDelete), new CanExecuteRoutedEventHandler(OnCanExecuteDelete)));

            // Default Clipboard handling
            CommandManager.RegisterClassCommandBinding(typeof(EGC.DataGrid), new CommandBinding(ApplicationCommands.Copy, new ExecutedRoutedEventHandler(OnExecutedCopy), new CanExecuteRoutedEventHandler(OnCanExecuteCopy)));

            EventManager.RegisterClassHandler(typeof(EGC.DataGrid), MouseUpEvent, new MouseButtonEventHandler(OnAnyMouseUpThunk), true);
        }

        /// <summary>
        ///     Instantiates a new instance of this class.
        /// </summary>
        public DataGrid()
        {
            _columns = new EGC.DataGridColumnCollection(this);
            _columns.CollectionChanged += new NotifyCollectionChangedEventHandler(OnColumnsChanged);

            _rowValidationRules = new ObservableCollection<ValidationRule>();
            _rowValidationRules.CollectionChanged += new NotifyCollectionChangedEventHandler(OnRowValidationRulesChanged);

            _selectedCells = new EGC.SelectedCellsCollection(this);

            ((INotifyCollectionChanged)Items).CollectionChanged += new NotifyCollectionChangedEventHandler(OnItemsCollectionChanged);

            ((INotifyCollectionChanged)Items.SortDescriptions).CollectionChanged += new NotifyCollectionChangedEventHandler(OnItemsSortDescriptionsChanged);
            Items.GroupDescriptions.CollectionChanged += new NotifyCollectionChangedEventHandler(OnItemsGroupDescriptionsChanged);

            // Compute column widths but wait until first load
            InternalColumns.InvalidateColumnWidthsComputation();

            CellsPanelHorizontalOffsetComputationPending = false;
        }

        #endregion

        #region Columns

        /// <summary>
        ///     A collection of column definitions describing the individual 
        ///     columns of each row.
        /// </summary>
        public ObservableCollection<EGC.DataGridColumn> Columns
        {
            get { return _columns; }
        }

        /// <summary>
        ///     Returns the column collection without having to upcast from ObservableCollection
        /// </summary>
        internal EGC.DataGridColumnCollection InternalColumns
        {
            get { return _columns; }
        }

        /// <summary>
        ///     A property that specifies whether the user can resize columns in the UI by dragging the column headers.
        /// </summary>
        /// <remarks>
        ///     This does not affect whether column widths can be changed programmatically via a property such as Column.Width.
        /// </remarks>
        public bool CanUserResizeColumns
        {
            get { return (bool)GetValue(CanUserResizeColumnsProperty); }
            set { SetValue(CanUserResizeColumnsProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty that represents the CanUserResizeColumns property.
        /// </summary>
        public static readonly DependencyProperty CanUserResizeColumnsProperty =
            DependencyProperty.Register("CanUserResizeColumns", typeof(bool), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnNotifyColumnHeaderPropertyChanged)));

        /// <summary>
        ///     Specifies the width of the header and cells within all the columns.
        /// </summary>
        public EGC.DataGridLength ColumnWidth
        {
            get { return (EGC.DataGridLength)GetValue(ColumnWidthProperty); }
            set { SetValue(ColumnWidthProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty that represents the ColumnWidth property.
        /// </summary>
        public static readonly DependencyProperty ColumnWidthProperty =
            DependencyProperty.Register("ColumnWidth", typeof(EGC.DataGridLength), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(EGC.DataGridLength.SizeToHeader));

        /// <summary>
        ///     Specifies the minimum width of the header and cells within all columns.
        /// </summary>
        public double MinColumnWidth
        {
            get { return (double)GetValue(MinColumnWidthProperty); }
            set { SetValue(MinColumnWidthProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty that represents the MinColumnWidth property.
        /// </summary>
        public static readonly DependencyProperty MinColumnWidthProperty =
            DependencyProperty.Register(
                "MinColumnWidth", 
                typeof(double),
                typeof(EGC.DataGrid),
                new FrameworkPropertyMetadata(20d, new PropertyChangedCallback(OnColumnSizeConstraintChanged)),
                new ValidateValueCallback(ValidateMinColumnWidth));

        /// <summary>
        ///     Specifies the maximum width of the header and cells within all columns.
        /// </summary>
        public double MaxColumnWidth
        {
            get { return (double)GetValue(MaxColumnWidthProperty); }
            set { SetValue(MaxColumnWidthProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty that represents the  MaxColumnWidth property.
        /// </summary>
        public static readonly DependencyProperty MaxColumnWidthProperty =
            DependencyProperty.Register(
                "MaxColumnWidth", 
                typeof(double),
                typeof(EGC.DataGrid),
                new FrameworkPropertyMetadata(double.PositiveInfinity, new PropertyChangedCallback(OnColumnSizeConstraintChanged)),
                new ValidateValueCallback(ValidateMaxColumnWidth));

        private static void OnColumnSizeConstraintChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.Columns);
        }

        /// <summary>
        /// Validates that the minimum column width is an acceptable value
        /// </summary>
        private static bool ValidateMinColumnWidth(object v)
        {
            double value = (double)v;
            return !(value < 0d || DoubleUtil.IsNaN(value) || Double.IsPositiveInfinity(value));
        }

        /// <summary>
        /// Validates that the maximum column width is an acceptable value
        /// </summary>
        private static bool ValidateMaxColumnWidth(object v)
        {
            double value = (double)v;
            return !(value < 0d || DoubleUtil.IsNaN(value));
        }

        /// <summary>
        ///     Called when the Columns collection changes.
        /// </summary>
        private void OnColumnsChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // Update the reference to this DataGrid on the affected column(s)
            // and update the SelectedCells collection.
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    UpdateDataGridReference(e.NewItems, /* clear = */ false);
                    UpdateColumnSizeConstraints(e.NewItems);
                    break;

                case NotifyCollectionChangedAction.Remove:
                    UpdateDataGridReference(e.OldItems, /* clear = */ true);
                    break;

                case NotifyCollectionChangedAction.Replace:
                    UpdateDataGridReference(e.OldItems, /* clear = */ true);
                    UpdateDataGridReference(e.NewItems, /* clear = */ false);
                    UpdateColumnSizeConstraints(e.NewItems);
                    break;

                case NotifyCollectionChangedAction.Reset:
                    // We can't clear column references on Reset: _columns has 0 items and e.OldItems is empty.
                    _selectedCells.Clear();
                    break;
            }

            // FrozenColumns rely on column DisplayIndex
            // Delay the coercion if necessary
            if (InternalColumns.DisplayIndexMapInitialized)
            {
                CoerceValue(FrozenColumnCountProperty);
            }

            bool visibleColumnsChanged = HasVisibleColumns(e.OldItems);
            visibleColumnsChanged |= HasVisibleColumns(e.NewItems);
            visibleColumnsChanged |= (e.Action == NotifyCollectionChangedAction.Reset);

            if (visibleColumnsChanged)
            {
                InternalColumns.InvalidateColumnRealization(true);
            }

            UpdateColumnsOnRows(e);

            // Recompute the column width if required, but wait until the first load
            if (visibleColumnsChanged && e.Action != NotifyCollectionChangedAction.Move)
            {
                InternalColumns.InvalidateColumnWidthsComputation();
            }
        }

        /// <summary>
        ///     Updates the reference to this DataGrid on the list of columns.
        /// </summary>
        /// <param name="list">The list of affected columns.</param>
        /// <param name="clear">Whether to add or remove the reference to this grid.</param>
        internal void UpdateDataGridReference(IList list, bool clear)
        {
            int numItems = list.Count;
            for (int i = 0; i < numItems; i++)
            {
                EGC.DataGridColumn column = (EGC.DataGridColumn)list[i];
                if (clear)
                {
                    // Set the owner to null only if the current owner is this grid
                    if (column.DataGridOwner == this)
                    {
                        column.DataGridOwner = null;
                    }
                }
                else
                {
                    // Remove the column from any old owner
                    if (column.DataGridOwner != null && column.DataGridOwner != this)
                    {
                        column.DataGridOwner.Columns.Remove(column);
                    }

                    column.DataGridOwner = this;
                }
            }
        }

        /// <summary>
        ///     Updates the transferred size constraints from DataGrid on the columns.
        /// </summary>
        /// <param name="list">The list of affected columns.</param>
        private static void UpdateColumnSizeConstraints(IList list)
        {
            var count = list.Count;
            for (var i = 0; i < count; i++)
            {
                var column = (EGC.DataGridColumn)list[i];
                column.SyncProperties();
            }
        }

        /// <summary>
        ///     Helper method which determines if the
        ///     given list has visible columns
        /// </summary>
        private static bool HasVisibleColumns(IList columns)
        {
            if (columns != null && columns.Count > 0)
            {
                foreach (EGC.DataGridColumn column in columns)
                {
                    if (column.IsVisible)
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        #endregion

        #region Display Index

        /// <summary>
        ///     Returns the DataGridColumn with the given DisplayIndex
        /// </summary>
        public EGC.DataGridColumn ColumnFromDisplayIndex(int displayIndex)
        {
            if (displayIndex < 0 || displayIndex >= Columns.Count)
            {
                throw new ArgumentOutOfRangeException("displayIndex", displayIndex, SR.Get(SRID.DataGrid_DisplayIndexOutOfRange));
            }

            return InternalColumns.ColumnFromDisplayIndex(displayIndex);
        }

        /// <summary>
        ///     Event that is fired when the DisplayIndex on one of the DataGrid's Columns changes.
        /// </summary>
        public event EventHandler<EGC.DataGridColumnEventArgs> ColumnDisplayIndexChanged;

        /// <summary>
        ///     Called when the DisplayIndex of a column is modified.
        /// </summary>
        /// <remarks>
        ///     A column's DisplayIndex may be modified as the result of another column's DisplayIndex changing.  This is because the 
        ///     DataGrid enforces that the DisplayIndex of all Columns are unique integers from 0 to Columns.Count -1.
        /// </remarks>
        protected internal virtual void OnColumnDisplayIndexChanged(EGC.DataGridColumnEventArgs e)
        {
            if (ColumnDisplayIndexChanged != null)
            {
                ColumnDisplayIndexChanged(this, e);
            }
        }

        /// <summary>
        ///     A map of display index (key) to index in the column collection (value).  
        ///     Used by the CellsPanel to quickly find a child from a column display index.
        /// </summary>
        internal List<int> DisplayIndexMap
        {
            get { return InternalColumns.DisplayIndexMap; }
        }

        /// <summary>
        ///     Throws an ArgumentOutOfRangeException if the given displayIndex is invalid.
        /// </summary>
        internal void ValidateDisplayIndex(EGC.DataGridColumn column, int displayIndex)
        {
            InternalColumns.ValidateDisplayIndex(column, displayIndex);
        }

        /// <summary>
        ///     Returns the index of a column from the given DisplayIndex
        /// </summary>
        internal int ColumnIndexFromDisplayIndex(int displayIndex)
        {
            if (displayIndex >= 0 && displayIndex < DisplayIndexMap.Count)
            {
                return DisplayIndexMap[displayIndex];
            }

            return -1;
        }

        /// <summary>
        ///     Given the DisplayIndex of a column returns the DataGridColumnHeader for that column.
        ///     Used by DataGridColumnHeader to find its previous sibling.
        /// </summary>
        /// <param name="displayIndex"></param>
        /// <returns></returns>
        internal EGC.DataGridColumnHeader ColumnHeaderFromDisplayIndex(int displayIndex)
        {
            int columnIndex = ColumnIndexFromDisplayIndex(displayIndex);
          
            if (columnIndex != -1)
            {
                if (ColumnHeadersPresenter != null && ColumnHeadersPresenter.ItemContainerGenerator != null)
                {
                    return (EGC.DataGridColumnHeader)ColumnHeadersPresenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
                }
            }

            return null;
        }

        #endregion

        #region Notification Propagation

        /// <summary>
        ///     Notifies each CellsPresenter about property changes.
        /// </summary>
        private static void OnNotifyCellsPresenterPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.CellsPresenter);
        }

        /// <summary>
        ///     Notifies each Column and Cell about property changes.
        /// </summary>
        private static void OnNotifyColumnAndCellPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.Columns | NotificationTarget.Cells);
        }

        /// <summary>
        ///     Notifies each Column about property changes.
        /// </summary>
        private static void OnNotifyColumnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.Columns);
        }

        /// <summary>
        ///     Notifies the Column & Column Headers about property changes.
        /// </summary>
        private static void OnNotifyColumnAndColumnHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.Columns | NotificationTarget.ColumnHeaders);
        }

        /// <summary>
        ///     Notifies the Column Headers about property changes.
        /// </summary>
        private static void OnNotifyColumnHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.ColumnHeaders);
        }

        /// <summary>
        ///     Notifies the Row and Column Headers about property changes (used by the AlternationBackground property)
        /// </summary>
        private static void OnNotifyHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.ColumnHeaders | NotificationTarget.RowHeaders);
        }

        /// <summary>
        ///     Notifies the DataGrid and each Row about property changes.
        /// </summary>
        private static void OnNotifyDataGridAndRowPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.Rows | NotificationTarget.DataGrid);
        }

        /// <summary>
        ///     Notifies everyone who cares about GridLine property changes (Row, Cell, RowHeader, ColumnHeader)
        /// </summary>
        private static void OnNotifyGridLinePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Clear out and regenerate all containers.  We do this so that we don't have to propagate this notification
            // to containers that are currently on the recycle queue -- doing so costs us perf on every scroll.  We don't
            // care about the time spent on a GridLine change since it'll be a very rare occurance.
            //
            // ItemsControl.OnItemTemplateChanged calls the internal ItemContainerGenerator.Refresh() method, which
            // clears out all containers and notifies the panel.  The fact we're passing in two null templates is ignored.
            if (e.OldValue != e.NewValue)
            {
                ((EGC.DataGrid)d).OnItemTemplateChanged(null, null);
            }
        }

        /// <summary>
        ///     Notifies each Row about property changes.
        /// </summary>
        private static void OnNotifyRowPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.Rows);
        }

        /// <summary>
        ///     Notifies the Row Headers about property changes.
        /// </summary>
        private static void OnNotifyRowHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.RowHeaders);
        }

        /// <summary>
        ///     Notifies the Row & Row Headers about property changes.
        /// </summary>
        private static void OnNotifyRowAndRowHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.Rows | NotificationTarget.RowHeaders);
        }

        /// <summary>
        ///     Notifies the Row & Details about property changes.
        /// </summary>
        private static void OnNotifyRowAndDetailsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.Rows | NotificationTarget.DetailsPresenter);
        }

        /// <summary>
        ///     Notifies HorizontalOffset change to columns collection, cellspresenter and column headers presenter
        /// </summary>
        private static void OnNotifyHorizontalOffsetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.ColumnCollection | NotificationTarget.CellsPresenter | NotificationTarget.ColumnHeadersPresenter);
        }

        /// <summary>
        ///     General notification for DependencyProperty changes from the grid or from columns.
        /// </summary>
        /// <remarks>
        ///     This can be called from a variety of sources, such as from column objects
        ///     or from this DataGrid itself when there is a need to notify the rows and/or
        ///     the cells in the DataGrid about a property change. Down-stream handlers 
        ///     can check the source of the change using the "d" parameter.
        /// </remarks>
        internal void NotifyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e, NotificationTarget target)
        {
            NotifyPropertyChanged(d, string.Empty, e, target);
        }

        /// <summary>
        ///     General notification for DependencyProperty changes from the grid or from columns.
        /// </summary>
        /// <remarks>
        ///     This can be called from a variety of sources, such as from column objects
        ///     or from this DataGrid itself when there is a need to notify the rows and/or
        ///     the cells in the DataGrid about a property change. Down-stream handlers 
        ///     can check the source of the change using the "d" parameter.
        /// </remarks>
        internal void NotifyPropertyChanged(DependencyObject d, string propertyName, DependencyPropertyChangedEventArgs e, NotificationTarget target)
        {
            if (EGC.DataGridHelper.ShouldNotifyDataGrid(target))
            {
                if (e.Property == AlternatingRowBackgroundProperty)
                {
                    // If the alternate row background is set, the count may be coerced to 2
                    CoerceValue(AlternationCountProperty);
                }
            }

            // Rows, Cells, CellsPresenter, DetailsPresenter or RowHeaders
            if (EGC.DataGridHelper.ShouldNotifyRowSubtree(target))  
            {
                // Notify the Rows about the property change
                EGC.ContainerTracking<EGC.DataGridRow> tracker = _rowTrackingRoot;
                while (tracker != null)
                {
                    tracker.Container.NotifyPropertyChanged(d, propertyName, e, target);
                    tracker = tracker.Next;
                }
            }

            if (EGC.DataGridHelper.ShouldNotifyColumnCollection(target) || EGC.DataGridHelper.ShouldNotifyColumns(target))
            {
                InternalColumns.NotifyPropertyChanged(d, propertyName, e, target);
            }

            if ((EGC.DataGridHelper.ShouldNotifyColumnHeadersPresenter(target) || EGC.DataGridHelper.ShouldNotifyColumnHeaders(target)) && ColumnHeadersPresenter != null)
            {
                ColumnHeadersPresenter.NotifyPropertyChanged(d, propertyName, e, target);   
            }
        }

        /// <summary>
        ///     Called by DataGridColumnCollection when columns' DisplayIndex changes
        /// </summary>
        /// <param name="e"></param>
        internal void UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction action, int oldDisplayIndex, EGC.DataGridColumn oldColumn, int newDisplayIndex)
        {
            using (UpdateSelectedCells())
            {
                _selectedCells.OnColumnsChanged(action, oldDisplayIndex, oldColumn, newDisplayIndex, SelectedItems);
            }
        }

        /// <summary>
        ///     Reference to the ColumnHeadersPresenter. The presenter sets this when it is created.
        /// </summary>
        internal EGC.DataGridColumnHeadersPresenter ColumnHeadersPresenter
        {
            get { return _columnHeadersPresenter; }
            set { _columnHeadersPresenter = value; }
        }

        /// <summary>
        ///     OnTemplateChanged override
        /// </summary>
        protected override void OnTemplateChanged(ControlTemplate oldTemplate, ControlTemplate newTemplate)
        {
            base.OnTemplateChanged(oldTemplate, newTemplate);

            // Our column headers presenter comes from the template.  Clear out the reference to it if the template has changed
            ColumnHeadersPresenter = null;
        }

        /// <summary>
        ///     A cell is notifying the DataGrid that its IsKeyboardFocusWithin property changed.
        /// </summary>
        internal void CellIsKeyboardFocusWithinChanged(EGC.DataGridCell cell, bool isKeyboardFocusWithin)
        {
            UpdateCurrentCell(cell, isKeyboardFocusWithin);
        }

        #endregion

        #region GridLines

        /// <summary>
        ///     GridLinesVisibility Dependency Property
        /// </summary>
        public static readonly DependencyProperty GridLinesVisibilityProperty =
            DependencyProperty.Register(
                "GridLinesVisibility",
                typeof(EGC.DataGridGridLinesVisibility),
                typeof(EGC.DataGrid),
                new FrameworkPropertyMetadata(EGC.DataGridGridLinesVisibility.All, new PropertyChangedCallback(OnNotifyGridLinePropertyChanged)));

        /// <summary>
        ///     Specifies the visibility of the DataGrid's grid lines
        /// </summary>
        public EGC.DataGridGridLinesVisibility GridLinesVisibility
        {
            get { return (EGC.DataGridGridLinesVisibility)GetValue(GridLinesVisibilityProperty); }
            set { SetValue(GridLinesVisibilityProperty, value); }
        }

        /// <summary>
        /// HorizontalGridLinesBrush Dependency Property
        /// </summary>
        public static readonly DependencyProperty HorizontalGridLinesBrushProperty =
            DependencyProperty.Register(
                "HorizontalGridLinesBrush", 
                typeof(Brush),
                typeof(EGC.DataGrid),
                new FrameworkPropertyMetadata(Brushes.Black, new PropertyChangedCallback(OnNotifyGridLinePropertyChanged)));

        /// <summary>
        /// Specifies the Brush used to draw the horizontal grid lines
        /// </summary>
        public Brush HorizontalGridLinesBrush
        {
            get { return (Brush)GetValue(HorizontalGridLinesBrushProperty); }
            set { SetValue(HorizontalGridLinesBrushProperty, value); }
        }

        /// <summary>
        /// VerticalGridLinesBrush Dependency Property
        /// </summary>
        public static readonly DependencyProperty VerticalGridLinesBrushProperty =
            DependencyProperty.Register(
                "VerticalGridLinesBrush", 
                typeof(Brush),
                typeof(EGC.DataGrid),
                new FrameworkPropertyMetadata(Brushes.Black, new PropertyChangedCallback(OnNotifyGridLinePropertyChanged)));

        /// <summary>
        /// Specifies the Brush used to draw the vertical grid lines
        /// </summary>
        public Brush VerticalGridLinesBrush
        {
            get { return (Brush)GetValue(VerticalGridLinesBrushProperty); }
            set { SetValue(VerticalGridLinesBrushProperty, value); }
        }

#if GridLineThickness
        /// <summary>
        /// HorizontalGridLineThickness DependencyProperty
        /// </summary>
        public static readonly DependencyProperty HorizontalGridLineThicknessProperty =
                DependencyProperty.Register("HorizontalGridLineThickness", typeof(double), typeof(EGC.DataGrid),
                                            new FrameworkPropertyMetadata(1d, new PropertyChangedCallback(OnNotifyGridLinePropertyChanged)));
        
        /// <summary>
        /// Specifies the thickness of the horizontal grid lines.  
        /// </summary>
        public double HorizontalGridLineThickness 
        {
            get { return (double)GetValue(HorizontalGridLineThicknessProperty); }
            set { SetValue(HorizontalGridLineThicknessProperty, value); }
        }

        /// <summary>
        /// VerticalGridLineThickness DependencyProperty
        /// </summary>
        public static readonly DependencyProperty VerticalGridLineThicknessProperty =
                    DependencyProperty.Register("VerticalGridLineThickness", typeof(double), typeof(EGC.DataGrid),
                                                new FrameworkPropertyMetadata(1d, new PropertyChangedCallback(OnNotifyGridLinePropertyChanged)));


        /// <summary>
        /// Specifies the thickness of the vertical grid lines.  
        /// </summary>
        public double VerticalGridLineThickness
        {
            get { return (double)GetValue(VerticalGridLineThicknessProperty); }
            set { SetValue(VerticalGridLineThicknessProperty, value); }
        }

#else
        internal double HorizontalGridLineThickness
        {
            get { return 1.0; }
        }

        internal double VerticalGridLineThickness
        {
            get { return 1.0; }
        }
#endif

        #endregion 

        #region Row Generation

        /// <summary>
        ///     Determines if an item is its own container.
        /// </summary>
        /// <param name="item">The item to test.</param>
        /// <returns>true if the item is a DataGridRow, false otherwise.</returns>
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is EGC.DataGridRow;
        }

        /// <summary>
        ///     Instantiates an instance of a container.
        /// </summary>
        /// <returns>A new DataGridRow.</returns>
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new EGC.DataGridRow();
        }

        /// <summary>
        ///     Prepares a new container for a given item.
        /// </summary>
        /// <param name="element">The new container.</param>
        /// <param name="item">The item that the container represents.</param>
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);

            EGC.DataGridRow row = (EGC.DataGridRow)element;
            if (row.DataGridOwner != this)
            {
                row.Tracker.StartTracking(ref _rowTrackingRoot);
                EnsureInternalScrollControls();
            }

            // We dont want changes to DetailsVisibilty to fire the LoadingRowDetails event during prepare, so we disable it.
            row.DetailsEventStatus = EGC.DataGridRow.RowDetailsEventStatus.Disabled;
            row.PrepareRow(item, this);
            row.DetailsEventStatus = EGC.DataGridRow.RowDetailsEventStatus.Pending;
            OnLoadingRow(new EGC.DataGridRowEventArgs(row));
        }

        /// <summary>
        ///     Clears a container of references.
        /// </summary>
        /// <param name="element">The container being cleared.</param>
        /// <param name="item">The data item that the container represented.</param>
        protected override void ClearContainerForItemOverride(DependencyObject element, object item)
        {
            base.ClearContainerForItemOverride(element, item);

            EGC.DataGridRow row = (EGC.DataGridRow)element;
            if (row.DataGridOwner == this)
            {
                row.Tracker.StopTracking(ref _rowTrackingRoot);
            }

            OnUnloadingRow(new EGC.DataGridRowEventArgs(row));
            row.ClearRow(this);
        }

        /// <summary>
        ///     Propagates the collection changed notification on Columns down to
        ///     each active DataGridRow.
        /// </summary>
        /// <param name="e">The event arguments from the original collection changed event.</param>
        private void UpdateColumnsOnRows(NotifyCollectionChangedEventArgs e)
        {
            EGC.ContainerTracking<EGC.DataGridRow> tracker = _rowTrackingRoot;
            while (tracker != null)
            {
                tracker.Container.OnColumnsChanged(_columns, e);
                tracker = tracker.Next;
            }
        }

        /// <summary>
        ///     Equivalent of ItemContainerStyle.
        /// </summary>
        /// <remarks>
        ///     If this property has a non-null value, it will override the value
        ///     of ItemContainerStyle.
        /// </remarks>
        public Style RowStyle
        {
            get { return (Style)GetValue(RowStyleProperty); }
            set { SetValue(RowStyleProperty, value); }
        }

        /// <summary>
        ///     DependencyProperty for the RowStyle property.
        /// </summary>
        public static readonly DependencyProperty RowStyleProperty =
            DependencyProperty.Register("RowStyle", typeof(Style), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnRowStyleChanged)));

        private static void OnRowStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(ItemContainerStyleProperty);
        }

        private static object OnCoerceItemContainerStyle(DependencyObject d, object baseValue)
        {
            if (!EGC.DataGridHelper.IsDefaultValue(d, EGC.DataGrid.RowStyleProperty))
            {
                return d.GetValue(EGC.DataGrid.RowStyleProperty);
            }

            return baseValue;
        }

        /// <summary>
        /// Template used to visually indicate an error in row Validation.
        /// </summary>
        public ControlTemplate RowValidationErrorTemplate
        {
            get { return (ControlTemplate)GetValue(RowValidationErrorTemplateProperty); }
            set { SetValue(RowValidationErrorTemplateProperty, value); }
        }

        /// <summary>
        ///     DependencyProperty for the RowValidationErrorTemplate property.
        /// </summary>
        public static readonly DependencyProperty RowValidationErrorTemplateProperty =
            DependencyProperty.Register("RowValidationErrorTemplate", typeof(ControlTemplate), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyRowPropertyChanged)));

        /// <summary>
        ///     Validation rules that are run on each DataGridRow.  If DataGrid.ItemBindingGroup is used, RowValidationRules is ignored.
        /// </summary>
        public ObservableCollection<ValidationRule> RowValidationRules
        {
            get { return _rowValidationRules; }
        }

        /// <summary>
        ///     Called when the Columns collection changes.
        /// </summary>
        private void OnRowValidationRulesChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            BindingGroup itemBindingGroup = ItemBindingGroup;

            if (itemBindingGroup == null)
            {
                ItemBindingGroup = itemBindingGroup = new BindingGroup();
                _rowValidationBindingGroup = itemBindingGroup;
            }

            // only update the ItemBindingGroup if it's not user created.
            if (_rowValidationBindingGroup != null)
            {
                if (object.ReferenceEquals(itemBindingGroup, _rowValidationBindingGroup))
                {
                    switch (e.Action)
                    {
                        case NotifyCollectionChangedAction.Add:
                            foreach (ValidationRule rule in e.NewItems)
                            {
                                _rowValidationBindingGroup.ValidationRules.Add(rule);
                            }

                            break;

                        case NotifyCollectionChangedAction.Remove:
                            foreach (ValidationRule rule in e.OldItems)
                            {
                                _rowValidationBindingGroup.ValidationRules.Remove(rule);
                            }

                            break;

                        case NotifyCollectionChangedAction.Replace:
                            foreach (ValidationRule rule in e.OldItems)
                            {
                                _rowValidationBindingGroup.ValidationRules.Remove(rule);
                            }

                            foreach (ValidationRule rule in e.NewItems)
                            {
                                _rowValidationBindingGroup.ValidationRules.Add(rule);
                            }

                            break;

                        case NotifyCollectionChangedAction.Reset:
                            _rowValidationBindingGroup.ValidationRules.Clear();
                            break;
                    }
                }
                else
                {
                    _rowValidationBindingGroup = null;
                }
            }
        }

        /// <summary>
        ///     Equivalent of ItemContainerStyleSelector.
        /// </summary>
        /// <remarks>
        ///     If this property has a non-null value, it will override the value
        ///     of ItemContainerStyleSelector.
        /// </remarks>
        public StyleSelector RowStyleSelector
        {
            get { return (StyleSelector)GetValue(RowStyleSelectorProperty); }
            set { SetValue(RowStyleSelectorProperty, value); }
        }

        /// <summary>
        ///     DependencyProperty for the RowStyleSelector property.
        /// </summary>
        public static readonly DependencyProperty RowStyleSelectorProperty =
            DependencyProperty.Register("RowStyleSelector", typeof(StyleSelector), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnRowStyleSelectorChanged)));

        private static void OnRowStyleSelectorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(ItemContainerStyleSelectorProperty);
        }

        private static object OnCoerceItemContainerStyleSelector(DependencyObject d, object baseValue)
        {
            if (!EGC.DataGridHelper.IsDefaultValue(d, EGC.DataGrid.RowStyleSelectorProperty))
            {
                return d.GetValue(EGC.DataGrid.RowStyleSelectorProperty);
            }

            return baseValue;
        }

        private static object OnCoerceIsSynchronizedWithCurrentItem(DependencyObject d, object baseValue)
        {
            EGC.DataGrid dataGrid = (EGC.DataGrid)d;
            if (dataGrid.SelectionUnit == EGC.DataGridSelectionUnit.Cell)
            {
                // IsSynchronizedWithCurrentItem makes IsSelected=true on the current row. 
                // When SelectionUnit is Cell, we should not allow row selection. 
                return false;
            }

            return baseValue;
        }

        /// <summary>
        ///     The default row background brush.
        /// </summary>
        public Brush RowBackground
        {
            get { return (Brush)GetValue(RowBackgroundProperty); }
            set { SetValue(RowBackgroundProperty, value); }
        }

        /// <summary>
        ///     DependencyProperty for RowBackground.
        /// </summary>
        public static readonly DependencyProperty RowBackgroundProperty =
            DependencyProperty.Register("RowBackground", typeof(Brush), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyRowPropertyChanged)));

        /// <summary>
        ///     The default row background brush for use on every other row.
        /// </summary>
        /// <remarks>
        ///     Setting this property to a non-null value will coerce AlternationCount to 2.
        /// </remarks>
        public Brush AlternatingRowBackground
        {
            get { return (Brush)GetValue(AlternatingRowBackgroundProperty); }
            set { SetValue(AlternatingRowBackgroundProperty, value); }
        }

        /// <summary>
        ///     DependencyProperty for AlternatingRowBackground.
        /// </summary>
        public static readonly DependencyProperty AlternatingRowBackgroundProperty =
            DependencyProperty.Register("AlternatingRowBackground", typeof(Brush), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyDataGridAndRowPropertyChanged)));

        private static object OnCoerceAlternationCount(DependencyObject d, object baseValue)
        {
            // Only check AlternatingRowBackground if the value isn't already set
            // to something that can use it.
            if (((int)baseValue) < 2)
            {
                EGC.DataGrid dataGrid = (EGC.DataGrid)d;
                if (dataGrid.AlternatingRowBackground != null)
                {
                    // There is an alternate background, coerce to 2.
                    return 2;
                }
            }

            return baseValue;
        }

        /// <summary>
        ///     The default height of a row.
        /// </summary>
        public double RowHeight
        {
            get { return (double)GetValue(RowHeightProperty); }
            set { SetValue(RowHeightProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for RowHeight.
        /// </summary>
        public static readonly DependencyProperty RowHeightProperty =
            DependencyProperty.Register("RowHeight", typeof(double), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(double.NaN, new PropertyChangedCallback(OnNotifyCellsPresenterPropertyChanged)));

        /// <summary>
        ///     The default minimum height of a row.
        /// </summary>
        public double MinRowHeight
        {
            get { return (double)GetValue(MinRowHeightProperty); }
            set { SetValue(MinRowHeightProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for MinRowHeight.
        /// </summary>
        public static readonly DependencyProperty MinRowHeightProperty =
            DependencyProperty.Register("MinRowHeight", typeof(double), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(OnNotifyCellsPresenterPropertyChanged)));

        /// <summary>
        ///     The NewItemPlaceholder row uses this to set its visibility while it's preparing.
        /// </summary>
        internal Visibility PlaceholderVisibility
        {
            get
            {
                return _placeholderVisibility;
            }
        }

        /// <summary>
        ///     Event that is fired just before a row is prepared.
        /// </summary>
        public event EventHandler<EGC.DataGridRowEventArgs> LoadingRow;

        /// <summary>
        ///     Event that is fired just before a row is cleared.
        /// </summary>
        public event EventHandler<EGC.DataGridRowEventArgs> UnloadingRow;

        /// <summary>
        ///     Invokes the LoadingRow event
        /// </summary>
        protected virtual void OnLoadingRow(EGC.DataGridRowEventArgs e)
        {
            if (LoadingRow != null)
            {
                LoadingRow(this, e);
            }

            var row = e.Row;
            if (row.DetailsVisibility == Visibility.Visible && row.DetailsPresenter != null)
            {
                // Invoke LoadingRowDetails, but only after the details template is expanded (so DetailsElement will be available).
                // We dont want changes to DetailsVisibilty to fire the event before this dispatcher operation fires, so we disable it.
                row.DetailsEventStatus = EGC.DataGridRow.RowDetailsEventStatus.Disabled;
                Dispatcher.CurrentDispatcher.BeginInvoke(new DispatcherOperationCallback(DelayedOnLoadingRowDetails), DispatcherPriority.Loaded, row);
            }
        }

        private static object DelayedOnLoadingRowDetails(object arg)
        {
            var row = (EGC.DataGridRow)arg;
            var dataGrid = row.DataGridOwner;

            if (dataGrid != null && row.DetailsPresenter != null)
            {
                dataGrid.OnLoadingRowDetails(new EGC.DataGridRowDetailsEventArgs(row, row.DetailsPresenter.DetailsElement));
            }

            return null;
        }

        /// <summary>
        ///     Invokes the UnloadingRow event
        /// </summary>
        protected virtual void OnUnloadingRow(EGC.DataGridRowEventArgs e)
        {
            if (UnloadingRow != null)
            {
                UnloadingRow(this, e);
            }

            var row = e.Row;
            if (row.DetailsEventStatus == EGC.DataGridRow.RowDetailsEventStatus.Loaded && row.DetailsPresenter != null)
            {
                OnUnloadingRowDetails(new EGC.DataGridRowDetailsEventArgs(row, row.DetailsPresenter.DetailsElement));
            }
        }

        #endregion

        #region Row/Column Headers

        /// <summary>
        ///     The default width of a row header.
        /// </summary>
        public double RowHeaderWidth
        {
            get { return (double)GetValue(RowHeaderWidthProperty); }
            set { SetValue(RowHeaderWidthProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for RowHeaderWidth.
        /// </summary>
        public static readonly DependencyProperty RowHeaderWidthProperty =
            DependencyProperty.Register("RowHeaderWidth", typeof(double), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(double.NaN, new PropertyChangedCallback(OnNotifyRowHeaderWidthPropertyChanged)));

        /// <summary>
        ///     The actual width of row headers used for binding.  This is computed from the measure of all the visible row headers.
        /// </summary>
        public double RowHeaderActualWidth
        {
            get { return (double)GetValue(RowHeaderActualWidthProperty); }
            internal set { SetValue(RowHeaderActualWidthPropertyKey, value); }
        }

        /// <summary>
        ///     The DependencyPropertyKey for RowHeaderActualWidth.
        /// </summary>
        private static readonly DependencyPropertyKey RowHeaderActualWidthPropertyKey =
            DependencyProperty.RegisterReadOnly("RowHeaderActualWidth", typeof(double), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(OnNotifyRowHeaderPropertyChanged)));

        /// <summary>
        ///     The DependencyProperty for RowHeaderActualWidth.
        /// </summary>
        public static readonly DependencyProperty RowHeaderActualWidthProperty = RowHeaderActualWidthPropertyKey.DependencyProperty;            

        /// <summary>
        ///     The default height of a column header.
        /// </summary>
        public double ColumnHeaderHeight
        {
            get { return (double)GetValue(ColumnHeaderHeightProperty); }
            set { SetValue(ColumnHeaderHeightProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for ColumnHeaderHeight.
        /// </summary>
        public static readonly DependencyProperty ColumnHeaderHeightProperty =
            DependencyProperty.Register("ColumnHeaderHeight", typeof(double), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(double.NaN, OnNotifyColumnHeaderPropertyChanged));

        /// <summary>
        ///     A property that specifies the visibility of the column & row headers.
        /// </summary>
        public EGC.DataGridHeadersVisibility HeadersVisibility
        {
            get { return (EGC.DataGridHeadersVisibility)GetValue(HeadersVisibilityProperty); }
            set { SetValue(HeadersVisibilityProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty that represents the HeadersVisibility property.
        /// </summary>
        public static readonly DependencyProperty HeadersVisibilityProperty =
            DependencyProperty.Register("HeadersVisibility", typeof(EGC.DataGridHeadersVisibility), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(DataGridHeadersVisibility.All));

        /// <summary>
        ///     Updates RowHeaderActualWidth to reflect changes to RowHeaderWidth 
        /// </summary>
        private static void OnNotifyRowHeaderWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var dataGrid = ((EGC.DataGrid)d);
            var newValue = (double)e.NewValue;
            
            if (!DoubleUtil.IsNaN(newValue))
            {
                dataGrid.RowHeaderActualWidth = newValue;
            }
            else
            {
                // If we're entering Auto mode we need to reset the RowHeaderActualWidth 
                // because the previous explicit value may have been bigger than the Auto width.
                dataGrid.RowHeaderActualWidth = 0.0;
            }

            OnNotifyRowHeaderPropertyChanged(d, e);
        }

        /// <summary>
        /// Resets the RowHeaderActualWidth to 0.0 if in Auto mode
        /// </summary>
        private void ResetRowHeaderActualWidth()
        {
            if (DoubleUtil.IsNaN(RowHeaderWidth))
            {
                RowHeaderActualWidth = 0.0;
            }
        }

        #endregion

        #region Item Associated Properties

        /// <summary>
        ///     Sets the specified item's DetailsVisibility.
        /// </summary>
        /// <remarks>
        ///     This is useful when a DataGridRow may not currently exists to set DetailsVisibility on.
        /// </remarks>
        /// <param name="item">The item that will have its DetailsVisibility set.</param>
        /// <param name="detailsVisibility">The Visibility that the item's details should get.</param>
        public void SetDetailsVisibilityForItem(object item, Visibility detailsVisibility)
        {
            _itemAttachedStorage.SetValue(item, EGC.DataGridRow.DetailsVisibilityProperty, detailsVisibility);

            var row = (EGC.DataGridRow)ItemContainerGenerator.ContainerFromItem(item);
            if (row != null)
            {
                row.DetailsVisibility = detailsVisibility;
            }
        }

        /// <summary>
        ///     Returns the current DetailsVisibility for an item that's in the DataGrid.
        /// </summary>
        /// <param name="item">The item who's DetailsVisibility you would like to get</param>
        /// <returns>The DetailsVisibility associated with the specified item.</returns>
        public Visibility GetDetailsVisibilityForItem(object item)
        {
            object detailsVisibility;
            if (_itemAttachedStorage.TryGetValue(item, EGC.DataGridRow.DetailsVisibilityProperty, out detailsVisibility))
            {
                return (Visibility)detailsVisibility;
            }

            var row = (EGC.DataGridRow)ItemContainerGenerator.ContainerFromItem(item);
            if (row != null)
            {
                return row.DetailsVisibility;
            }

            // If we dont have a row, we can infer it's Visibility from the current RowDetailsVisibilityMode
            switch (RowDetailsVisibilityMode)
            {
                case EGC.DataGridRowDetailsVisibilityMode.VisibleWhenSelected:
                    return SelectedItems.Contains(item) ? Visibility.Visible : Visibility.Collapsed;
                case EGC.DataGridRowDetailsVisibilityMode.Visible:
                    return Visibility.Visible;
                default:
                    return Visibility.Collapsed;  
            }
        }

        /// <summary>
        ///     Clears the DetailsVisibility for the specified item
        /// </summary>
        /// <param name="item">The item to clear the DetailsVisibility on.</param>
        public void ClearDetailsVisibilityForItem(object item)
        {
            _itemAttachedStorage.ClearValue(item, EGC.DataGridRow.DetailsVisibilityProperty);

            var row = (EGC.DataGridRow)ItemContainerGenerator.ContainerFromItem(item);
            if (row != null)
            {
                row.ClearValue(EGC.DataGridRow.DetailsVisibilityProperty);
            }
        }

        internal EGC.DataGridItemAttachedStorage ItemAttachedStorage
        {
            get { return _itemAttachedStorage; }
        }

        #endregion

        #region Style Properties

        /// <summary>
        ///     A style to apply to all cells in the DataGrid.
        /// </summary>
        public Style CellStyle
        {
            get { return (Style)GetValue(CellStyleProperty); }
            set { SetValue(CellStyleProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty that represents the CellStyle property.
        /// </summary>
        public static readonly DependencyProperty CellStyleProperty =
            DependencyProperty.Register("CellStyle", typeof(Style), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyColumnAndCellPropertyChanged)));

        /// <summary>
        ///     A style to apply to all column headers in the DataGrid
        /// </summary>
        public Style ColumnHeaderStyle
        {
            get { return (Style)GetValue(ColumnHeaderStyleProperty); }
            set { SetValue(ColumnHeaderStyleProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty that represents the ColumnHeaderStyle property.
        /// </summary>
        public static readonly DependencyProperty ColumnHeaderStyleProperty =
            DependencyProperty.Register("ColumnHeaderStyle", typeof(Style), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyColumnAndColumnHeaderPropertyChanged)));

        /// <summary>
        ///     A style to apply to all row headers in the DataGrid
        /// </summary>
        public Style RowHeaderStyle
        {
            get { return (Style)GetValue(RowHeaderStyleProperty); }
            set { SetValue(RowHeaderStyleProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty that represents the RowHeaderStyle property.
        /// </summary>
        public static readonly DependencyProperty RowHeaderStyleProperty =
            DependencyProperty.Register("RowHeaderStyle", typeof(Style), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyRowAndRowHeaderPropertyChanged)));

        /// <summary>
        ///     The object representing the Row Header template.  
        /// </summary>
        public DataTemplate RowHeaderTemplate
        {
            get { return (DataTemplate)GetValue(RowHeaderTemplateProperty); }
            set { SetValue(RowHeaderTemplateProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for the RowHeaderTemplate property.
        /// </summary>
        public static readonly DependencyProperty RowHeaderTemplateProperty =
            DependencyProperty.Register("RowHeaderTemplate", typeof(DataTemplate), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyRowAndRowHeaderPropertyChanged)));

        /// <summary>
        ///     The object representing the Row Header template selector.  
        /// </summary>
        public DataTemplateSelector RowHeaderTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(RowHeaderTemplateSelectorProperty); }
            set { SetValue(RowHeaderTemplateSelectorProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for the RowHeaderTemplateSelector property.
        /// </summary>
        public static readonly DependencyProperty RowHeaderTemplateSelectorProperty =
            DependencyProperty.Register("RowHeaderTemplateSelector", typeof(DataTemplateSelector), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyRowAndRowHeaderPropertyChanged)));

        /// <summary>
        ///     The default style references this brush to create a thicker border
        ///     around the focused cell.
        /// </summary>
        public static ComponentResourceKey FocusBorderBrushKey
        {
            get
            {
                if (_focusBorderBrushKey == null)
                {
                    _focusBorderBrushKey = new ComponentResourceKey(typeof(EGC.DataGrid), "FocusBorderBrushKey");
                }

                return _focusBorderBrushKey;
            }
        }

        /// <summary>
        ///     A converter which converts DataGridHeadersVisibility to VisibilityConverter based on a ConverterParameter.
        /// </summary>
        /// <remarks>
        ///     This can be used in the DataGrid's template to control which parts of the DataGrid are visible for a given DataGridHeadersVisibility.
        /// </remarks>
        public static IValueConverter HeadersVisibilityConverter
        {
            get
            {
                // This is delay created in case the template doesn't use it. 
                if (_headersVisibilityConverter == null)
                {
                    _headersVisibilityConverter = new EGC.DataGridHeadersVisibilityToVisibilityConverter();
                }

                return _headersVisibilityConverter;
            }
        }

        /// <summary>
        ///     A converter which converts bool to SelectiveScrollingOrientation based on a ConverterParameter.
        /// </summary>
        /// <remarks>
        ///     This can be used in the DataGrid's template to control how the RowDetails selectively scroll based on a bool.
        /// </remarks>
        public static IValueConverter RowDetailsScrollingConverter
        {
            get
            {
                // This is delay created in case the template doesn't use it. 
                if (_rowDetailsScrollingConverter == null)
                {
                    _rowDetailsScrollingConverter = new EGC.BooleanToSelectiveScrollingOrientationConverter();
                }

                return _rowDetailsScrollingConverter;
            }
        }

        #endregion

        #region Scrolling

        /// <summary>
        ///     Defines the behavior that determines the visibility of horizontal ScrollBars.
        /// </summary>
        public ScrollBarVisibility HorizontalScrollBarVisibility
        {
            get { return (ScrollBarVisibility)GetValue(HorizontalScrollBarVisibilityProperty); }
            set { SetValue(HorizontalScrollBarVisibilityProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for the HorizontalScrollBarVisibility property.
        /// </summary>
        public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty = ScrollViewer.HorizontalScrollBarVisibilityProperty.AddOwner(typeof(EGC.DataGrid), new FrameworkPropertyMetadata(ScrollBarVisibility.Auto));

        /// <summary>
        ///     Defines the behavior that determines the visibility of vertical ScrollBars.
        /// </summary>
        public ScrollBarVisibility VerticalScrollBarVisibility
        {
            get { return (ScrollBarVisibility)GetValue(VerticalScrollBarVisibilityProperty); }
            set { SetValue(VerticalScrollBarVisibilityProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for the HorizontalScrollBarVisibility property.
        /// </summary>
        public static readonly DependencyProperty VerticalScrollBarVisibilityProperty = ScrollViewer.VerticalScrollBarVisibilityProperty.AddOwner(typeof(EGC.DataGrid), new FrameworkPropertyMetadata(ScrollBarVisibility.Auto));

        /// <summary>
        ///     Scrolls a row into view.
        /// </summary>
        /// <param name="item">The data item of the row to bring into view.</param>
        public void ScrollIntoView(object item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
            {
                ScrollRowIntoView(item);
            }
            else
            {
                // The items aren't generated, try at a later time
                Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(OnScrollIntoView), item);
            }
        }

        /// <summary>
        ///     Scrolls a cell into view.
        /// If column is null then only vertical scroll is performed.
        /// If row is null then only horizontal scroll is performed.
        /// </summary>
        /// <param name="item">The data item row that contains the cell.</param>
        /// <param name="column">The cell's column.</param>
        public void ScrollIntoView(object item, EGC.DataGridColumn column)
        {
            if (column == null)
            {
                ScrollIntoView(item);
                return;
            }

            if (!column.IsVisible)
            {
                return;
            }

            if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
            {
                // Scroll by column only
                if (item == null) 
                {
                    ScrollColumnIntoView(column);
                }
                else
                {
                    ScrollCellIntoView(item, column);
                }
            }
            else
            {
                // The items aren't generated, try at a later time
                Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(OnScrollIntoView), new object[] { item, column });
            }
        }

        /// <summary>
        ///     Previous call to ScrollIntoView found that the generator had not finished
        ///     generating cells. This is the callback at Loaded priority when hopefully
        ///     that has occured.
        /// </summary>
        private object OnScrollIntoView(object arg)
        {
            object[] arguments = arg as object[];
            if (arguments != null)
            {
                if (arguments[0] != null)
                {
                    ScrollCellIntoView(arguments[0], (EGC.DataGridColumn)arguments[1]);
                }
                else
                {
                    ScrollColumnIntoView((EGC.DataGridColumn)arguments[1]);
                }
            }
            else
            {
                ScrollRowIntoView(arg);
            }

            return null;
        }

        private void ScrollColumnIntoView(EGC.DataGridColumn column)
        {
            if (_rowTrackingRoot != null)
            {
                EGC.DataGridRow row = _rowTrackingRoot.Container;
                if (row != null)
                {
                    int columnIndex = _columns.IndexOf(column);
                    row.ScrollCellIntoView(columnIndex);
                }
            }
        }

        // TODO: Consider making a protected virtual so that sub-classes can customize the behavior
        private void ScrollRowIntoView(object item)
        {
            FrameworkElement element = ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
            if (element != null)
            {
                element.BringIntoView();
            }
            else if (!IsGrouping)
            {
                // We might be virtualized, try scrolling by index.
                int index = Items.IndexOf(item);
                if (index >= 0)
                {
                    // It would be convenient for ItemsHost to be public, but since it is not,
                    // we are relying on some internal communication between DataGridRowsPresenter
                    // and the DataGrid.
                    EGC.DataGridRowsPresenter itemsHost = InternalItemsHost as EGC.DataGridRowsPresenter;
                    if (itemsHost != null)
                    {
                        // It would have been better to be able to directly call BringIndexIntoView,
                        // but that method is protected, so we are relying on an internal
                        // method on DataGridRowsPresenter to make the call.
                        itemsHost.InternalBringIndexIntoView(index);
                    }
                }
            }
        }

        // TODO: Consider making a protected virtual so that sub-classes can customize the behavior
        private void ScrollCellIntoView(object item, EGC.DataGridColumn column)
        {
            Debug.Assert(item != null, "item is null.");
            Debug.Assert(column != null, "column is null.");

            if (!column.IsVisible)
            {
                return;
            }

            // Devirtualize the concerned row if it is not already
            EGC.DataGridRow row = ItemContainerGenerator.ContainerFromItem(item) as EGC.DataGridRow;
            if (row == null)
            {
                ScrollRowIntoView(item);
                UpdateLayout();
                row = ItemContainerGenerator.ContainerFromItem(item) as EGC.DataGridRow;
            }

            // Use the row to scroll cell into view.
            if (row != null)
            {
                int columnIndex = _columns.IndexOf(column);
                row.ScrollCellIntoView(columnIndex);
            }
        }

        /// <summary>
        ///     Called when IsMouseCaptured changes on this element.
        /// </summary>
        protected override void OnIsMouseCapturedChanged(DependencyPropertyChangedEventArgs e)
        {
            if (!IsMouseCaptured)
            {
                // When capture is lost, stop auto-scrolling
                StopAutoScroll();
            }

            base.OnIsMouseCapturedChanged(e);
        }

        private static TimeSpan AutoScrollTimeout
        {
            get
            {
                // NOTE: NtUser does the following (file: windows/ntuser/kernel/sysmet.c)
                //     gpsi->dtLBSearch = dtTime * 4;          // dtLBSearch   = 4   * gdtDblClk
                //     gpsi->dtScroll = gpsi->dtLBSearch / 5;  // dtScroll     = 4/5 * gdtDblClk
                return TimeSpan.FromMilliseconds(NativeMethods.GetDoubleClickTime() * 0.8);
            }
        }

        /// <summary>
        ///     Begins a timer that will periodically scroll and select.
        /// </summary>
        private void StartAutoScroll()
        {
            if (_autoScrollTimer == null)
            {
                _hasAutoScrolled = false;

                // Same priority as ListBox. Currently choosing SystemIdle over ApplicationIdle since the layout
                // manger will do some work (sometimes) at ApplicationIdle.
                _autoScrollTimer = new DispatcherTimer(DispatcherPriority.SystemIdle);
                _autoScrollTimer.Interval = AutoScrollTimeout;
                _autoScrollTimer.Tick += new EventHandler(OnAutoScrollTimeout);
                _autoScrollTimer.Start();
            }
        }

        /// <summary>
        ///     Stops the timer that controls auto-scrolling.
        /// </summary>
        private void StopAutoScroll()
        {
            if (_autoScrollTimer != null)
            {
                _autoScrollTimer.Stop();
                _autoScrollTimer = null;
                _hasAutoScrolled = false;
            }
        }

        /// <summary>
        ///     The callback when the auto-scroll timer ticks.
        /// </summary>
        private void OnAutoScrollTimeout(object sender, EventArgs e)
        {
            if (Mouse.LeftButton == MouseButtonState.Pressed)
            {
                DoAutoScroll();
            }
            else
            {
                StopAutoScroll();
            }
        }

        /// <summary>
        ///     Based on the mouse position relative to the rows and cells,
        ///     scrolls and selects rows and/or cells.
        /// </summary>
        /// <returns>true if a scroll and select was attempted. false otherwise.</returns>
        private bool DoAutoScroll()
        {
            Debug.Assert(_isDraggingSelection, "DoAutoScroll should only be called when dragging selection.");

            RelativeMousePositions position = RelativeMousePosition;
            if (position != RelativeMousePositions.Over)
            {
                // Get the cell that is nearest the mouse position and is
                // not being clipped by the ScrollViewer.
                EGC.DataGridCell cell = GetCellNearMouse();
                if (cell != null)
                {
                    EGC.DataGridColumn column = cell.Column;
                    object dataItem = cell.RowDataItem;

                    // Based on the position of the mouse relative to the field
                    // of cells, choose the cell that is torwards the mouse.
                    // Note: This assumes a grid layout.
                    if (IsMouseToLeft(position))
                    {
                        int columnIndex = column.DisplayIndex;
                        if (columnIndex > 0)
                        {
                            column = ColumnFromDisplayIndex(columnIndex - 1);
                        }
                    }
                    else if (IsMouseToRight(position))
                    {
                        int columnIndex = column.DisplayIndex;
                        if (columnIndex < (_columns.Count - 1))
                        {
                            column = ColumnFromDisplayIndex(columnIndex + 1);
                        }
                    }

                    if (IsMouseAbove(position))
                    {
                        int rowIndex = Items.IndexOf(dataItem);
                        if (rowIndex > 0)
                        {
                            dataItem = Items[rowIndex - 1];
                        }
                    }
                    else if (IsMouseBelow(position))
                    {
                        int rowIndex = Items.IndexOf(dataItem);
                        if (rowIndex < (Items.Count - 1))
                        {
                            dataItem = Items[rowIndex + 1];
                        }
                    }

                    if (_isRowDragging)
                    {
                        // Perform a row header drag-select
                        ScrollRowIntoView(dataItem);
                        EGC.DataGridRow row = (EGC.DataGridRow)ItemContainerGenerator.ContainerFromItem(dataItem);
                        if (row != null)
                        {
                            _hasAutoScrolled = true;
                            HandleSelectionForRowHeaderAndDetailsInput(row, /* startDragging = */ false);
                            CurrentItem = dataItem;
                            return true;
                        }
                    }
                    else
                    {
                        // Perform a cell drag-select
                        ScrollCellIntoView(dataItem, column);
                        cell = TryFindCell(dataItem, column);
                        if (cell != null)
                        {
                            _hasAutoScrolled = true;
                            HandleSelectionForCellInput(cell, /* startDragging = */ false, /* allowsExtendSelect = */ true, /* allowsMinimalSelect = */ true);
                            cell.Focus();
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        /// <summary>
        ///     Prevents the ScrollViewer from handling keyboard input.
        /// </summary>
        protected override bool HandlesScrolling
        {
            get { return true; }
        }

        /// <summary>
        ///     Workaround for not having access to ItemsControl.ItemsHost.
        /// </summary>
        internal Panel InternalItemsHost
        {
            get { return _internalItemsHost; }
            set
            {
                if (_internalItemsHost != value)
                {
                    _internalItemsHost = value;
                    if (_internalItemsHost != null)
                    {
                        EnsureInternalScrollControls();
                    }
                }
            }
        }

        /// <summary>
        ///     Workaround for not having access to ItemsControl.ScrollHost.
        /// </summary>
        internal ScrollViewer InternalScrollHost
        {
            get
            {
                EnsureInternalScrollControls();
                return _internalScrollHost;
            }
        }

        /// <summary>
        ///     Workaround for not having access to ScrollContentPresenter
        /// </summary>
        internal ScrollContentPresenter InternalScrollContentPresenter
        {
            get
            {
                EnsureInternalScrollControls();
                return _internalScrollContentPresenter;
            }
        }

        /// <summary>
        ///     Helper method which ensures the initialization of scroll controls.
        /// </summary>
        protected virtual void EnsureInternalScrollControls()
        {
            if (_internalScrollContentPresenter == null)
            {
                if (_internalItemsHost != null)
                {
                    _internalScrollContentPresenter = EGC.DataGridHelper.FindVisualParent<ScrollContentPresenter>(_internalItemsHost);
                }
                else if (_rowTrackingRoot != null)
                {
                    EGC.DataGridRow row = _rowTrackingRoot.Container;
                    _internalScrollContentPresenter = EGC.DataGridHelper.FindVisualParent<ScrollContentPresenter>(row);
                }
                if (_internalScrollContentPresenter != null)
                {
                    _internalScrollContentPresenter.SizeChanged += new SizeChangedEventHandler(OnInternalScrollContentPresenterSizeChanged);
                }
            }

            if (_internalScrollHost == null)
            {
                if (_internalItemsHost != null)
                {
                    _internalScrollHost = EGC.DataGridHelper.FindVisualParent<ScrollViewer>(_internalItemsHost);
                }
                else if (_rowTrackingRoot != null)
                {
                    EGC.DataGridRow row = _rowTrackingRoot.Container;
                    _internalScrollHost = EGC.DataGridHelper.FindVisualParent<ScrollViewer>(row);
                }
                if (_internalScrollHost != null)
                {
                    Binding horizontalOffsetBinding = new Binding("ContentHorizontalOffset");
                    horizontalOffsetBinding.Source = _internalScrollHost;
                    SetBinding(HorizontalScrollOffsetProperty, horizontalOffsetBinding);
                }
            }
        }

        /// <summary>
        ///     Helper method which cleans up the internal scroll controls.
        /// </summary>
        private void CleanUpInternalScrollControls()
        {
            BindingOperations.ClearBinding(this, HorizontalScrollOffsetProperty);
            _internalScrollHost = null;
            if (_internalScrollContentPresenter != null)
            {
                _internalScrollContentPresenter.SizeChanged -= new SizeChangedEventHandler(OnInternalScrollContentPresenterSizeChanged);
                _internalScrollContentPresenter = null;
            }
        }

        /// <summary>
        ///     Size changed handler for InteralScrollContentPresenter.
        /// </summary>
        private void OnInternalScrollContentPresenterSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (_internalScrollContentPresenter != null &&
                !_internalScrollContentPresenter.CanContentScroll)
            {
                OnViewportSizeChanged(e.PreviousSize, e.NewSize);
            }
        }

        /// <summary>
        ///     Helper method which enqueues a viewport width change 
        ///     request to Dispatcher if needed.
        /// </summary>
        internal void OnViewportSizeChanged(Size oldSize, Size newSize)
        {
            if (!InternalColumns.ColumnWidthsComputationPending)
            {
                double widthChange = newSize.Width - oldSize.Width;
                if (!DoubleUtil.AreClose(widthChange, 0.0))
                {
                    _finalViewportWidth = newSize.Width;
                    if (!_viewportWidthChangeNotificationPending)
                    {
                        _originalViewportWidth = oldSize.Width;
                        Dispatcher.BeginInvoke(new DispatcherOperationCallback(OnDelayedViewportWidthChanged), DispatcherPriority.Loaded, this);
                        _viewportWidthChangeNotificationPending = true;
                    }
                }
            }
        }

        /// <summary>
        ///     Dispatcher callback method for Viewport width change
        ///     which propagates the notification if needed.
        /// </summary>
        private object OnDelayedViewportWidthChanged(object args)
        {
            if (!_viewportWidthChangeNotificationPending)
            {
                return null;
            }

            double widthChange = _finalViewportWidth - _originalViewportWidth;
            if (!DoubleUtil.AreClose(widthChange, 0.0))
            {
                NotifyPropertyChanged(this, 
                    "ViewportWidth", 
                    new DependencyPropertyChangedEventArgs(), 
                    NotificationTarget.CellsPresenter | NotificationTarget.ColumnHeadersPresenter | NotificationTarget.ColumnCollection);

                double totalAvailableWidth = _finalViewportWidth;
                totalAvailableWidth -= CellsPanelHorizontalOffset;
                InternalColumns.RedistributeColumnWidthsOnAvailableSpaceChange(widthChange, totalAvailableWidth);
            }
            _viewportWidthChangeNotificationPending = false;
            return null;
        }

        /// <summary>
        ///     Dependency property which would be bound to ContentHorizontalOffset
        ///     property of the ScrollViewer.
        /// </summary>
        internal static readonly DependencyProperty HorizontalScrollOffsetProperty =
            DependencyProperty.Register(
                "HorizontalScrollOffset",
                typeof(double),
                typeof(EGC.DataGrid),
                new FrameworkPropertyMetadata(0d, OnNotifyHorizontalOffsetPropertyChanged));

        /// <summary>
        ///     The HorizontalOffset of the scroll viewer
        /// </summary>
        internal double HorizontalScrollOffset
        {
            get
            {
                return (double)GetValue(HorizontalScrollOffsetProperty);
            }
        }

        #endregion

        #region Editing Commands

        /// <summary>
        ///     The command to fire and allow to route to the DataGrid in order to indicate that the
        ///     current cell or row should begin editing.
        /// </summary>
        public static readonly RoutedCommand BeginEditCommand = new RoutedCommand(SR.Get(SRID.DataGrid_BeginEditCommandText), typeof(EGC.DataGrid));

        /// <summary>
        ///     The command to fire and allow to route to the DataGrid in order to indicate that the
        ///     current cell or row should commit any pending changes and exit edit mode.
        /// </summary>
        public static readonly RoutedCommand CommitEditCommand = new RoutedCommand(SR.Get(SRID.DataGrid_CommitEditCommandText), typeof(EGC.DataGrid));

        /// <summary>
        ///     The command to fire and allow to route to the DataGrid in order to indicate that the
        ///     current cell or row should purge any pending changes and revert to the state it was
        ///     in before BeginEdit.
        /// </summary>
        public static readonly RoutedCommand CancelEditCommand = new RoutedCommand(SR.Get(SRID.DataGrid_CancelEditCommandText), typeof(EGC.DataGrid));

        /// <summary>
        ///     A command that, when invoked, will delete the current row.
        /// </summary>
        public static readonly RoutedCommand DeleteCommand = new RoutedCommand(SR.Get(SRID.DataGrid_DeleteCommandText), typeof(EGC.DataGrid));

        private static void OnCanExecuteBeginEdit(object sender, CanExecuteRoutedEventArgs e)
        {
            ((EGC.DataGrid)sender).OnCanExecuteBeginEdit(e);
        }

        private static void OnExecutedBeginEdit(object sender, ExecutedRoutedEventArgs e)
        {
            ((EGC.DataGrid)sender).OnExecutedBeginEdit(e);
        }

        /// <summary>
        ///     Invoked to determine if the BeginEdit command can be executed.
        /// </summary>
        protected virtual void OnCanExecuteBeginEdit(CanExecuteRoutedEventArgs e)
        {
            bool canExecute = !IsReadOnly && (CurrentCellContainer != null) && !IsEditingCurrentCell && !IsCurrentCellReadOnly && !HasCellValidationError;

            if (canExecute && HasRowValidationError)
            {
                EGC.DataGridCell cellContainer = GetEventCellOrCurrentCell(e);
                if (cellContainer != null)
                {
                    object rowItem = cellContainer.RowDataItem;

                    // When there is a validation error, only allow editing on that row
                    canExecute = IsAddingOrEditingRowItem(rowItem);
                }
                else
                {
                    // Don't allow entering edit mode when there is a pending validation error
                    canExecute = false;
                }
            }

            e.CanExecute = canExecute;
            e.Handled = true;
        }

        /// <summary>
        ///     Invoked when the BeginEdit command is executed.
        /// </summary>
        protected virtual void OnExecutedBeginEdit(ExecutedRoutedEventArgs e)
        {
            EGC.DataGridCell cell = CurrentCellContainer;
            if ((cell != null) && !cell.IsReadOnly && !cell.IsEditing)
            {
                bool addedPlaceholder = false;
                bool deselectedPlaceholder = false;
                bool reselectPlaceholderCells = false;
                List<int> columnIndexRanges = null;
                int newItemIndex = -1;
                object newItem = null;
                bool placeholderAtBeginning = (EditableItems.NewItemPlaceholderPosition == NewItemPlaceholderPosition.AtBeginning);

                if (IsNewItemPlaceholder(cell.RowDataItem))
                {
                    // If editing the new item placeholder, then create a new item and edit that instead.
                    if (SelectedItems.Contains(CollectionView.NewItemPlaceholder))
                    {
                        // Unselect the NewItemPlaceholder and select the new row
                        UnselectItem(CollectionView.NewItemPlaceholder);
                        deselectedPlaceholder = true;
                    }
                    else
                    {
                        // Cells will automatically unselect when the new item placeholder is removed, but we
                        // should reselect them on the new item.
                        newItemIndex = Items.IndexOf(cell.RowDataItem);
                        reselectPlaceholderCells = ((newItemIndex >= 0) && _selectedCells.Intersects(newItemIndex, out columnIndexRanges));
                    }

                    newItem = AddNewItem();
                    CurrentItem = newItem; // Puts focus on the added row
                    cell = CurrentCellContainer;
                    if (CurrentCellContainer == null)
                    {
                        // CurrentCellContainer becomes null if focus moves out of the datagrid
                        // Calling UpdateLayout instantiates the CurrentCellContainer
                        UpdateLayout();
                        cell = CurrentCellContainer;
                        if ((cell != null) && !cell.IsKeyboardFocusWithin)
                        {
                            cell.Focus();
                        }
                    }

                    if (deselectedPlaceholder)
                    {
                        // Re-select the new item if the placeholder was selected before
                        SelectItem(newItem);
                    }
                    else if (reselectPlaceholderCells)
                    {
                        // Re-select placeholder cells if they were selected before
                        using (UpdateSelectedCells())
                        {
                            int rowIndex = newItemIndex;

                            // When the placeholder is at the beginning, we don't hide it, so those cells need to be unselected.
                            // The cells to select are also now one row below.
                            if (placeholderAtBeginning)
                            {
                                _selectedCells.RemoveRegion(newItemIndex, 0, 1, Columns.Count);
                                rowIndex++;
                            }

                            for (int i = 0, count = columnIndexRanges.Count; i < count; i += 2)
                            {
                                _selectedCells.AddRegion(rowIndex, columnIndexRanges[i], 1, columnIndexRanges[i + 1]);
                            }
                        }
                    }

                    addedPlaceholder = true;
                }

                RoutedEventArgs editingEventArgs = e.Parameter as RoutedEventArgs;
                EGC.DataGridBeginningEditEventArgs beginningEditEventArgs = null;

                if (cell != null)
                {
                    // Give the callback an opportunity to cancel edit mode
                    beginningEditEventArgs = new EGC.DataGridBeginningEditEventArgs(cell.Column, cell.RowOwner, editingEventArgs);
                    OnBeginningEdit(beginningEditEventArgs);
                }

                if ((cell == null) || beginningEditEventArgs.Cancel)
                {
                    // If CurrentCellContainer is null then cancel editing
                    if (deselectedPlaceholder)
                    {
                        // If the new item placeholder was deselected and the new item was selected,
                        // de-select the new item. Selecting the new item placeholder comes at the end.
                        // This is to accomodate the scenario where the new item placeholder only appears
                        // when not editing a new item.
                        UnselectItem(newItem);
                    }
                    else if (reselectPlaceholderCells && placeholderAtBeginning)
                    {
                        // When the placeholder is at the beginning, we need to unselect the added item cells.
                        _selectedCells.RemoveRegion(newItemIndex + 1, 0, 1, Columns.Count);
                    }

                    if (addedPlaceholder)
                    {
                        // The edit was canceled, cancel the new item
                        CancelRowItem();

                        // Display the new item placeholder again
                        UpdateNewItemPlaceholder(/* isAddingNewItem = */ false);

                        // Put focus back on the placeholder
                        SetCurrentItemToPlaceholder();
                    }

                    if (deselectedPlaceholder)
                    {
                        // If the new item placeholder was deselected, then select it again.
                        SelectItem(CollectionView.NewItemPlaceholder);
                    }
                    else if (reselectPlaceholderCells)
                    {
                        for (int i = 0, count = columnIndexRanges.Count; i < count; i += 2)
                        {
                            _selectedCells.AddRegion(newItemIndex, columnIndexRanges[i], 1, columnIndexRanges[i + 1]);
                        }
                    }
                }
                else
                {
                    if (!addedPlaceholder && !IsEditingRowItem)
                    {
                        EditRowItem(cell.RowDataItem);
                        
                        var bindingGroup = cell.RowOwner.BindingGroup;
                        if (bindingGroup != null)
                        {
                            bindingGroup.BeginEdit();
                        }

                        _editingRowItem = cell.RowDataItem;
                        _editingRowIndex = Items.IndexOf(_editingRowItem);
                    }

                    cell.BeginEdit(editingEventArgs);
                    cell.RowOwner.IsEditing = true;
                }
            }

            // CancelEdit and CommitEdit rely on IsAddingNewItem and IsEditingRowItem
            CommandManager.InvalidateRequerySuggested();

            e.Handled = true;
        }

        private static void OnCanExecuteCommitEdit(object sender, CanExecuteRoutedEventArgs e)
        {
            ((EGC.DataGrid)sender).OnCanExecuteCommitEdit(e);
        }

        private static void OnExecutedCommitEdit(object sender, ExecutedRoutedEventArgs e)
        {
            ((EGC.DataGrid)sender).OnExecutedCommitEdit(e);
        }

        private EGC.DataGridCell GetEventCellOrCurrentCell(RoutedEventArgs e)
        {
            // If the command routed through a cell, then use that cell. Otherwise, use the current cell.
            UIElement source = e.OriginalSource as UIElement;
            return ((source == this) || (source == null)) ? CurrentCellContainer : EGC.DataGridHelper.FindVisualParent<EGC.DataGridCell>(source);
        }

        private bool CanEndEdit(CanExecuteRoutedEventArgs e, bool commit)
        {
            EGC.DataGridCell cellContainer = GetEventCellOrCurrentCell(e);
            if (cellContainer == null)
            {
                // If there is no cell, then nothing can be determined. So, no edit could end.
                return false;
            }

            EGC.DataGridEditingUnit editingUnit = GetEditingUnit(e.Parameter);
            IEditableCollectionView editableItems = EditableItems;
            object rowItem = cellContainer.RowDataItem;

            // Check that there is an appropriate pending add or edit.
            // - If any cell is in edit mode
            // - OR If the editing unit is row AND one of:
            //   - There is a pending add OR
            //   - There is a pending edit
            return cellContainer.IsEditing ||
                   ((editingUnit == EGC.DataGridEditingUnit.Row) && 
                   !HasCellValidationError && 
                   ((editableItems.IsAddingNew && (editableItems.CurrentAddItem == rowItem)) || 
                   (editableItems.IsEditingItem && (commit || editableItems.CanCancelEdit || HasRowValidationError) && (editableItems.CurrentEditItem == rowItem))));
        }

        /// <summary>
        ///     Invoked to determine if the CommitEdit command can be executed.
        /// </summary>
        protected virtual void OnCanExecuteCommitEdit(CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CanEndEdit(e, /* commit = */ true);
            e.Handled = true;
        }

        /// <summary>
        ///     Invoked when the CommitEdit command is executed.
        /// </summary>
        protected virtual void OnExecutedCommitEdit(ExecutedRoutedEventArgs e)
        {
            EGC.DataGridCell cell = CurrentCellContainer;
            bool validationPassed = true;
            if (cell != null)
            {
                EGC.DataGridEditingUnit editingUnit = GetEditingUnit(e.Parameter);

                bool eventCanceled = false;
                if (cell.IsEditing)
                {
                    EGC.DataGridCellEditEndingEventArgs cellEditEndingEventArgs = new EGC.DataGridCellEditEndingEventArgs(cell.Column, cell.RowOwner, cell.EditingElement, EGC.DataGridEditAction.Commit);
                    OnCellEditEnding(cellEditEndingEventArgs);

                    eventCanceled = cellEditEndingEventArgs.Cancel;
                    if (!eventCanceled)
                    {
                        validationPassed = cell.CommitEdit();
                        HasCellValidationError = !validationPassed;
                    }
                }

                // Consider commiting the row if:
                // 1. Validation passed on the cell or no cell was in edit mode.
                // 2. A cell in edit mode didn't have it's ending edit event canceled.
                // 3. The row can be commited:
                //    a. The row is being edited or added and being targeted directly.
                //    b. If the row is being edited (not added), but it doesn't support
                //       pending changes, then tell the row to commit (this doesn't really
                //       do anything except update the IEditableCollectionView state)
                if (validationPassed && 
                    !eventCanceled &&
                    (((editingUnit == EGC.DataGridEditingUnit.Row) && IsAddingOrEditingRowItem(cell.RowDataItem)) ||
                     (!EditableItems.CanCancelEdit && IsEditingItem(cell.RowDataItem))))
                {
                    EGC.DataGridRowEditEndingEventArgs rowEditEndingEventArgs = new EGC.DataGridRowEditEndingEventArgs(cell.RowOwner, EGC.DataGridEditAction.Commit);
                    OnRowEditEnding(rowEditEndingEventArgs);

                    if (!rowEditEndingEventArgs.Cancel)
                    {                        
                        var bindingGroup = cell.RowOwner.BindingGroup;
                        if (bindingGroup != null)
                        {
                            // CommitEdit will invoke the bindingGroup's ValidationRule's, so we need to make sure that all of the BindingExpressions
                            // have already registered with the BindingGroup.  Synchronously flushing the Dispatcher to DataBind priority lets us ensure this.
                            // Had we used BeginInvoke instead, IsEditing would not reflect the correct value.
                            Dispatcher.Invoke(new DispatcherOperationCallback(DoNothing), DispatcherPriority.DataBind, bindingGroup);
                            validationPassed = bindingGroup.CommitEdit();
                        }

                        HasRowValidationError = !validationPassed;
                        if (validationPassed)
                        {
                            CommitRowItem();
                        }
                    }
                }

                if (validationPassed)
                {
                    // Update the state of row editing
                    UpdateRowEditing(cell);
                }

                // CancelEdit and CommitEdit rely on IsAddingNewItem and IsEditingRowItem
                CommandManager.InvalidateRequerySuggested();
            }

            e.Handled = true;
        }

        /// <summary>
        /// This is a helper method used to flush the dispatcher down to DataBind priority so that the bindingGroup will be ready for CommitEdit.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private static object DoNothing(object arg)
        {
            return null;
        }

        private EGC.DataGridEditingUnit GetEditingUnit(object parameter)
        {
            // If the parameter contains a DataGridEditingUnit, then use it.
            // Otherwise, choose Cell if a cell is currently being edited, or Row if not.
            return ((parameter != null) && (parameter is EGC.DataGridEditingUnit)) ?
                    (EGC.DataGridEditingUnit)parameter :
                    IsEditingCurrentCell ? EGC.DataGridEditingUnit.Cell : EGC.DataGridEditingUnit.Row;
        }

        /// <summary>
        ///     Raised just before row editing is ended.
        ///     Gives handlers the opportunity to cancel the operation.
        /// </summary>
        public event EventHandler<EGC.DataGridRowEditEndingEventArgs> RowEditEnding;

        /// <summary>
        ///     Called just before row editing is ended.
        ///     Gives subclasses the opportunity to cancel the operation.
        /// </summary>
        protected virtual void OnRowEditEnding(EGC.DataGridRowEditEndingEventArgs e)
        {
            if (RowEditEnding != null)
            {
                RowEditEnding(this, e);
            }

            if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked))
            {
                EGC.DataGridAutomationPeer peer = EGC.DataGridAutomationPeer.FromElement(this) as EGC.DataGridAutomationPeer;
                if (peer != null)
                {
                    peer.RaiseAutomationRowInvokeEvents(e.Row);
                }
            }
        }

        /// <summary>
        ///     Raised just before cell editing is ended.
        ///     Gives handlers the opportunity to cancel the operation.
        /// </summary>
        public event EventHandler<EGC.DataGridCellEditEndingEventArgs> CellEditEnding;

        /// <summary>
        ///     Called just before cell editing is ended.
        ///     Gives subclasses the opportunity to cancel the operation.
        /// </summary>
        protected virtual void OnCellEditEnding(EGC.DataGridCellEditEndingEventArgs e)
        {
            if (CellEditEnding != null)
            {
                CellEditEnding(this, e);
            }

            if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked))
            {
                EGC.DataGridAutomationPeer peer = EGC.DataGridAutomationPeer.FromElement(this) as EGC.DataGridAutomationPeer;
                if (peer != null)
                {
                    peer.RaiseAutomationCellInvokeEvents(e.Column, e.Row);
                }
            }
        }

        private static void OnCanExecuteCancelEdit(object sender, CanExecuteRoutedEventArgs e)
        {
            ((EGC.DataGrid)sender).OnCanExecuteCancelEdit(e);
        }

        private static void OnExecutedCancelEdit(object sender, ExecutedRoutedEventArgs e)
        {
            ((EGC.DataGrid)sender).OnExecutedCancelEdit(e);
        }

        /// <summary>
        ///     Invoked to determine if the CancelEdit command can be executed.
        /// </summary>
        protected virtual void OnCanExecuteCancelEdit(CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CanEndEdit(e, /* commit = */ false);
            e.Handled = true;
        }

        /// <summary>
        ///     Invoked when the CancelEdit command is executed.
        /// </summary>
        protected virtual void OnExecutedCancelEdit(ExecutedRoutedEventArgs e)
        {
            EGC.DataGridCell cell = CurrentCellContainer;
            if (cell != null)
            {
                EGC.DataGridEditingUnit editingUnit = GetEditingUnit(e.Parameter);

                bool eventCanceled = false;
                if (cell.IsEditing)
                {
                    EGC.DataGridCellEditEndingEventArgs cellEditEndingEventArgs = new EGC.DataGridCellEditEndingEventArgs(cell.Column, cell.RowOwner, cell.EditingElement, EGC.DataGridEditAction.Cancel);
                    OnCellEditEnding(cellEditEndingEventArgs);

                    eventCanceled = cellEditEndingEventArgs.Cancel;
                    if (!eventCanceled)
                    {
                        cell.CancelEdit();
                        HasCellValidationError = false;
                    }
                }

                var editableItems = EditableItems;
                bool needsCommit = IsEditingItem(cell.RowDataItem) && !editableItems.CanCancelEdit;
                if (!eventCanceled && 
                    (CanCancelAddingOrEditingRowItem(editingUnit, cell.RowDataItem) || needsCommit))
                {
                    bool cancelAllowed = true;

                    if (!needsCommit)
                    {
                        EGC.DataGridRowEditEndingEventArgs rowEditEndingEventArgs = new EGC.DataGridRowEditEndingEventArgs(cell.RowOwner, EGC.DataGridEditAction.Cancel);
                        OnRowEditEnding(rowEditEndingEventArgs);
                        cancelAllowed = !rowEditEndingEventArgs.Cancel;
                    }

                    if (cancelAllowed)
                    {
                        if (needsCommit)
                        {
                            // If the row is being edited (not added), but it doesn't support
                            // pending changes, then tell the item to commit (this doesn't really
                            // do anything except update the IEditableCollectionView state).
                            // This allows us to exit row editing mode.
                            editableItems.CommitEdit();
                        }
                        else
                        {
                            CancelRowItem();
                        }

                        var bindingGroup = cell.RowOwner.BindingGroup;
                        if (bindingGroup != null)
                        {
                            bindingGroup.CancelEdit();

                            // This is to workaround the bug that BindingGroup 
                            // does nor clear errors on CancelEdit
                            bindingGroup.UpdateSources();
                        }
                    }
                }

                // Update the state of row editing
                UpdateRowEditing(cell);

                if (!cell.RowOwner.IsEditing)
                {
                    // Allow the user to cancel the row and avoid being locked to that row.
                    // If the row is still not valid, it means that the source data is already
                    // invalid, and that is OK.
                    HasRowValidationError = false;
                }

                // CancelEdit and CommitEdit rely on IsAddingNewItem and IsEditingRowItem
                CommandManager.InvalidateRequerySuggested();
            }

            e.Handled = true;
        }

        private static void OnCanExecuteDelete(object sender, CanExecuteRoutedEventArgs e)
        {
            ((EGC.DataGrid)sender).OnCanExecuteDelete(e);
        }

        private static void OnExecutedDelete(object sender, ExecutedRoutedEventArgs e)
        {
            ((EGC.DataGrid)sender).OnExecutedDelete(e);
        }

        /// <summary>
        ///     Invoked to determine if the Delete command can be executed.
        /// </summary>
        protected virtual void OnCanExecuteDelete(CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CanUserDeleteRows &&  // User is allowed to delete
                CurrentCell.IsValid &&           // There is a current cell
                (DataItemsSelected > 0) &&       // There is a selection
                ((_currentCellContainer == null) || !_currentCellContainer.IsEditing); // Not editing a cell
            e.Handled = true;
        }

        /// <summary>
        ///     Invoked when the Delete command is executed.
        /// </summary>
        protected virtual void OnExecutedDelete(ExecutedRoutedEventArgs e)
        {
            if (DataItemsSelected > 0)
            {
                bool shouldDelete = false;
                bool isEditingRowItem = IsEditingRowItem;
                if (isEditingRowItem || IsAddingNewItem)
                {
                    // If editing or adding a row, cancel that edit.
                    if (CancelEdit(EGC.DataGridEditingUnit.Row) && isEditingRowItem)
                    {
                        // If adding, we're done. If editing, then an actual delete
                        // needs to happen.
                        shouldDelete = true;
                    }
                }
                else
                {
                    // There is no pending edit, just delete.
                    shouldDelete = true;
                }

                if (shouldDelete)
                {
                    // Normally, the current item will be within the selection,
                    // deteremine a new item to select once the items are removed.
                    int numSelected = SelectedItems.Count;
                    int indexToSelect = -1;
                    object currentItem = CurrentItem;

                    // The current item is in the selection
                    if (SelectedItems.Contains(currentItem)) 
                    {
                        // Choose the smaller index between the anchor and the current item
                        // as the index to select after the items are removed.
                        indexToSelect = Items.IndexOf(currentItem);
                        if (_selectionAnchor != null)
                        {
                            int anchorIndex = Items.IndexOf(_selectionAnchor.Value.Item);
                            if ((anchorIndex >= 0) && (anchorIndex < indexToSelect))
                            {
                                indexToSelect = anchorIndex;
                            }
                        }

                        indexToSelect = Math.Min(Items.Count - numSelected - 1, indexToSelect);
                    }

                    // Save off the selected items. The selected items are going to be cleared
                    // first as a performance optimization. When items are removed, they are checked
                    // against the selected items to be removed from that collection. This can be slow
                    // since each item could cause a linear search of the selected items collection.
                    // Since it is known that all of the selected items are going to be deleted, they
                    // can safely be unselected.
                    ArrayList itemsToRemove = new ArrayList(SelectedItems);

                    using (UpdateSelectedCells())
                    {
                        bool alreadyUpdating = IsUpdatingSelectedItems;
                        if (!alreadyUpdating)
                        {
                            BeginUpdateSelectedItems();
                        }

                        try
                        {
                            // Pre-emptively clear the selection lists
                            _selectedCells.ClearFullRows(SelectedItems);
                            SelectedItems.Clear();
                        }
                        finally
                        {
                            if (!alreadyUpdating)
                            {
                                EndUpdateSelectedItems();
                            }
                        }
                    }

                    // We are not going to defer the rest of the selection change due to existing
                    // Selector behavior. When an item is removed from the ItemsSource, the Selector
                    // will immediately remove it from SelectedItems. In this process, it starts a 
                    // defer, which asserts because this code would have already started a defer.

                    // Remove the items that are selected
                    for (int i = 0; i < numSelected; i++)
                    {
                        object itemToRemove = itemsToRemove[i];
                        if (itemToRemove != CollectionView.NewItemPlaceholder)
                        {
                            EditableItems.Remove(itemToRemove);
                        }
                    }

                    // Select a new item
                    if (indexToSelect >= 0)
                    {
                        object itemToSelect = Items[indexToSelect];

                        // This should focus the row and bring it into view.
                        CurrentItem = itemToSelect;

                        // Since the current cell should be in view, there should be a container
                        EGC.DataGridCell cell = CurrentCellContainer;
                        if (cell != null)
                        {
                            _selectionAnchor = null;
                            HandleSelectionForCellInput(cell, /* startDragging = */ false, /* allowsExtendSelect = */ false, /* allowsMinimalSelect = */ false);
                        }
                    }
                }
            }

            e.Handled = true;
        }

        #endregion

        #region Editing

        /// <summary>
        ///     Whether the DataGrid's rows and cells can be placed in edit mode.
        /// </summary>
        public bool IsReadOnly
        {
            get { return (bool)GetValue(IsReadOnlyProperty); }
            set { SetValue(IsReadOnlyProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for IsReadOnly.
        /// </summary>
        public static readonly DependencyProperty IsReadOnlyProperty =
            DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsReadOnlyChanged)));

        private static void OnIsReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue)
            {
                // When going from R/W to R/O, cancel any current edits
                ((EGC.DataGrid)d).CancelAnyEdit();
            }

            // re-evalutate the BeginEdit command's CanExecute.
            CommandManager.InvalidateRequerySuggested();

            d.CoerceValue(CanUserAddRowsProperty);
            d.CoerceValue(CanUserDeleteRowsProperty);

            // Affects the IsReadOnly property on cells
            OnNotifyColumnAndCellPropertyChanged(d, e);
        }

        /// <summary>
        ///     The object (or row) that, if not in edit mode, can be edited.
        /// </summary>
        /// <remarks>
        ///     This is the data item for the row that either has or contains focus.
        /// </remarks>
        public object CurrentItem
        {
            get { return (object)GetValue(CurrentItemProperty); }
            set { SetValue(CurrentItemProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for CurrentItem.
        /// </summary>
        public static readonly DependencyProperty CurrentItemProperty =
            DependencyProperty.Register("CurrentItem", typeof(object), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnCurrentItemChanged)));

        private static void OnCurrentItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            EGC.DataGrid dataGrid = (EGC.DataGrid)d;
            EGC.DataGridCellInfo currentCell = dataGrid.CurrentCell;
            object newItem = e.NewValue;

            if (currentCell.Item != newItem)
            {
                // Update the CurrentCell structure with the new item
                dataGrid.CurrentCell = EGC.DataGridCellInfo.CreatePossiblyPartialCellInfo(newItem, currentCell.Column, dataGrid);
            }
        }

        /// <summary>
        ///     The column of the CurrentItem (row) that corresponds with the current cell.
        /// </summary>
        /// <remarks>
        ///     null indicates that a cell does not have focus. The row may still have focus.
        /// </remarks>
        public EGC.DataGridColumn CurrentColumn
        {
            get { return (EGC.DataGridColumn)GetValue(CurrentColumnProperty); }
            set { SetValue(CurrentColumnProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for CurrentColumn.
        /// </summary>
        public static readonly DependencyProperty CurrentColumnProperty =
            DependencyProperty.Register("CurrentColumn", typeof(EGC.DataGridColumn), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnCurrentColumnChanged)));

        private static void OnCurrentColumnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            EGC.DataGrid dataGrid = (EGC.DataGrid)d;
            EGC.DataGridCellInfo currentCell = dataGrid.CurrentCell;
            EGC.DataGridColumn newColumn = (EGC.DataGridColumn)e.NewValue;

            if (currentCell.Column != newColumn)
            {
                // Update the CurrentCell structure with the new column
                dataGrid.CurrentCell = EGC.DataGridCellInfo.CreatePossiblyPartialCellInfo(currentCell.Item, newColumn, dataGrid);
            }
        }
        
        /// <summary>
        ///     The cell that, if not in edit mode, can be edited.
        /// </summary>
        /// <remarks>
        ///     The value returned is a structure that provides enough information to describe
        ///     the cell. It is neither an actual reference to the cell container nor the value
        ///     displayed in a given cell.
        /// </remarks>
        public EGC.DataGridCellInfo CurrentCell
        {
            get { return (EGC.DataGridCellInfo)GetValue(CurrentCellProperty); }
            set { SetValue(CurrentCellProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for CurrentCell.
        /// </summary>
        public static readonly DependencyProperty CurrentCellProperty =
            DependencyProperty.Register("CurrentCell", typeof(EGC.DataGridCellInfo), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(EGC.DataGridCellInfo.Unset, new PropertyChangedCallback(OnCurrentCellChanged)));

        private static void OnCurrentCellChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            EGC.DataGrid dataGrid = (EGC.DataGrid)d;
            EGC.DataGridCellInfo oldCell = (EGC.DataGridCellInfo)e.OldValue;
            EGC.DataGridCellInfo currentCell = (EGC.DataGridCellInfo)e.NewValue;

            if (dataGrid.CurrentItem != currentCell.Item)
            {
                dataGrid.CurrentItem = currentCell.Item;
            }

            if (dataGrid.CurrentColumn != currentCell.Column)
            {
                dataGrid.CurrentColumn = currentCell.Column;
            }

            if (dataGrid._currentCellContainer != null)
            {
                // _currentCellContainer should still be the old container and not the new one.
                // If _currentCellContainer were null, then it should mean that no BeginEdit was called
                // so, we shouldn't be missing any EndEdits.
                if ((dataGrid.IsAddingNewItem || dataGrid.IsEditingRowItem) && (oldCell.Item != currentCell.Item))
                {
                    // There is a row edit pending and the current cell changed to another row.
                    // Commit the row, which also commits the cell.
                    dataGrid.EndEdit(CommitEditCommand, dataGrid._currentCellContainer, EGC.DataGridEditingUnit.Row, /* exitEditingMode = */ true);
                }
                else if (dataGrid._currentCellContainer.IsEditing)
                {
                    // Only the cell needs to commit.
                    dataGrid.EndEdit(CommitEditCommand, dataGrid._currentCellContainer, EGC.DataGridEditingUnit.Cell, /* exitEditingMode = */ true);
                }
            }

            dataGrid._currentCellContainer = null;

            if (currentCell.IsValid && dataGrid.IsKeyboardFocusWithin)
            {
                // If CurrentCell was set by the user and not through a focus change,
                // then focus must be updated, but only when the DataGrid already
                // has focus.
                EGC.DataGridCell cell = dataGrid._pendingCurrentCellContainer;
                if (cell == null)
                {
                    cell = dataGrid.CurrentCellContainer;
                    if (cell == null)
                    {
                        // The cell might be virtualized. Try to devirtualize by scrolling.
                        dataGrid.ScrollCellIntoView(currentCell.Item, currentCell.Column);
                        cell = dataGrid.CurrentCellContainer;
                    }
                }

                if ((cell != null) && !cell.IsKeyboardFocusWithin)
                {
                    cell.Focus();
                }
            }

            dataGrid.OnCurrentCellChanged(EventArgs.Empty);
        }

        /// <summary>
        ///     An event to notify that the value of CurrentCell changed.
        /// </summary>
        public event EventHandler<EventArgs> CurrentCellChanged;

        /// <summary>
        ///     Called when the value of CurrentCell changes.
        /// </summary>
        /// <param name="e">Empty event arguments.</param>
        protected virtual void OnCurrentCellChanged(EventArgs e)
        {
            if (CurrentCellChanged != null)
            {
                CurrentCellChanged(this, e);
            }
        }

        private void UpdateCurrentCell(EGC.DataGridCell cell, bool isKeyboardFocusWithin)
        {
            if (isKeyboardFocusWithin)
            {
                // Focus is within the cell, make it the current cell.
                CurrentCellContainer = cell;
            }
            else if (!IsKeyboardFocusWithin)
            {
                // Focus moved outside the DataGrid, so clear out the current cell.
                CurrentCellContainer = null;
            }

            // Focus is within the DataGrid but not within this particular cell.
            // Assume that focus is moving to another cell, and that cell will update
            // the current cell.
        }

        protected EGC.DataGridCell CurrentCellContainer
        {
            get
            {
                if (_currentCellContainer == null)
                {
                    EGC.DataGridCellInfo currentCell = CurrentCell;
                    if (currentCell.IsValid)
                    {
                        _currentCellContainer = TryFindCell(currentCell);
                    }
                }

                return _currentCellContainer;
            }

            set
            {
                if ((_currentCellContainer != value) && 
                    ((value == null) || (value != _pendingCurrentCellContainer)))
                {
                    // Setting CurrentCell might cause some re-entrancy due to focus changes.
                    // We need to detect this without actually changing the value until after
                    // setting CurrentCell.
                    _pendingCurrentCellContainer = value;

                    // _currentCellContainer must remain intact while changing CurrentCell
                    // so that the previous edit can be committed.
                    if (value == null)
                    {
                        ClearValue(CurrentCellProperty);
                    }
                    else
                    {
                        CurrentCell = new EGC.DataGridCellInfo(value);
                    }

                    _pendingCurrentCellContainer = null;
                    _currentCellContainer = value;
                    CommandManager.InvalidateRequerySuggested();
                }
            }
        }

        private bool IsEditingCurrentCell
        {
            get
            {
                EGC.DataGridCell cell = CurrentCellContainer;
                if (cell != null)
                {
                    return cell.IsEditing;
                }

                return false;
            }
        }

        private bool IsCurrentCellReadOnly
        {
            get
            {
                EGC.DataGridCell cell = CurrentCellContainer;
                if (cell != null)
                {
                    return cell.IsReadOnly;
                }

                return false;
            }
        }

        /// <summary>
        ///     Called just before a cell will change to edit mode
        ///     to allow handlers to prevent the cell from entering edit mode.
        /// </summary>
        public event EventHandler<EGC.DataGridBeginningEditEventArgs> BeginningEdit;

        /// <summary>
        ///     Called just before a cell will change to edit mode
        ///     to all subclasses to prevent the cell from entering edit mode.
        /// </summary>
        /// <remarks>
        ///     Default implementation raises the BeginningEdit event.
        /// </remarks>
        protected virtual void OnBeginningEdit(EGC.DataGridBeginningEditEventArgs e)
        {
            if (BeginningEdit != null)
            {
                BeginningEdit(this, e);
            }

            if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked))
            {
                EGC.DataGridAutomationPeer peer = EGC.DataGridAutomationPeer.FromElement(this) as EGC.DataGridAutomationPeer;
                if (peer != null)
                {
                    peer.RaiseAutomationCellInvokeEvents(e.Column, e.Row);
                }
            }
        }

        /// <summary>
        ///     Called after a cell has changed to editing mode to allow
        ///     handlers to modify the contents of the cell.
        /// </summary>
        public event EventHandler<EGC.DataGridPreparingCellForEditEventArgs> PreparingCellForEdit;

        /// <summary>
        ///     Called after a cell has changed to editing mode to allow
        ///     subclasses to modify the contents of the cell.
        /// </summary>
        /// <remarks>
        ///     Default implementation raises the PreparingCellForEdit event.
        ///     This method is invoked from DataGridCell (instead of DataGrid) once it has entered edit mode.
        /// </remarks>
        protected internal virtual void OnPreparingCellForEdit(EGC.DataGridPreparingCellForEditEventArgs e)
        {
            if (PreparingCellForEdit != null)
            {
                PreparingCellForEdit(this, e);
            }
        }

        /// <summary>
        ///     Raises the BeginEdit command, which will place the current cell or row into
        ///     edit mode.
        /// </summary>
        /// <remarks>
        ///     If the command is enabled, this will Strip to the BeginningEdit and PreparingCellForEdit
        ///     overrides and events.
        /// </remarks>
        /// <returns>true if the current cell or row enters edit mode, false otherwise.</returns>
        public bool BeginEdit()
        {
            return BeginEdit(/* editingEventArgs = */ null);
        }

        /// <summary>
        ///     Raises the BeginEdit command, which will place the current cell or row into
        ///     edit mode.
        /// </summary>
        /// <remarks>
        ///     If the command is enabled, this will Strip to the BeginningEdit and PreparingCellForEdit
        ///     overrides and events.
        /// </remarks>
        /// <param name="editingEventArgs">The event arguments, if any, that led to BeginEdit being called. May be null.</param>
        /// <returns>true if the current cell or row enters edit mode, false otherwise.</returns>
        public virtual bool BeginEdit(RoutedEventArgs editingEventArgs)
        {
            if (!IsReadOnly)
            {
                EGC.DataGridCell cellContainer = CurrentCellContainer;
                if (cellContainer != null)
                {
                    if (!cellContainer.IsEditing &&
                        BeginEditCommand.CanExecute(editingEventArgs, cellContainer))
                    {
                        BeginEditCommand.Execute(editingEventArgs, cellContainer);
                    }

                    return cellContainer.IsEditing;
                }
            }

            return false;
        }

        /// <summary>
        ///     Raises the CancelEdit command.
        ///     If a cell is currently in edit mode, cancels the cell edit, but leaves any row edits alone.
        ///     If a cell is not in edit mode, then cancels any pending row edits.
        /// </summary>
        /// <returns>true if the current cell or row exits edit mode, false otherwise.</returns>
        public bool CancelEdit()
        {
            if (IsEditingCurrentCell)
            {
                return CancelEdit(EGC.DataGridEditingUnit.Cell);
            }
            else if (IsEditingRowItem || IsAddingNewItem)
            {
                return CancelEdit(EGC.DataGridEditingUnit.Row);
            }

            return true; // No one is in edit mode
        }

        /// <summary>
        ///     Raises the CancelEdit command.
        ///     If a cell is currently in edit mode, cancels the cell edit, but leaves any row edits alone.
        /// </summary>
        /// <returns>true if the cell exits edit mode, false otherwise.</returns>
        internal bool CancelEdit(EGC.DataGridCell cell)
        {
            EGC.DataGridCell currentCell = CurrentCellContainer;
            if (currentCell != null && currentCell == cell && currentCell.IsEditing)
            {
                return CancelEdit(EGC.DataGridEditingUnit.Cell);
            }

            return true;
        }

        /// <summary>
        ///     Raises the CancelEdit command.
        ///     Reverts any pending editing changes to the desired editing unit and exits edit mode.
        /// </summary>
        /// <param name="editingUnit">Whether to cancel edit mode of the current cell or current row.</param>
        /// <returns>true if the current cell or row exits edit mode, false otherwise.</returns>
        public bool CancelEdit(EGC.DataGridEditingUnit editingUnit)
        {
            return EndEdit(CancelEditCommand, CurrentCellContainer, editingUnit, true);
        }

        private void CancelAnyEdit()
        {
            if (IsAddingNewItem || IsEditingRowItem)
            {
                // There is a row edit in progress, cancel it, which will also cancel the cell edit.
                CancelEdit(EGC.DataGridEditingUnit.Row);
            }
            else if (IsEditingCurrentCell)
            {
                // Cancel the current cell edit.
                CancelEdit(EGC.DataGridEditingUnit.Cell);
            }
        }

        /// <summary>
        ///     Raises the CommitEdit command.
        ///     If a cell is currently being edited, commits any pending changes to the cell, but
        ///     leaves any pending changes to the row. This should mean that changes are propagated
        ///     from the editing environment to the pending row.
        ///     If a cell is not currently being edited, then commits any pending rows.
        /// </summary>
        /// <returns>true if the current cell or row exits edit mode, false otherwise.</returns>
        public bool CommitEdit()
        {
            if (IsEditingCurrentCell)
            {
                return CommitEdit(EGC.DataGridEditingUnit.Cell, true);
            }
            else if (IsEditingRowItem || IsAddingNewItem)
            {
                return CommitEdit(EGC.DataGridEditingUnit.Row, true);
            }

            return true; // No one is in edit mode
        }

        /// <summary>
        ///     Raises the CommitEdit command.
        ///     Commits any pending changes for the given editing unit and exits edit mode.
        /// </summary>
        /// <param name="editingUnit">Whether to commit changes for the current cell or current row.</param>
        /// <param name="exitEditingMode">Whether to exit edit mode.</param>
        /// <returns>true if the current cell or row exits edit mode, false otherwise.</returns>
        public bool CommitEdit(EGC.DataGridEditingUnit editingUnit, bool exitEditingMode)
        {
            return EndEdit(CommitEditCommand, CurrentCellContainer, editingUnit, exitEditingMode);
        }

        private bool CommitAnyEdit()
        {
            if (IsAddingNewItem || IsEditingRowItem)
            {
                // There is a row edit in progress, commit it, which will also commit the cell edit.
                return CommitEdit(EGC.DataGridEditingUnit.Row, /* exitEditingMode = */ true);
            }
            else if (IsEditingCurrentCell)
            {
                // Commit the current cell edit.
                return CommitEdit(EGC.DataGridEditingUnit.Cell, /* exitEditingMode = */ true);
            }

            return true;
        }

        private bool EndEdit(RoutedCommand command, EGC.DataGridCell cellContainer, EGC.DataGridEditingUnit editingUnit, bool exitEditMode)
        {
            bool cellLeftEditingMode = true;
            bool rowLeftEditingMode = true;

            if (cellContainer != null)
            {
                if (command.CanExecute(editingUnit, cellContainer))
                {
                    command.Execute(editingUnit, cellContainer);
                }

                cellLeftEditingMode = !cellContainer.IsEditing;
                rowLeftEditingMode = !IsEditingRowItem && !IsAddingNewItem;
            }

            if (!exitEditMode)
            {
                if (editingUnit == EGC.DataGridEditingUnit.Cell)
                {
                    if (cellContainer != null)
                    {
                        if (cellLeftEditingMode)
                        {
                            return BeginEdit(null);
                        }
                    }
                    else
                    {
                        // A cell was not placed in edit mode
                        return false;
                    }
                }
                else
                {
                    if (rowLeftEditingMode)
                    {
                        object rowItem = cellContainer.RowDataItem;
                        if (rowItem != null)
                        {
                            EditRowItem(rowItem);
                            return IsEditingRowItem;
                        }
                    }

                    // A row item was not placed in edit mode
                    return false;
                }
            }

            return cellLeftEditingMode && ((editingUnit == EGC.DataGridEditingUnit.Cell) || rowLeftEditingMode);
        }

        private bool HasCellValidationError
        {
            get
            {
                return _hasCellValidationError;
            }

            set
            {
                if (_hasCellValidationError != value)
                {
                    _hasCellValidationError = value;

                    // BeginEdit's CanExecute status relies on this flag
                    CommandManager.InvalidateRequerySuggested();
                }
            }
        }

        private bool HasRowValidationError
        {
            get
            {
                return _hasRowValidationError;
            }

            set
            {
                if (_hasRowValidationError != value)
                {
                    _hasRowValidationError = value;

                    // BeginEdit's CanExecute status relies on this flag
                    CommandManager.InvalidateRequerySuggested();
                }
            }
        }

        #endregion

        #region Row Editing

        /// <summary>
        ///     Whether the end-user can add new rows to the ItemsSource.
        /// </summary>
        public bool CanUserAddRows
        {
            get { return (bool)GetValue(CanUserAddRowsProperty); }
            set { SetValue(CanUserAddRowsProperty, value); }
        }

        /// <summary>
        ///     DependencyProperty for CanUserAddRows.
        /// </summary>
        public static readonly DependencyProperty CanUserAddRowsProperty =
            DependencyProperty.Register("CanUserAddRows", typeof(bool), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnCanUserAddRowsChanged), new CoerceValueCallback(OnCoerceCanUserAddRows)));

        private static void OnCanUserAddRowsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).UpdateNewItemPlaceholder(/* isAddingNewItem = */ false);
        }

        private static object OnCoerceCanUserAddRows(DependencyObject d, object baseValue)
        {
            return OnCoerceCanUserAddOrDeleteRows((EGC.DataGrid)d, (bool)baseValue, /* canUserAddRowsProperty = */ true);
        }

        private static bool OnCoerceCanUserAddOrDeleteRows(EGC.DataGrid dataGrid, bool baseValue, bool canUserAddRowsProperty)
        {
            // Only when the base value is true do we need to validate that the user
            // can actually add or delete rows.
            if (baseValue)
            {
                if (dataGrid.IsReadOnly || !dataGrid.IsEnabled)
                {
                    // Read-only/disabled DataGrids cannot be modified.
                    return false;
                }
                else
                {
                    if ((canUserAddRowsProperty && !dataGrid.EditableItems.CanAddNew) ||
                        (!canUserAddRowsProperty && !dataGrid.EditableItems.CanRemove))
                    {
                        // The collection view does not allow the add or delete action
                        return false;
                    }
                }
            }

            return baseValue;
        }

        /// <summary>
        ///     Whether the end-user can delete rows from the ItemsSource.
        /// </summary>
        public bool CanUserDeleteRows
        {
            get { return (bool)GetValue(CanUserDeleteRowsProperty); }
            set { SetValue(CanUserDeleteRowsProperty, value); }
        }

        /// <summary>
        ///     DependencyProperty for CanUserDeleteRows.
        /// </summary>
        public static readonly DependencyProperty CanUserDeleteRowsProperty =
            DependencyProperty.Register("CanUserDeleteRows", typeof(bool), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnCanUserDeleteRowsChanged), new CoerceValueCallback(OnCoerceCanUserDeleteRows)));

        private static void OnCanUserDeleteRowsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // The Delete command needs to have CanExecute run
            CommandManager.InvalidateRequerySuggested();
        }

        private static object OnCoerceCanUserDeleteRows(DependencyObject d, object baseValue)
        {
            return OnCoerceCanUserAddOrDeleteRows((EGC.DataGrid)d, (bool)baseValue, /* canUserAddRowsProperty = */ false);
        }

        /// <summary>
        ///     An event that is raised when a new item is created so that
        ///     developers can initialize the item with custom default values.
        /// </summary>
        public event EGC.InitializingNewItemEventHandler InitializingNewItem;
        
        /// <summary>
        ///     A method that is called when a new item is created so that
        ///     overrides can initialize the item with custom default values.
        /// </summary>
        /// <remarks>
        ///     The default implementation raises the InitializingNewItem event.
        /// </remarks>
        /// <param name="e">Event arguments that provide access to the new item.</param>
        protected virtual void OnInitializingNewItem(EGC.InitializingNewItemEventArgs e)
        {
            if (InitializingNewItem != null)
            {
                InitializingNewItem(this, e);
            }
        }

        private object AddNewItem()
        {
            Debug.Assert(CanUserAddRows, "AddNewItem called when the end-user cannot add new rows.");
            Debug.Assert(!IsAddingNewItem, "AddNewItem called when a pending add is taking place.");

            // Hide the placeholder
            UpdateNewItemPlaceholder(/* isAddingNewItem = */ true);

            object newItem = EditableItems.AddNew();
            if (newItem != null)
            {
                EGC.InitializingNewItemEventArgs e = new EGC.InitializingNewItemEventArgs(newItem);
                OnInitializingNewItem(e);
            }

            // CancelEdit and CommitEdit rely on IsAddingNewItem
            CommandManager.InvalidateRequerySuggested();

            return newItem;
        }

        private void EditRowItem(object rowItem)
        {
            EditableItems.EditItem(rowItem);

            // CancelEdit and CommitEdit rely on IsEditingRowItem
            CommandManager.InvalidateRequerySuggested();
        }

        private void CommitRowItem()
        {
            // This case illustrates a minor side-effect of communicating with IEditableObject through two different means
            // - BindingGroup
            // - IEditableCollectionView
            // The sequence of operations is as below.
            // IEditableCollectionView.BeginEdit
            // BindingGroup.BeginEdit
            // BindingGroup.CommitEdit
            // IEditableCollectionView.CommitEdit
            // After having commited the NewItem row the first time it is possible that the IsAddingNewItem is false 
            // during the second call to CommitEdit. Hence we cannot quite make this assertion here.

            // Debug.Assert(IsEditingRowItem || IsAddingNewItem, "CommitRowItem was called when a row was not being edited or added.");
            if (IsEditingRowItem)
            {
                EditableItems.CommitEdit();
            }
            else
            {
                EditableItems.CommitNew();

                // Show the placeholder again
                UpdateNewItemPlaceholder(/* isAddingNewItem = */ false);
            }
        }

        private void CancelRowItem()
        {
            // This case illustrates a minor side-effect of communicating with IEditableObject through two different means
            // - BindingGroup
            // - IEditableCollectionView
            // The sequence of operations is as below.
            // IEditableCollectionView.BeginEdit
            // BindingGroup.BeginEdit
            // IEditableCollectionView.CancelEdit
            // BindingGroup.CancelEdit
            // After having cancelled the NewItem row the first time it is possible that the IsAddingNewItem is false 
            // during the second call to CancelEdit. Hence we cannot quite make this assertion here.

            // Debug.Assert(IsEditingRowItem || IsAddingNewItem, "CancelRowItem was called when a row was not being edited or added.");
            if (IsEditingRowItem)
            {
                EditableItems.CancelEdit();
            }
            else
            {
                object currentAddItem = EditableItems.CurrentAddItem;
                bool wasCurrent = currentAddItem == CurrentItem;
                bool wasSelected = SelectedItems.Contains(currentAddItem);
                bool reselectPlaceholderCells = false;
                List<int> columnIndexRanges = null;
                int newItemIndex = -1;

                if (wasSelected)
                {
                    // Unselect the item that was being added
                    UnselectItem(currentAddItem);
                }
                else
                {
                    // Cells will automatically unselect when the new item is removed, but we
                    // should reselect them on the placeholder.
                    newItemIndex = Items.IndexOf(currentAddItem);
                    reselectPlaceholderCells = ((newItemIndex >= 0) && _selectedCells.Intersects(newItemIndex, out columnIndexRanges));
                }

                // Cancel the add and remove it from the collection
                EditableItems.CancelNew();

                // Show the placeholder again
                UpdateNewItemPlaceholder(/* isAddingNewItem = */ false);

                if (wasCurrent)
                {
                    // Focus the placeholder if the new item had focus
                    CurrentItem = CollectionView.NewItemPlaceholder;
                }

                if (wasSelected)
                {
                    // Re-select the placeholder if it was selected before
                    SelectItem(CollectionView.NewItemPlaceholder);
                }
                else if (reselectPlaceholderCells)
                {
                    // Re-select placeholder cells if they were selected before
                    using (UpdateSelectedCells())
                    {
                        int rowIndex = newItemIndex;
                        bool placeholderAtBeginning = (EditableItems.NewItemPlaceholderPosition == NewItemPlaceholderPosition.AtBeginning);

                        // When the placeholder is at the beginning, we need to unselect the cells 
                        // in the added row and move those back to the previous row.
                        if (placeholderAtBeginning)
                        {
                            _selectedCells.RemoveRegion(newItemIndex, 0, 1, Columns.Count);
                            rowIndex--;
                        }

                        for (int i = 0, count = columnIndexRanges.Count; i < count; i += 2)
                        {
                            _selectedCells.AddRegion(rowIndex, columnIndexRanges[i], 1, columnIndexRanges[i + 1]);
                        }
                    }
                }
            }
        }

        private void UpdateRowEditing(EGC.DataGridCell cell)
        {
            object rowDataItem = cell.RowDataItem;

            // If the row is not in edit/add mode, then clear its IsEditing flag.
            if (!IsAddingOrEditingRowItem(rowDataItem))
            {
                cell.RowOwner.IsEditing = false;
                _editingRowItem = null;
                _editingRowIndex = -1;
            }
        }

        private IEditableCollectionView EditableItems
        {
            get { return (IEditableCollectionView)Items; }
        }

        private bool IsAddingNewItem
        {
            get { return EditableItems.IsAddingNew; }
        }

        private bool IsEditingRowItem
        {
            get { return EditableItems.IsEditingItem; }
        }

        private bool IsAddingOrEditingRowItem(object item)
        {
            return IsEditingItem(item) ||
                (IsAddingNewItem && (EditableItems.CurrentAddItem == item));
        }

        private bool CanCancelAddingOrEditingRowItem(EGC.DataGridEditingUnit editingUnit, object item)
        {
            return (editingUnit == EGC.DataGridEditingUnit.Row) &&
                   ((IsEditingItem(item) && EditableItems.CanCancelEdit) ||
                   (IsAddingNewItem && (EditableItems.CurrentAddItem == item)));
        }

        private bool IsEditingItem(object item)
        {
            return IsEditingRowItem && (EditableItems.CurrentEditItem == item);
        }

        private void UpdateNewItemPlaceholder(bool isAddingNewItem)
        {
            var editableItems = EditableItems;
            bool canUserAddRows = CanUserAddRows;

            if (EGC.DataGridHelper.IsDefaultValue(this, CanUserAddRowsProperty))
            {
                canUserAddRows = OnCoerceCanUserAddOrDeleteRows(this, canUserAddRows, true);
            }

            if (!isAddingNewItem)
            {
                if (canUserAddRows)
                {
                    // NewItemPlaceholderPosition isn't a DP but we want to default to AtEnd instead of None (can only be done
                    // when canUserAddRows becomes true).  This may override the users intent to make it None, however
                    // they can work around this by resetting it to None after making a change which results in canUserAddRows
                    // becoming true.
                    if (editableItems.NewItemPlaceholderPosition == NewItemPlaceholderPosition.None)
                    {
                        editableItems.NewItemPlaceholderPosition = NewItemPlaceholderPosition.AtEnd;
                    }

                    _placeholderVisibility = Visibility.Visible;
                }
                else
                {
                    if (editableItems.NewItemPlaceholderPosition != NewItemPlaceholderPosition.None)
                    {
                        editableItems.NewItemPlaceholderPosition = NewItemPlaceholderPosition.None;
                    }

                    _placeholderVisibility = Visibility.Collapsed;
                }
            }
            else
            {
                // During a row add, hide the placeholder
                _placeholderVisibility = Visibility.Collapsed;
            }

            // Make sure the newItemPlaceholderRow reflects the correct visiblity
            EGC.DataGridRow newItemPlaceholderRow = (EGC.DataGridRow)ItemContainerGenerator.ContainerFromItem(CollectionView.NewItemPlaceholder);
            if (newItemPlaceholderRow != null)
            {
                newItemPlaceholderRow.CoerceValue(VisibilityProperty);
            }
        }

        private void SetCurrentItemToPlaceholder()
        {
            NewItemPlaceholderPosition position = EditableItems.NewItemPlaceholderPosition;
            if (position == NewItemPlaceholderPosition.AtEnd)
            {
                int itemCount = Items.Count;
                if (itemCount > 0)
                {
                    CurrentItem = Items[itemCount - 1];
                }
            }
            else if (position == NewItemPlaceholderPosition.AtBeginning)
            {
                if (Items.Count > 0)
                {
                    CurrentItem = Items[0];
                }
            }
        }

        private int DataItemsCount
        {
            get
            {
                int itemsCount = Items.Count;
                
                // Subtract one if there is a new item placeholder
                if (HasNewItemPlaceholder)
                {
                    itemsCount--;
                }

                return itemsCount;
            }
        }

        private int DataItemsSelected
        {
            get
            {
                int itemsSelected = SelectedItems.Count;

                if (HasNewItemPlaceholder && SelectedItems.Contains(CollectionView.NewItemPlaceholder))
                {
                    itemsSelected--;
                }

                return itemsSelected;
            }
        }

        private bool HasNewItemPlaceholder
        {
            get
            {
                IEditableCollectionView editableItems = EditableItems;
                return editableItems.NewItemPlaceholderPosition != NewItemPlaceholderPosition.None;
            }
        }

        private bool IsNewItemPlaceholder(object item)
        {
            return (item == CollectionView.NewItemPlaceholder) || (item == EGC.DataGrid.NewItemPlaceholder);
        }

        #endregion

        #region Row Details

        /// <summary>
        ///     Determines which visibility mode the Row's details use.
        /// </summary>
        public EGC.DataGridRowDetailsVisibilityMode RowDetailsVisibilityMode 
        {
            get { return (EGC.DataGridRowDetailsVisibilityMode)GetValue(RowDetailsVisibilityModeProperty); }
            set { SetValue(RowDetailsVisibilityModeProperty, value); }
        }

        /// <summary>
        ///     DependencyProperty for RowDetailsVisibilityMode.
        /// </summary>
        public static readonly DependencyProperty RowDetailsVisibilityModeProperty =
            DependencyProperty.Register("RowDetailsVisibilityMode", typeof(EGC.DataGridRowDetailsVisibilityMode), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(EGC.DataGridRowDetailsVisibilityMode.VisibleWhenSelected, OnNotifyRowAndDetailsPropertyChanged));

        /// <summary>
        ///     Controls if the row details scroll.
        /// </summary>
        public bool AreRowDetailsFrozen
        {
            get { return (bool)GetValue(AreRowDetailsFrozenProperty); }
            set { SetValue(AreRowDetailsFrozenProperty, value); }
        }

        /// <summary>
        ///     DependencyProperty for AreRowDetailsFrozen.
        /// </summary>
        public static readonly DependencyProperty AreRowDetailsFrozenProperty =
            DependencyProperty.Register("AreRowDetailsFrozen", typeof(bool), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(false));

        /// <summary>
        ///     Template used for the Row details.
        /// </summary>
        public DataTemplate RowDetailsTemplate
        {
            get { return (DataTemplate)GetValue(RowDetailsTemplateProperty); }
            set { SetValue(RowDetailsTemplateProperty, value); }
        }

        /// <summary>
        ///     DependencyProperty for RowDetailsTemplate.
        /// </summary>
        public static readonly DependencyProperty RowDetailsTemplateProperty =
            DependencyProperty.Register("RowDetailsTemplate", typeof(DataTemplate), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, OnNotifyRowAndDetailsPropertyChanged));

        /// <summary>
        ///     TemplateSelector used for the Row details
        /// </summary>
        public DataTemplateSelector RowDetailsTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(RowDetailsTemplateSelectorProperty); }
            set { SetValue(RowDetailsTemplateSelectorProperty, value); }
        }

        /// <summary>
        ///     DependencyProperty for RowDetailsTemplateSelector.
        /// </summary>
        public static readonly DependencyProperty RowDetailsTemplateSelectorProperty =
            DependencyProperty.Register("RowDetailsTemplateSelector", typeof(DataTemplateSelector), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, OnNotifyRowAndDetailsPropertyChanged));

        /// <summary>
        ///     Event that is fired just before the details of a Row is shown
        /// </summary>
        public event EventHandler<EGC.DataGridRowDetailsEventArgs> LoadingRowDetails;

        /// <summary>
        ///     Event that is fired just before the details of a Row is hidden
        /// </summary>
        public event EventHandler<EGC.DataGridRowDetailsEventArgs> UnloadingRowDetails;

        /// <summary>
        ///     Event that is fired when the visibility of a Rows details changes.
        /// </summary>
        public event EventHandler<EGC.DataGridRowDetailsEventArgs> RowDetailsVisibilityChanged;

        /// <summary>
        ///     Invokes the LoadingRowDetails event
        /// </summary>
        protected virtual void OnLoadingRowDetails(EGC.DataGridRowDetailsEventArgs e)
        {
            e.Row.DetailsEventStatus = EGC.DataGridRow.RowDetailsEventStatus.Loaded;
            if (LoadingRowDetails != null)
            {
                LoadingRowDetails(this, e);
            }
        }

        /// <summary>
        ///     Invokes the UnloadingRowDetails event
        /// </summary>
        protected virtual void OnUnloadingRowDetails(EGC.DataGridRowDetailsEventArgs e)
        {
            if (UnloadingRowDetails != null)
            {
                UnloadingRowDetails(this, e);
            }
        }

        /// <summary>
        ///     Invokes the RowDetailsVisibilityChanged event
        /// </summary>
        protected internal virtual void OnRowDetailsVisibilityChanged(EGC.DataGridRowDetailsEventArgs e)
        {
            if (RowDetailsVisibilityChanged != null)
            {
                RowDetailsVisibilityChanged(this, e);
            }

            var row = e.Row;

            // Don't fire LoadingRowDetails it's disabled or already loaded.
            if (row.DetailsEventStatus == EGC.DataGridRow.RowDetailsEventStatus.Pending && row.DetailsVisibility == Visibility.Visible && row.DetailsPresenter != null)
            {
                // No need to used DelayedOnLoadingRowDetails because OnRowDetailsVisibilityChanged isn't called until after the
                // template is expanded.
                OnLoadingRowDetails(new EGC.DataGridRowDetailsEventArgs(row, row.DetailsPresenter.DetailsElement));
            }
        }

        #endregion

        #region Row Resizing

        /// <summary>
        ///     A property that specifies whether the user can resize rows in the UI by dragging the row headers.
        /// </summary>
        public bool CanUserResizeRows
        {
            get { return (bool)GetValue(CanUserResizeRowsProperty); }
            set { SetValue(CanUserResizeRowsProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty that represents the CanUserResizeColumns property.
        /// </summary>
        public static readonly DependencyProperty CanUserResizeRowsProperty =
            DependencyProperty.Register("CanUserResizeRows", typeof(bool), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnNotifyRowHeaderPropertyChanged)));

        #endregion

        #region Selection

        /// <summary>
        ///     The currently selected cells.
        /// </summary>
        public IList<EGC.DataGridCellInfo> SelectedCells
        {
            get { return _selectedCells; }
        }

        internal EGC.SelectedCellsCollection SelectedCellsInternal
        {
            get { return _selectedCells; }
        }

        /// <summary>
        ///     Event that fires when the SelectedCells collection changes.
        /// </summary>
        public event SelectedCellsChangedEventHandler SelectedCellsChanged;

        /// <summary>
        ///     Direct notification from the SelectedCells collection of a change.
        /// </summary>
        internal void OnSelectedCellsChanged(NotifyCollectionChangedAction action, EGC.VirtualizedCellInfoCollection oldItems, EGC.VirtualizedCellInfoCollection newItems)
        {
            EGC.DataGridSelectionMode selectionMode = SelectionMode;
            EGC.DataGridSelectionUnit selectionUnit = SelectionUnit;
            if (!IsUpdatingSelectedCells && (selectionUnit == EGC.DataGridSelectionUnit.FullRow))
            {
                throw new InvalidOperationException(SR.Get(SRID.DataGrid_CannotSelectCell));
            }

            // Update the pending list of changes
            if (oldItems != null)
            {
                // When IsUpdatingSelectedCells is true, there may have been cells
                // added to _pendingSelectedCells that are now being removed.
                // These cells should be removed from _pendingSelectedCells and
                // not added to _pendingUnselectedCells.
                if (_pendingSelectedCells != null)
                {
                    EGC.VirtualizedCellInfoCollection.Xor(_pendingSelectedCells, oldItems);
                }

                if (_pendingUnselectedCells == null)
                {
                    _pendingUnselectedCells = oldItems;
                }
                else
                {
                    _pendingUnselectedCells.Union(oldItems);
                }
            }

            if (newItems != null)
            {
                // When IsUpdatingSelectedCells is true, there may have been cells
                // added to _pendingUnselectedCells that are now being removed.
                // These cells should be removed from _pendingUnselectedCells and
                // not added to _pendingSelectedCells.
                if (_pendingUnselectedCells != null)
                {
                    EGC.VirtualizedCellInfoCollection.Xor(_pendingUnselectedCells, newItems);
                }

                if (_pendingSelectedCells == null)
                {
                    _pendingSelectedCells = newItems;
                }
                else
                {
                    _pendingSelectedCells.Union(newItems);
                }
            }

            // Not deferring change notifications
            if (!IsUpdatingSelectedCells) 
            {
                // This is most likely the case when SelectedCells was updated by
                // the application. In this case, some fix-up is required, and
                // the public event needs to fire.

                // This will fire the event on dispose
                using (UpdateSelectedCells()) 
                {
                    if ((selectionMode == EGC.DataGridSelectionMode.Single) && // Single select mode
                        (action == NotifyCollectionChangedAction.Add) && // An item was added
                        (_selectedCells.Count > 1)) // There is more than one selected cell 
                    {
                        // When in single selection mode and there is more than one selected
                        // cell, remove all cells but the new cell.
                        _selectedCells.RemoveAllButOne(newItems[0]);
                    }
                    else if ((action == NotifyCollectionChangedAction.Remove) && 
                             (oldItems != null) &&
                             (selectionUnit == EGC.DataGridSelectionUnit.CellOrRowHeader))
                    {
                        // If removed cells belong to rows that are selected, then the row
                        // needs to be unselected (other selected cells may remain selected).
                        bool alreadyUpdating = IsUpdatingSelectedItems;
                        if (!alreadyUpdating)
                        {
                            BeginUpdateSelectedItems();
                        }

                        try
                        {
                            object lastRowItem = null;
                            foreach (EGC.DataGridCellInfo cellInfo in oldItems)
                            {
                                // First ensure that we haven't already checked the item
                                object rowItem = cellInfo.Item;
                                if (rowItem != lastRowItem)
                                {
                                    lastRowItem = rowItem;

                                    if (SelectedItems.Contains(rowItem))
                                    {
                                        // Remove the item
                                        SelectedItems.Remove(rowItem);
                                    }
                                }
                            }
                        }
                        finally
                        {
                            if (!alreadyUpdating)
                            {
                                EndUpdateSelectedItems();
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        ///     Fires the public change event when there are pending cell changes.
        /// </summary>
        private void NotifySelectedCellsChanged()
        {
            if (((_pendingSelectedCells != null) && (_pendingSelectedCells.Count > 0)) || 
                ((_pendingUnselectedCells != null) && (_pendingUnselectedCells.Count > 0)))
            {
                // Create the new event args
                EGC.SelectedCellsChangedEventArgs e = new EGC.SelectedCellsChangedEventArgs(this, _pendingSelectedCells, _pendingUnselectedCells);

                // Calculate the previous and current selection counts to determine if commands need invalidating
                int currentSelectionCount = _selectedCells.Count;
                int unselectedCellCount = (_pendingUnselectedCells != null) ? _pendingUnselectedCells.Count : 0;
                int selectedCellCount = (_pendingSelectedCells != null) ? _pendingSelectedCells.Count : 0;
                int previousSelectionCount = currentSelectionCount - selectedCellCount + unselectedCellCount;

                // Clear the pending lists
                _pendingSelectedCells = null;
                _pendingUnselectedCells = null;

                // Fire the public event
                OnSelectedCellsChanged(e);

                // If old or new selection is empty - invalidate Copy command
                if ((previousSelectionCount == 0) || (currentSelectionCount == 0))
                {
                    // The Copy command needs to have CanExecute run
                    CommandManager.InvalidateRequerySuggested();
                }
            }
        }

        /// <summary>
        ///     Called when there are changes to the SelectedCells collection.
        /// </summary>
        /// <param name="e">Event arguments that indicate which cells were added or removed.</param>
        /// <remarks>
        ///     Base implementation fires the public SelectedCellsChanged event.
        /// </remarks>
        protected virtual void OnSelectedCellsChanged(EGC.SelectedCellsChangedEventArgs e)
        {
            if (SelectedCellsChanged != null)
            {
                SelectedCellsChanged(this, e);
            }

            // Raise automation events
            if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected) ||
                AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementAddedToSelection) ||
                AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection))
            {
                EGC.DataGridAutomationPeer peer = EGC.DataGridAutomationPeer.FromElement(this) as EGC.DataGridAutomationPeer;
                if (peer != null)
                {
                    peer.RaiseAutomationCellSelectedEvent(e);
                }
            }
        }

        /// <summary>
        ///     A command that, when invoked, will select all items in the DataGrid.
        /// </summary>
        public static readonly RoutedCommand SelectAllCommand = new RoutedCommand(SR.Get(SRID.DataGrid_SelectAllCommandText), typeof(EGC.DataGrid));

        private static void OnCanExecuteSelectAll(object sender, CanExecuteRoutedEventArgs e)
        {
            EGC.DataGrid dataGrid = (EGC.DataGrid)sender;
            e.CanExecute = (dataGrid.SelectionMode == EGC.DataGridSelectionMode.Extended) && dataGrid.IsEnabled;
            e.Handled = true;
        }

        private static void OnExecutedSelectAll(object sender, ExecutedRoutedEventArgs e)
        {
            EGC.DataGrid dataGrid = (EGC.DataGrid)sender;
            if (dataGrid.SelectionUnit == EGC.DataGridSelectionUnit.Cell)
            {
                dataGrid.SelectAllCells();
            }
            else
            {
                dataGrid.SelectAllRows();
            }

            e.Handled = true;
        }

        private void SelectAllRows()
        {
            int numItems = Items.Count;
            int numColumns = _columns.Count;
            if ((numColumns > 0) && (numItems > 0))
            {
                using (UpdateSelectedCells())
                {
                    // Selecting the cells first is an optimization, which doesn't happen in a direct call to SelectAll.
                    _selectedCells.AddRegion(0, 0, numItems, numColumns);
                    SelectAll();
                }
            }
        }

        internal void SelectOnlyThisCell(EGC.DataGridCellInfo currentCellInfo)
        {
            using (UpdateSelectedCells())
            {
                _selectedCells.Clear();
                _selectedCells.Add(currentCellInfo);
            }
        }

        /// <summary>
        ///     Selects all cells.
        /// </summary>
        public void SelectAllCells()
        {
            if (SelectionUnit == EGC.DataGridSelectionUnit.FullRow)
            {
                SelectAllRows();
            }
            else
            {
                int numItems = Items.Count;
                int numColumns = _columns.Count;

                if ((numItems > 0) && (numColumns > 0))
                {
                    using (UpdateSelectedCells())
                    {
                        if (_selectedCells.Count > 0)
                        {
                            _selectedCells.Clear();
                        }

                        _selectedCells.AddRegion(0, 0, numItems, numColumns);
                    }
                }
            }
        }

        /// <summary>
        ///     Unselects all cells.
        /// </summary>
        public void UnselectAllCells()
        {
            EGC.DataGridSelectionUnit selectionUnit = SelectionUnit;

            using (UpdateSelectedCells())
            {
                if (selectionUnit != EGC.DataGridSelectionUnit.FullRow)
                {
                    // Unselect all of the cells
                    _selectedCells.Clear();
                }

                if (selectionUnit != EGC.DataGridSelectionUnit.Cell)
                {
                    // Unselect all the items
                    UnselectAll();
                }
            }
        }

        /// <summary>
        ///     Defines the selection behavior.
        /// </summary>
        /// <remarks>
        ///     The SelectionMode and the SelectionUnit properties together define
        ///     the selection behavior for the DataGrid.
        /// </remarks>
        public EGC.DataGridSelectionMode SelectionMode
        {
            get { return (EGC.DataGridSelectionMode)GetValue(SelectionModeProperty); }
            set { SetValue(SelectionModeProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for the SelectionMode property.
        /// </summary>
        public static readonly DependencyProperty SelectionModeProperty =
            DependencyProperty.Register("SelectionMode", typeof(EGC.DataGridSelectionMode), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(EGC.DataGridSelectionMode.Extended, new PropertyChangedCallback(OnSelectionModeChanged)));

        private static void OnSelectionModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            EGC.DataGrid dataGrid = (EGC.DataGrid)d;
            EGC.DataGridSelectionMode newSelectionMode = (EGC.DataGridSelectionMode)e.NewValue;
            bool changingToSingleMode = newSelectionMode == EGC.DataGridSelectionMode.Single;
            EGC.DataGridSelectionUnit selectionUnit = dataGrid.SelectionUnit;

            if (changingToSingleMode && (selectionUnit == EGC.DataGridSelectionUnit.Cell))
            {
                // Setting CanSelectMultipleItems affects SelectedItems, but DataGrid
                // needs to modify SelectedCells manually.
                using (dataGrid.UpdateSelectedCells())
                {
                    dataGrid._selectedCells.RemoveAllButOne();
                }
            }

            // Update whether multiple items can be selected. Setting this property
            // will remove items when going from multiple to single mode.
            dataGrid.CanSelectMultipleItems = (newSelectionMode != EGC.DataGridSelectionMode.Single);

            if (changingToSingleMode && (selectionUnit == EGC.DataGridSelectionUnit.CellOrRowHeader))
            {
                // In CellOrRowHeader, wait until after CanSelectMultipleItems is done removing items.
                if (dataGrid.SelectedItems.Count > 0)
                {
                    // If there is a selected item, then de-select all cells except for that one row.
                    using (dataGrid.UpdateSelectedCells())
                    {
                        dataGrid._selectedCells.RemoveAllButOneRow(dataGrid.Items.IndexOf(dataGrid.SelectedItems[0]));
                    }
                }
                else
                {
                    // If there is no selected item, then de-select all cells except for one.
                    using (dataGrid.UpdateSelectedCells())
                    {
                        dataGrid._selectedCells.RemoveAllButOne();
                    }
                }
            }
        }

        /// <summary>
        ///     Defines the selection behavior.
        /// </summary>
        /// <remarks>
        ///     The SelectionMode and the SelectionUnit properties together define
        ///     the selection behavior for the DataGrid.
        /// </remarks>
        public EGC.DataGridSelectionUnit SelectionUnit
        {
            get { return (EGC.DataGridSelectionUnit)GetValue(SelectionUnitProperty); }
            set { SetValue(SelectionUnitProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for the SelectionUnit property.
        /// </summary>
        public static readonly DependencyProperty SelectionUnitProperty =
            DependencyProperty.Register("SelectionUnit", typeof(EGC.DataGridSelectionUnit), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(EGC.DataGridSelectionUnit.FullRow, new PropertyChangedCallback(OnSelectionUnitChanged)));

        private static void OnSelectionUnitChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            EGC.DataGrid dataGrid = (EGC.DataGrid)d;
            EGC.DataGridSelectionUnit oldUnit = (EGC.DataGridSelectionUnit)e.OldValue;
            
            // Full wipe on unit change
            if (oldUnit != EGC.DataGridSelectionUnit.Cell)
            {
                dataGrid.UnselectAll();
            }

            if (oldUnit != EGC.DataGridSelectionUnit.FullRow)
            {
                using (dataGrid.UpdateSelectedCells())
                {
                    dataGrid._selectedCells.Clear();
                }
            }

            dataGrid.CoerceValue(IsSynchronizedWithCurrentItemProperty);
        }

        /// <summary>
        ///     Called when SelectedItems changes.
        /// </summary>
        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            if (!IsUpdatingSelectedCells)
            {
                using (UpdateSelectedCells())
                {
                    // Remove cells of rows that were deselected
                    int count = e.RemovedItems.Count;
                    for (int i = 0; i < count; i++)
                    {
                        object rowItem = e.RemovedItems[i];
                        UpdateSelectionOfCellsInRow(rowItem, /* isSelected = */ false);
                    }

                    // Add cells of rows that were selected
                    count = e.AddedItems.Count;
                    for (int i = 0; i < count; i++)
                    {
                        object rowItem = e.AddedItems[i];
                        UpdateSelectionOfCellsInRow(rowItem, /* isSelected = */ true);
                    }
                }
            }

            // Delete depends on the selection state
            CommandManager.InvalidateRequerySuggested();

            // Raise automation events
            if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected) ||
                AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementAddedToSelection) ||
                AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection))
            {
                EGC.DataGridAutomationPeer peer = EGC.DataGridAutomationPeer.FromElement(this) as EGC.DataGridAutomationPeer;
                if (peer != null)
                {
                    peer.RaiseAutomationSelectionEvents(e);
                }
            }

            base.OnSelectionChanged(e);
        }

        private void UpdateIsSelected()
        {
            UpdateIsSelected(_pendingUnselectedCells, /* isSelected = */ false);
            UpdateIsSelected(_pendingSelectedCells, /* isSelected = */ true);
        }

        /// <summary>
        ///     Updates the IsSelected property on cells due to a change in SelectedCells.
        /// </summary>
        private void UpdateIsSelected(EGC.VirtualizedCellInfoCollection cells, bool isSelected)
        {
            if (cells != null)
            {
                int numCells = cells.Count;
                if (numCells > 0)
                {
                    // Determine if it would be better to iterate through all the visible cells
                    // instead of through the update list.
                    bool useTracker = false;
                    
                    // For "small" updates it's simpler to just go through the cells, get the container,
                    // and update IsSelected. For "large" updates, it's faster to go through the visible
                    // cells, see if they're in the collection, and then update IsSelected.
                    // Determining small vs. large is going to be done using a magic number.
                    // 750 is close to the number of visible cells Excel shows by default on a 1280x1024 monitor.
                    if (numCells > 750)
                    {
                        int numTracker = 0;
                        int numColumns = _columns.Count;

                        EGC.ContainerTracking<EGC.DataGridRow> rowTracker = _rowTrackingRoot;
                        while (rowTracker != null)
                        {
                            numTracker += numColumns;
                            if (numTracker >= numCells)
                            {
                                // There are more cells visible than being updated
                                break;
                            }
                     
                            rowTracker = rowTracker.Next;
                        }

                        useTracker = (numCells > numTracker);
                    }

                    if (useTracker)
                    {
                        EGC.ContainerTracking<EGC.DataGridRow> rowTracker = _rowTrackingRoot;
                        while (rowTracker != null)
                        {
                            EGC.DataGridRow row = rowTracker.Container;
                            EGC.DataGridCellsPresenter cellsPresenter = row.CellsPresenter;
                            if (cellsPresenter != null)
                            {
                                EGC.ContainerTracking<EGC.DataGridCell> cellTracker = cellsPresenter.CellTrackingRoot;
                                while (cellTracker != null)
                                {
                                    EGC.DataGridCell cell = cellTracker.Container;
                                    EGC.DataGridCellInfo cellInfo = new EGC.DataGridCellInfo(cell);
                                    if (cells.Contains(cellInfo))
                                    {
                                        cell.SyncIsSelected(isSelected);
                                    }

                                    cellTracker = cellTracker.Next;
                                }
                            }

                            rowTracker = rowTracker.Next;
                        }
                    }
                    else
                    {
                        foreach (EGC.DataGridCellInfo cellInfo in cells)
                        {
                            EGC.DataGridCell cell = TryFindCell(cellInfo);
                            if (cell != null)
                            {
                                cell.SyncIsSelected(isSelected);
                            }
                        }
                    }
                }
            }
        }

        private void UpdateSelectionOfCellsInRow(object rowItem, bool isSelected)
        {
            int rowIndex = Items.IndexOf(rowItem);
            if (rowIndex >= 0)
            {
                int columnCount = _columns.Count;
                if (columnCount > 0)
                {
                    if (isSelected)
                    {
                        _selectedCells.AddRegion(rowIndex, 0, 1, columnCount);
                    }
                    else
                    {
                        _selectedCells.RemoveRegion(rowIndex, 0, 1, columnCount);
                    }
                }
            }
        }

        /// <summary>
        ///     Notification that a particular cell's IsSelected property changed.
        /// </summary>
        internal void CellIsSelectedChanged(EGC.DataGridCell cell, bool isSelected)
        {
            if (!IsUpdatingSelectedCells)
            {
                EGC.DataGridCellInfo cellInfo = new EGC.DataGridCellInfo(cell);
                if (isSelected)
                {
                    _selectedCells.AddValidatedCell(cellInfo);
                }
                else if (_selectedCells.Contains(cellInfo))
                {
                    _selectedCells.Remove(cellInfo);
                }
            }
        }

        /// <summary>
        ///     There was general input that means that selection should occur on
        ///     the given cell.
        /// </summary>
        /// <param name="cell">The target cell.</param>
        /// <param name="startDragging">Whether the input also indicated that dragging should start.</param>
        internal void HandleSelectionForCellInput(EGC.DataGridCell cell, bool startDragging, bool allowsExtendSelect, bool allowsMinimalSelect)
        {
            EGC.DataGridSelectionUnit selectionUnit = SelectionUnit;

            // If the mode is None, then no selection will occur
            if (selectionUnit == EGC.DataGridSelectionUnit.FullRow)
            {
                // In FullRow mode, items are selected
                MakeFullRowSelection(cell.RowDataItem, allowsExtendSelect, allowsMinimalSelect);
            }
            else
            {
                // In the other modes, cells can be individually selected
                MakeCellSelection(new EGC.DataGridCellInfo(cell), allowsExtendSelect, allowsMinimalSelect);
            }

            if (startDragging)
            {
                BeginDragging();
            }
        }

        /// <summary>
        ///     There was general input on a row header that indicated that
        ///     selection should occur on the given row.
        /// </summary>
        /// <param name="row">The target row.</param>
        /// <param name="startDragging">Whether the input also indicated that dragging should start.</param>
        internal void HandleSelectionForRowHeaderAndDetailsInput(EGC.DataGridRow row, bool startDragging)
        {
            object rowItem = row.Item;

            // When not dragging, move focus to the first cell
            if (!_isDraggingSelection && (_columns.Count > 0))
            {
                if (!IsKeyboardFocusWithin)
                {
                    // In order for CurrentCell to move focus, the
                    // DataGrid needs to be focused.
                    Focus();
                }

                CurrentCell = new EGC.DataGridCellInfo(rowItem, ColumnFromDisplayIndex(0), this);
            }

            // Select a row when the mode is not None and the unit allows selecting rows
            if (CanSelectRows)
            {
                MakeFullRowSelection(rowItem, /* allowsExtendSelect = */ true, /* allowsMinimalSelect = */ true);

                if (startDragging)
                {
                    BeginRowDragging();
                }
            }
        }

        private void BeginRowDragging()
        {
            BeginDragging();
            _isRowDragging = true;
        }

        private void BeginDragging()
        {
            if (Mouse.Capture(this, CaptureMode.SubTree))
            {
                _isDraggingSelection = true;
                _dragPoint = Mouse.GetPosition(this);
            }
        }

        private void EndDragging()
        {
            StopAutoScroll();
            if (Mouse.Captured == this)
            {
                ReleaseMouseCapture();
            }

            _isDraggingSelection = false;
            _isRowDragging = false;
        }

        /// <summary>
        ///     Processes selection for a row.
        ///     Depending on the current keyboard state, this may mean
        ///     - Selecting the row
        ///     - Deselecting the row
        ///     - Deselecting other rows
        ///     - Extending selection to the row
        /// </summary>
        /// <remarks>
        ///     ADO.Net has a bug (#524977) where if the row is in edit mode
        ///     and atleast one of the cells are edited and committed without
        ///     commiting the row itself, DataView.IndexOf for that row returns -1
        ///     and DataView.Contains returns false. The Workaround to this problem 
        ///     is to try to use the previously computed row index if the operations 
        ///     are in the same row scope.
        /// </remarks>
        protected virtual void MakeFullRowSelection(object dataItem, bool allowsExtendSelect, bool allowsMinimalSelect)
        {
            bool extendSelection = allowsExtendSelect && ShouldExtendSelection;
            
            // minimalModify means that previous selections should not be cleared
            // or that the particular item should be toggled.
            bool minimalModify = allowsMinimalSelect && ShouldMinimallyModifySelection;

            using (UpdateSelectedCells())
            {
                bool alreadyUpdating = IsUpdatingSelectedItems;
                if (!alreadyUpdating)
                {
                    BeginUpdateSelectedItems();
                }

                try
                {
                    if (extendSelection)
                    {
                        // Extend selection from the anchor to the item
                        int numColumns = _columns.Count;
                        if (numColumns > 0)
                        {
                            ItemCollection items = Items;
                            int startIndex = items.IndexOf(_selectionAnchor.Value.Item);
                            int endIndex = items.IndexOf(dataItem);
                            if (startIndex > endIndex)
                            {
                                // Ensure that startIndex is before endIndex
                                int temp = startIndex;
                                startIndex = endIndex;
                                endIndex = temp;
                            }

                            if ((startIndex >= 0) && (endIndex >= 0))
                            {
                                IList selectedItems = SelectedItems;
                                int numItemsSelected = selectedItems.Count;

                                if (!minimalModify)
                                {
                                    bool clearedCells = false;

                                    // Unselect items not within the selection range
                                    for (int index = 0; index < numItemsSelected; index++)
                                    {
                                        object item = selectedItems[index];
                                        int itemIndex = items.IndexOf(item);

                                        if ((itemIndex < startIndex) || (endIndex < itemIndex))
                                        {
                                            // Selector has been signaled to delay updating the
                                            // collection until we have finished the entire update.
                                            // The item will actually remain in the collection
                                            // until EndUpdateSelectedItems.
                                            selectedItems.RemoveAt(index);

                                            if (!clearedCells)
                                            {
                                                // We only want to clear if something is actually being removed.
                                                _selectedCells.Clear();
                                                clearedCells = true;
                                            }
                                        }
                                    }
                                }
                                else 
                                {
                                    // If we hold Control key - unselect only the previous drag selection (between CurrentCell and endIndex)
                                    int currentCellIndex = items.IndexOf(CurrentCell.Item);
                                    int removeRangeStartIndex = -1;
                                    int removeRangeEndIndex = -1;
                                    if (currentCellIndex < startIndex)
                                    {
                                        removeRangeStartIndex = currentCellIndex;
                                        removeRangeEndIndex = startIndex - 1;
                                    }
                                    else if (currentCellIndex > endIndex)
                                    {
                                        removeRangeStartIndex = endIndex + 1;
                                        removeRangeEndIndex = currentCellIndex;
                                    }

                                    if (removeRangeStartIndex >= 0 && removeRangeEndIndex >= 0)
                                    {
                                        for (int index = 0; index < numItemsSelected; index++)
                                        {
                                            object item = selectedItems[index];
                                            int itemIndex = items.IndexOf(item);

                                            if ((removeRangeStartIndex <= itemIndex) && (itemIndex <= removeRangeEndIndex))
                                            {
                                                // Selector has been signaled to delay updating the
                                                // collection until we have finished the entire update.
                                                // The item will actually remain in the collection
                                                // until EndUpdateSelectedItems.
                                                selectedItems.RemoveAt(index);
                                            }
                                        }

                                        _selectedCells.RemoveRegion(removeRangeStartIndex, 0, removeRangeEndIndex - removeRangeStartIndex + 1, Columns.Count);
                                    }
                                }

                                // Select the children in the selection range
                                IEnumerator enumerator = ((IEnumerable)items).GetEnumerator();
                                for (int index = 0; index <= endIndex; index++)
                                {
                                    if (!enumerator.MoveNext())
                                    {
                                        // In case the enumerator ends unexpectedly
                                        break;
                                    }

                                    if (index >= startIndex)
                                    {
                                        selectedItems.Add(enumerator.Current);
                                    }
                                }

                                _selectedCells.AddRegion(startIndex, 0, endIndex - startIndex + 1, _columns.Count);
                            }
                        }
                    }
                    else
                    {
                        if (minimalModify && SelectedItems.Contains(dataItem))
                        {
                            // Unselect the one item
                            UnselectItem(dataItem);
                        }
                        else
                        {
                            if (!minimalModify || !CanSelectMultipleItems)
                            {
                                // Unselect the other items
                                if (_selectedCells.Count > 0)
                                {
                                    // Pre-emptively clear the SelectedCells collection, which is O(1),
                                    // instead of waiting for the selection change notification to clear
                                    // SelectedCells row by row, which is O(n).
                                    _selectedCells.Clear();
                                }
                     
                                if (SelectedItems.Count > 0)
                                {
                                    SelectedItems.Clear();
                                }
                            }

                            if (_editingRowIndex >= 0 && _editingRowItem == dataItem)
                            {
                                // ADO.Net bug HACK, see remarks.
                                int numColumns = _columns.Count;
                                if (numColumns > 0)
                                {
                                    _selectedCells.AddRegion(_editingRowIndex, 0, 1, numColumns);
                                }

                                SelectItem(dataItem, false);
                            }
                            else
                            {
                                // Select the item
                                SelectItem(dataItem);
                            }
                        }

                        _selectionAnchor = new EGC.DataGridCellInfo(dataItem, ColumnFromDisplayIndex(0), this);
                    }
                }
                finally
                {
                    if (!alreadyUpdating)
                    {
                        EndUpdateSelectedItems();
                    }
                }
            }
        }

        /// <summary>
        ///     Process selection on a cell.
        ///     Depending on the current keyboard state, this may mean
        ///     - Selecting the cell
        ///     - Deselecting the cell
        ///     - Deselecting other cells
        ///     - Extending selection to the cell
        /// </summary>
        /// <remarks>
        ///     ADO.Net has a bug (#524977) where if the row is in edit mode
        ///     and atleast one of the cells are edited and committed without
        ///     commiting the row itself, DataView.IndexOf for that row returns -1
        ///     and DataView.Contains returns false. The Workaround to this problem 
        ///     is to try to use the previously computed row index if the operations 
        ///     are in the same row scope.
        /// </remarks>
        protected virtual void MakeCellSelection(EGC.DataGridCellInfo cellInfo, bool allowsExtendSelect, bool allowsMinimalSelect)
        {
            bool extendSelection = allowsExtendSelect && ShouldExtendSelection;

            // minimalModify means that previous selections should not be cleared
            // or that the particular item should be toggled.
            bool minimalModify = allowsMinimalSelect && ShouldMinimallyModifySelection;

            using (UpdateSelectedCells())
            {
                int cellInfoColumnIndex = cellInfo.Column.DisplayIndex;
                if (extendSelection)
                {
                    // Extend selection from the anchor to the cell
                    ItemCollection items = Items;

                    int startIndex = items.IndexOf(_selectionAnchor.Value.Item);
                    int endIndex = items.IndexOf(cellInfo.Item);

                    if (_editingRowIndex >= 0)
                    {
                        // ADO.Net bug HACK, see remarks.
                        if (_selectionAnchor.Value.Item == _editingRowItem)
                        {
                            startIndex = _editingRowIndex;
                        }

                        if (cellInfo.Item == _editingRowItem)
                        {
                            endIndex = _editingRowIndex;
                        }
                    }

                    EGC.DataGridColumn anchorColumn = _selectionAnchor.Value.Column;
                    int startColumnIndex = anchorColumn.DisplayIndex;
                    int endColumnIndex = cellInfoColumnIndex;

                    if ((startIndex >= 0) && (endIndex >= 0) &&
                        (startColumnIndex >= 0) && (endColumnIndex >= 0))
                    {
                        int newRowCount = Math.Abs(endIndex - startIndex) + 1;
                        int newColumnCount = Math.Abs(endColumnIndex - startColumnIndex) + 1;

                        if (!minimalModify)
                        {
                            // When extending cell selection, clear out any selected items
                            if (SelectedItems.Count > 0)
                            {
                                UnselectAll();
                            }

                            _selectedCells.Clear();
                        }
                        else
                        {
                            // Remove the previously selected region
                            int currentCellIndex = items.IndexOf(CurrentCell.Item);
                            if (_editingRowIndex >= 0 &&
                                _editingRowItem == CurrentCell.Item)
                            {
                                // ADO.Net bug HACK, see remarks.
                                currentCellIndex = _editingRowIndex;
                            }

                            int currentCellColumnIndex = CurrentCell.Column.DisplayIndex;

                            int previousStartIndex = Math.Min(startIndex, currentCellIndex);
                            int previousRowCount = Math.Abs(currentCellIndex - startIndex) + 1;
                            int previousStartColumnIndex = Math.Min(startColumnIndex, currentCellColumnIndex);
                            int previousColumnCount = Math.Abs(currentCellColumnIndex - startColumnIndex) + 1;

                            _selectedCells.RemoveRegion(previousStartIndex, previousStartColumnIndex, previousRowCount, previousColumnCount);

                            if (SelectionUnit == EGC.DataGridSelectionUnit.CellOrRowHeader)
                            {
                                int removeRowStartIndex = previousStartIndex;
                                int removeRowEndIndex = previousStartIndex + previousRowCount - 1;

                                if (previousColumnCount <= newColumnCount)
                                {
                                    // When no columns were removed, we can check fewer rows
                                    if (previousRowCount > newRowCount)
                                    {
                                        // One or more rows were removed, so only check those rows
                                        int removeCount = previousRowCount - newRowCount;
                                        removeRowStartIndex = (previousStartIndex == currentCellIndex) ? currentCellIndex : currentCellIndex - removeCount + 1;
                                        removeRowEndIndex = removeRowStartIndex + removeCount - 1;
                                    }
                                    else
                                    {
                                        // No rows were removed, so don't check anything
                                        removeRowEndIndex = removeRowStartIndex - 1;
                                    }
                                }

                                // For cells that were removed, check if their row is selected
                                for (int i = removeRowStartIndex; i <= removeRowEndIndex; i++)
                                {
                                    object item = Items[i];
                                    if (SelectedItems.Contains(item))
                                    {
                                        // When a cell in a row is unselected, unselect the row too
                                        SelectedItems.Remove(item);
                                    }
                                }
                            }
                        }

                        // Select the cells in rows within the selection range
                        _selectedCells.AddRegion(Math.Min(startIndex, endIndex), Math.Min(startColumnIndex, endColumnIndex), newRowCount, newColumnCount);
                    }
                }
                else
                {
                    bool selectedCellsContainsCellInfo = _selectedCells.Contains(cellInfo);
                    bool singleRowOperation = (_editingRowIndex >= 0 && _editingRowItem == cellInfo.Item);
                    if (!selectedCellsContainsCellInfo &&
                        singleRowOperation)
                    {
                        // ADO.Net bug HACK, see remarks.
                        selectedCellsContainsCellInfo = _selectedCells.Contains(_editingRowIndex, cellInfoColumnIndex);
                    }

                    if (minimalModify && selectedCellsContainsCellInfo)
                    {
                        // Unselect the one cell
                        if (singleRowOperation)
                        {
                            // ADO.Net bug HACK, see remarks.
                            _selectedCells.RemoveRegion(_editingRowIndex, cellInfoColumnIndex, 1, 1);
                        }
                        else
                        {
                            _selectedCells.Remove(cellInfo);
                        }

                        if ((SelectionUnit == EGC.DataGridSelectionUnit.CellOrRowHeader) &&
                            SelectedItems.Contains(cellInfo.Item))
                        {
                            // When a cell in a row is unselected, unselect the row too
                            SelectedItems.Remove(cellInfo.Item);
                        }
                    }
                    else
                    {
                        if (!minimalModify || !CanSelectMultipleItems)
                        {
                            // Unselect any items
                            if (SelectedItems.Count > 0)
                            {
                                UnselectAll();
                            }

                            // Unselect all the other cells
                            _selectedCells.Clear();
                        }

                        if (singleRowOperation)
                        {
                            // ADO.Net bug HACK, see remarks.
                            _selectedCells.AddRegion(_editingRowIndex, cellInfoColumnIndex, 1, 1);
                        }
                        else
                        {
                            // Select the cell
                            _selectedCells.AddValidatedCell(cellInfo);
                        }
                    }

                    _selectionAnchor = cellInfo;
                }
            }
        }

        private void SelectItem(object item)
        {
            SelectItem(item, true);
        }

        private void SelectItem(object item, bool selectCells)
        {
            if (selectCells)
            {
                using (UpdateSelectedCells())
                {
                    int itemIndex = Items.IndexOf(item);
                    int numColumns = _columns.Count;
                    if ((itemIndex >= 0) && (numColumns > 0))
                    {
                        _selectedCells.AddRegion(itemIndex, 0, 1, numColumns);
                    }
                }
            }

            UpdateSelectedItems(item, /* Add = */ true);
        }

        private void UnselectItem(object item)
        {
            using (UpdateSelectedCells())
            {
                int itemIndex = Items.IndexOf(item);
                int numColumns = _columns.Count;
                if ((itemIndex >= 0) && (numColumns > 0))
                {
                    _selectedCells.RemoveRegion(itemIndex, 0, 1, numColumns);
                }
            }

            UpdateSelectedItems(item, /*Add = */ false);
        }

        /// <summary>
        ///     Adds or Removes from SelectedItems when deferred selection is not handled by the caller. 
        /// </summary>
        private void UpdateSelectedItems(object item, bool add)
        {
            bool updatingSelectedItems = IsUpdatingSelectedItems;
            if (!updatingSelectedItems)
            {
                BeginUpdateSelectedItems();
            }

            try
            {
                if (add)
                {
                    SelectedItems.Add(item);
                }
                else
                {
                    SelectedItems.Remove(item);
                }
            }
            finally
            {
                if (!updatingSelectedItems)
                {
                    EndUpdateSelectedItems();
                }
            }
        }

        /// <summary>
        ///     When changing SelectedCells, do:
        ///     using (UpdateSelectedCells())
        ///     {
        ///         ...
        ///     }
        /// </summary>
        private IDisposable UpdateSelectedCells()
        {
            return new ChangingSelectedCellsHelper(this);
        }

        private void BeginUpdateSelectedCells()
        {
            Debug.Assert(!IsUpdatingSelectedCells);
            _updatingSelectedCells = true;
        }

        private void EndUpdateSelectedCells()
        {
            Debug.Assert(IsUpdatingSelectedCells);

            UpdateIsSelected();
            _updatingSelectedCells = false;

            NotifySelectedCellsChanged();
        }

        private bool IsUpdatingSelectedCells
        {
            get { return _updatingSelectedCells; }
        }

        /// <summary>
        ///     Handles tracking defered selection change notifications for selected cells.
        /// </summary>
        private class ChangingSelectedCellsHelper : IDisposable
        {
            internal ChangingSelectedCellsHelper(EGC.DataGrid dataGrid)
            {
                _dataGrid = dataGrid;
                _wasUpdatingSelectedCells = _dataGrid.IsUpdatingSelectedCells;
                if (!_wasUpdatingSelectedCells)
                {
                    _dataGrid.BeginUpdateSelectedCells();
                }
            }

            public void Dispose()
            {
                if (!_wasUpdatingSelectedCells)
                {
                    _dataGrid.EndUpdateSelectedCells();
                }
            }

            private EGC.DataGrid _dataGrid;
            private bool _wasUpdatingSelectedCells;
        }

        /// <summary>
        ///     SHIFT is down or performing a drag selection.
        ///     Multiple items can be selected.
        ///     There is a selection anchor.
        /// </summary>
        private bool ShouldExtendSelection
        {
            get
            {
                return CanSelectMultipleItems && (_selectionAnchor != null) &&
                    (_isDraggingSelection || ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift));
            }
        }

        /// <summary>
        ///     CTRL is down.
        ///     Previous selection should not be cleared, or a selected item should be toggled.
        /// </summary>
        private static bool ShouldMinimallyModifySelection
        {
            get
            {
                return (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;
            }
        }

        private bool CanSelectRows
        {
            get
            {
                switch (SelectionUnit)
                {
                    case EGC.DataGridSelectionUnit.FullRow:
                    case EGC.DataGridSelectionUnit.CellOrRowHeader:
                        return true;

                    case EGC.DataGridSelectionUnit.Cell:
                        return false;
                }

                Debug.Fail("Unknown SelectionUnit encountered.");
                return false;
            }
        }

        private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            _currentCellContainer = null;

            using (UpdateSelectedCells())
            {
                // Send the change notification to the selected cells collection
                _selectedCells.OnItemsCollectionChanged(e, SelectedItems);
            }

            if (e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Replace)
            {
                foreach (object item in e.OldItems)
                {
                    _itemAttachedStorage.ClearItem(item);
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                _itemAttachedStorage.Clear();
            }
        }

        #endregion

        #region Input

        private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(CanUserAddRowsProperty);
            d.CoerceValue(CanUserDeleteRowsProperty);

            // Many commands use IsEnabled to determine if they are enabled or not
            CommandManager.InvalidateRequerySuggested();
        }

        /// <summary>
        ///     Called when a keyboard key is pressed.
        /// </summary>
        protected override void OnKeyDown(KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Tab:
                    OnTabKeyDown(e);
                    break;

                case Key.Enter:
                    OnEnterKeyDown(e);
                    break;

                case Key.Left:
                case Key.Right:
                case Key.Up:
                case Key.Down:
                    OnArrowKeyDown(e);
                    break;

                case Key.Home:
                case Key.End:
                    OnHomeOrEndKeyDown(e);
                    break;

                case Key.PageUp:
                case Key.PageDown:
                    OnPageUpOrDownKeyDown(e);
                    break;
            }

            if (!e.Handled)
            {
                base.OnKeyDown(e);
            }
        }

        private static FocusNavigationDirection KeyToTraversalDirection(Key key)
        {
            switch (key)
            {
                case Key.Left:
                    return FocusNavigationDirection.Left;

                case Key.Right:
                    return FocusNavigationDirection.Right;

                case Key.Up:
                    return FocusNavigationDirection.Up;

                case Key.Down:
                default:
                    return FocusNavigationDirection.Down;
            }
        }
        
        protected virtual void OnArrowKeyDown(KeyEventArgs e)
        {
            EGC.DataGridCell currentCellContainer = CurrentCellContainer;
            if (currentCellContainer != null)
            {
                e.Handled = true;
                bool wasEditing = currentCellContainer.IsEditing;

                UIElement startElement = Keyboard.FocusedElement as UIElement;
                ContentElement startContentElement = (startElement == null) ? Keyboard.FocusedElement as ContentElement : null;
                if ((startElement != null) || (startContentElement != null))
                {
                    bool navigateFromCellContainer = e.OriginalSource == currentCellContainer;
                    if (navigateFromCellContainer)
                    {
                        KeyboardNavigationMode keyboardNavigationMode = KeyboardNavigation.GetDirectionalNavigation(this);
                        if (keyboardNavigationMode == KeyboardNavigationMode.Once)
                        {
                            // KeyboardNavigation will move the focus out of the DataGrid
                            DependencyObject nextFocusTarget = this.PredictFocus(KeyToTraversalDirection(e.Key));
                            if (nextFocusTarget != null && !this.IsAncestorOf(nextFocusTarget))
                            {
                                Keyboard.Focus(nextFocusTarget as IInputElement);
                            }

                            return;
                        }

                        int currentDisplayIndex = this.CurrentColumn.DisplayIndex;
                        int currentRowIndex = Items.IndexOf(CurrentItem);
                        int nextDisplayIndex = currentDisplayIndex;
                        int nextRowIndex = currentRowIndex;
                        bool controlModifier = ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control);

                        // Reverse the navigation in RTL flow direction
                        Key rtlKey = e.Key;
                        if (this.FlowDirection == FlowDirection.RightToLeft)
                        {
                            if (rtlKey == Key.Left)
                            {
                                rtlKey = Key.Right;
                            }
                            else if (rtlKey == Key.Right)
                            {
                                rtlKey = Key.Left;
                            }
                        }

                        switch (rtlKey)
                        {
                            case Key.Left:
                                if (controlModifier)
                                {
                                    nextDisplayIndex = InternalColumns.FirstVisibleDisplayIndex;
                                }
                                else
                                {
                                    nextDisplayIndex--;
                                    while (nextDisplayIndex >= 0)
                                    {
                                        EGC.DataGridColumn column = ColumnFromDisplayIndex(nextDisplayIndex);
                                        if (column.IsVisible)
                                        {
                                            break;
                                        }
                             
                                        nextDisplayIndex--;
                                    }
                             
                                    if (nextDisplayIndex < 0)
                                    {
                                        if (keyboardNavigationMode == KeyboardNavigationMode.Cycle)
                                        {
                                            nextDisplayIndex = InternalColumns.LastVisibleDisplayIndex;
                                        }
                                        else if (keyboardNavigationMode == KeyboardNavigationMode.Contained)
                                        {
                                            return;
                                        }
                                        else // Continue, Local, None - move focus out of the datagrid
                                        {
                                            MoveFocus(new TraversalRequest(e.Key == Key.Left ? FocusNavigationDirection.Left : FocusNavigationDirection.Right));
                                            return;
                                        }
                                    }
                                }

                                break;

                            case Key.Right:
                                if (controlModifier)
                                {
                                    nextDisplayIndex = Math.Max(0, InternalColumns.LastVisibleDisplayIndex);
                                }
                                else
                                {
                                    nextDisplayIndex++;
                                    int columnCount = Columns.Count;
                                    while (nextDisplayIndex < columnCount)
                                    {
                                        EGC.DataGridColumn column = ColumnFromDisplayIndex(nextDisplayIndex);
                                        if (column.IsVisible)
                                        {
                                            break;
                                        }

                                        nextDisplayIndex++;
                                    }

                                    if (nextDisplayIndex >= Columns.Count)
                                    {
                                        if (keyboardNavigationMode == KeyboardNavigationMode.Cycle)
                                        {
                                            nextDisplayIndex = InternalColumns.FirstVisibleDisplayIndex;
                                        }
                                        else if (keyboardNavigationMode == KeyboardNavigationMode.Contained)
                                        {
                                            return;
                                        }
                                        else // Continue, Local, None - move focus out of the datagrid
                                        {
                                            MoveFocus(new TraversalRequest(e.Key == Key.Left ? FocusNavigationDirection.Left : FocusNavigationDirection.Right));
                                            return;
                                        }
                                    }
                                }

                                break;

                            case Key.Up:
                                if (controlModifier)
                                {
                                    nextRowIndex = 0;
                                }
                                else
                                {
                                    nextRowIndex--;
                                    if (nextRowIndex < 0)
                                    {
                                        if (keyboardNavigationMode == KeyboardNavigationMode.Cycle)
                                        {
                                            nextRowIndex = Items.Count - 1;
                                        }
                                        else if (keyboardNavigationMode == KeyboardNavigationMode.Contained)
                                        {
                                            return;
                                        }
                                        else // Continue, Local, None - move focus out of the datagrid
                                        {
                                            MoveFocus(new TraversalRequest(FocusNavigationDirection.Up));
                                            return;
                                        }
                                    }
                                }

                                break;

                            case Key.Down:
                            default:
                                if (controlModifier)
                                {
                                    nextRowIndex = Math.Max(0, Items.Count - 1);
                                }
                                else
                                {
                                    nextRowIndex++;
                                    if (nextRowIndex >= Items.Count)
                                    {
                                        if (keyboardNavigationMode == KeyboardNavigationMode.Cycle)
                                        {
                                            nextRowIndex = 0;
                                        }
                                        else if (keyboardNavigationMode == KeyboardNavigationMode.Contained)
                                        {
                                            return;
                                        }
                                        else // Continue, Local, None - move focus out of the datagrid
                                        {
                                            MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
                                            return;
                                        }
                                    }
                                }

                                break;
                        }

                        EGC.DataGridColumn nextColumn = ColumnFromDisplayIndex(nextDisplayIndex);
                        object nextItem = Items[nextRowIndex];
                        ScrollCellIntoView(nextItem, nextColumn);
                        EGC.DataGridCell nextCellContainer = TryFindCell(nextItem, nextColumn);

                        if (nextCellContainer == null || nextCellContainer == currentCellContainer || !nextCellContainer.Focus())
                        {
                            return;
                        }
                    }
 
                    // Attempt to move focus
                    TraversalRequest request = new TraversalRequest(KeyToTraversalDirection(e.Key));
                    if (navigateFromCellContainer ||
                        ((startElement != null) && startElement.MoveFocus(request)) ||
                        ((startContentElement != null) && startContentElement.MoveFocus(request)))
                    {
                        SelectAndEditOnFocusMove(e, currentCellContainer, wasEditing, /* allowsExtendSelect = */ true, /* ignoreControlKey = */ true);
                    }
                }
            }
        }

        /// <summary>
        ///     Called when the tab key is pressed to perform focus navigation.
        /// </summary>
        private void OnTabKeyDown(KeyEventArgs e)
        {
            // When the end-user uses the keyboard to tab to another cell while the current cell
            // is in edit-mode, then the next cell should enter edit mode in addition to gaining
            // focus. There is no way to detect this from the focus change events, so the cell
            // is going to handle the complete operation manually.
            // The standard focus change method is being called here, so even if focus moves
            // to something other than a cell, focus should land on the element that it would
            // have landed on anyway.
            EGC.DataGridCell currentCellContainer = CurrentCellContainer;
            if (currentCellContainer != null)
            {
                bool wasEditing = currentCellContainer.IsEditing;
                bool previous = ((e.KeyboardDevice.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift);

                // Start navigation from the current focus to allow moveing focus on other focusable elements inside the cell
                UIElement startElement = Keyboard.FocusedElement as UIElement;
                ContentElement startContentElement = (startElement == null) ? Keyboard.FocusedElement as ContentElement : null;
                if ((startElement != null) || (startContentElement != null))
                {
                    e.Handled = true;

                    FocusNavigationDirection direction = previous ? FocusNavigationDirection.Previous : FocusNavigationDirection.Next;
                    TraversalRequest request = new TraversalRequest(direction);
                    request.Wrapped = true; // Navigate only within datagrid

                    // Move focus to the the next or previous tab stop.
                    if (((startElement != null) && startElement.MoveFocus(request)) ||
                        ((startContentElement != null) && startContentElement.MoveFocus(request)))
                    {
                        // If focus moved to the cell while in edit mode - keep navigating to the previous cell
                        if (wasEditing && previous && Keyboard.FocusedElement == currentCellContainer)
                        {
                            currentCellContainer.MoveFocus(request);
                        }
                        else
                        {
                            UIElement tempElement = Keyboard.FocusedElement as UIElement;
                            if (tempElement != null && tempElement is EGC.DataGridCell)
                            {
                                tempElement.MoveFocus(request);
                            }
                        }

                        // In case of grouping if a row level commit happened due to
                        // the previous focus change, the container of the row gets
                        // removed from the visual tree by the CollectionView, 
                        // but we still hang on to a cell of that row, which will be used
                        // by the call to SelectAndEditOnFocusMove. Hence re-establishing the
                        // focus appropriately in such cases.
                        if (IsGrouping && wasEditing)
                        {
                            EGC.DataGridCell newCell = GetCellForSelectAndEditOnFocusMove();

                            if (newCell != null &&
                                newCell.RowDataItem == currentCellContainer.RowDataItem)
                            {
                                EGC.DataGridCell realNewCell = TryFindCell(newCell.RowDataItem, newCell.Column);

                                // Forcing an UpdateLayout since the generation of the new row
                                // container which was removed earlier is done in measure.
                                if (realNewCell == null)
                                {
                                    UpdateLayout();
                                    realNewCell = TryFindCell(newCell.RowDataItem, newCell.Column);
                                }
                                if (realNewCell != null && realNewCell != newCell)
                                {
                                    realNewCell.Focus();
                                }
                            }
                        }

                        // When doing TAB and SHIFT+TAB focus movement, don't confuse the selection
                        // code, which also relies on SHIFT to know whether to extend selection or not.
                        SelectAndEditOnFocusMove(e, currentCellContainer, wasEditing, /* allowsExtendSelect = */ false, /* ignoreControlKey = */ true);
                    }
                }
            }
        }

        private void OnEnterKeyDown(KeyEventArgs e)
        {
            EGC.DataGridCell currentCellContainer = CurrentCellContainer;
            if ((currentCellContainer != null) && (_columns.Count > 0))
            {
                e.Handled = true;

                EGC.DataGridColumn column = currentCellContainer.Column;

                // Commit any current edit
                if (CommitAnyEdit() && ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == 0))
                {
                    bool shiftModifier = ((e.KeyboardDevice.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift);

                    // Go to the next row, keeping the column the same
                    int numItems = Items.Count;
                    int index = Math.Max(0, Math.Min(numItems - 1, Items.IndexOf(currentCellContainer.RowDataItem) + (shiftModifier ? -1 : 1)));

                    if (index < numItems)
                    {
                        object rowItem = Items[index];
                        ScrollIntoView(rowItem, column);

                        if (CurrentCell.Item != rowItem)
                        {
                            // Focus the new cell
                            CurrentCell = new EGC.DataGridCellInfo(rowItem, column, this);

                            // Will never edit on ENTER, so just say that the old cell wasn't in edit mode
                            SelectAndEditOnFocusMove(e, currentCellContainer, /* wasEditing = */ false, /* allowsExtendSelect = */ false, /* ignoreControlKey = */ true);
                        }
                        else
                        {
                            // When the new item jumped to the bottom, CurrentCell doesn't actually change,
                            // but there is a new container.
                            currentCellContainer = CurrentCellContainer;
                            if (currentCellContainer != null)
                            {
                                currentCellContainer.Focus();
                            }
                        }
                    }
                }
            }
        }

        private EGC.DataGridCell GetCellForSelectAndEditOnFocusMove()
        {
            EGC.DataGridCell newCell = Keyboard.FocusedElement as EGC.DataGridCell;

            // If focus has moved within DataGridCell use CurrentCellContainer
            if (newCell == null && CurrentCellContainer != null && CurrentCellContainer.IsKeyboardFocusWithin)
            {
                newCell = CurrentCellContainer;
            }

            return newCell;
        }

        private void SelectAndEditOnFocusMove(KeyEventArgs e, EGC.DataGridCell oldCell, bool wasEditing, bool allowsExtendSelect, bool ignoreControlKey)
        {
            EGC.DataGridCell newCell = GetCellForSelectAndEditOnFocusMove();

            if ((newCell != null) && (newCell.DataGridOwner == this))
            {
                if (ignoreControlKey || ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == 0))
                {
                    HandleSelectionForCellInput(newCell, /* startDragging = */ false, allowsExtendSelect, /* allowsMinimalSelect = */ false);
                }

                // If focus moved to a new cell within the same row that didn't
                // decide on its own to enter edit mode, put it in edit mode.
                if (wasEditing && !newCell.IsEditing && (oldCell.RowDataItem == newCell.RowDataItem))
                {
                    BeginEdit(e);
                }
            }
        }

        private void OnHomeOrEndKeyDown(KeyEventArgs e)
        {
            if ((_columns.Count > 0) && (Items.Count > 0))
            {
                e.Handled = true;

                bool homeKey = (e.Key == Key.Home);
                bool controlModifier = ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control);

                // Go to the first or last cell
                object item = controlModifier ? Items[homeKey ? 0 : Items.Count - 1] : CurrentItem;
                EGC.DataGridColumn column = ColumnFromDisplayIndex(homeKey ? InternalColumns.FirstVisibleDisplayIndex : InternalColumns.LastVisibleDisplayIndex);

                ScrollCellIntoView(item, column);
                EGC.DataGridCell cell = TryFindCell(item, column);
                if (cell != null)
                {
                    cell.Focus();
                    HandleSelectionForCellInput(cell, /* startDragging = */ false, /* allowsExtendSelect = */ true, /* allowsMinimalSelect = */ false);
                }
            }
        }

        private void OnPageUpOrDownKeyDown(KeyEventArgs e)
        {
            // This code relies on DataGridRowsPresenter since ScrollHost relies
            // on InternalItemsHost, which relies on DataGridRowsPresenter.
            // Additionally, it relies on ViewportHeight being in logical units
            // instead of pixels.
            ScrollViewer scrollHost = InternalScrollHost;
            if (scrollHost != null)
            {
                object currentRow = CurrentItem;
                EGC.DataGridColumn currentColumn = CurrentColumn;
                int rowIndex = Items.IndexOf(currentRow);
                if (rowIndex >= 0)
                {
                    // Predict the page up/page down item based on the viewport height, which
                    // should be in logical units.
                    // This is not going to work well when the rows have different heights, but
                    // it is the best estimate we have at the moment.
                    int jumpDistance = Math.Max(1, (int)scrollHost.ViewportHeight - 1);
                    int targetIndex = (e.Key == Key.PageUp) ? rowIndex - jumpDistance : rowIndex + jumpDistance;
                    targetIndex = Math.Max(0, Math.Min(targetIndex, Items.Count - 1));

                    // Scroll the target row into view, keeping the current column
                    object targetRow = Items[targetIndex];
                    ScrollCellIntoView(targetRow, currentColumn);
                    EGC.DataGridCell cell = TryFindCell(targetRow, currentColumn);
                    if (cell != null)
                    {
                        cell.Focus();
                        HandleSelectionForCellInput(cell, /* startDragging = */ false, /* allowsExtendSelect = */ true, /* allowsMinimalSelect = */ false);
                    }
                }
            }
        }

        /// <summary>
        ///     Continues a drag selection.
        /// </summary>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (_isDraggingSelection)
            {
                if (e.LeftButton == MouseButtonState.Pressed)
                {
                    // Check that the mouse has moved relative to the DataGrid.
                    // This check prevents the case where a row is partially visible
                    // at the bottom. If this row is clicked, then it will be scrolled
                    // into view and away from the mouse. The mouse will then appear
                    // (according to these messages) as if it moved over a new cell, and
                    // could invoke a drag, but the actual mouse position relative to 
                    // the DataGrid hasn't changed.
                    Point currentMousePosition = Mouse.GetPosition(this);
                    if (!DoubleUtil.AreClose(currentMousePosition, _dragPoint))
                    {
                        _dragPoint = currentMousePosition;

                        RelativeMousePositions position = RelativeMousePosition;
                        if (position == RelativeMousePositions.Over)
                        {
                            // The mouse is within the field of cells and rows, use the actual
                            // elements to determine changes to selection.
                            if (_isRowDragging)
                            {
                                EGC.DataGridRow row = MouseOverRow;
                                if ((row != null) && (row.Item != CurrentItem))
                                {
                                    // Continue a row header drag to the given row
                                    HandleSelectionForRowHeaderAndDetailsInput(row, /* startDragging = */ false);
                                    CurrentItem = row.Item;
                                    e.Handled = true;
                                }
                            }
                            else
                            {
                                EGC.DataGridCell cell = MouseOverCell;
                                if (cell == null)
                                {
                                    EGC.DataGridRow row = MouseOverRow;
                                    if (row != null)
                                    {
                                        // The mouse is over a row but not necessarily a cell,
                                        // such as over a header or details section. Find the
                                        // nearest cell and use that.
                                        cell = GetCellNearMouse();
                                    }
                                }

                                if ((cell != null) && (cell != CurrentCellContainer))
                                {
                                    HandleSelectionForCellInput(cell, /* startDragging = */ false, /* allowsExtendSelect = */ true, /* allowsMinimalSelect = */ true);
                                    cell.Focus();
                                    e.Handled = true;
                                }
                            }
                        }
                        else
                        {
                            // The mouse is outside of the field of cells and rows.
                            if (_isRowDragging && IsMouseToLeftOrRightOnly(position))
                            {
                                // Figure out which row the mouse is in-line with and select it
                                EGC.DataGridRow row = GetRowNearMouse();
                                if ((row != null) && (row.Item != CurrentItem))
                                {
                                    // The mouse is directly to the left or right of the row
                                    HandleSelectionForRowHeaderAndDetailsInput(row, /* startDragging = */ false);
                                    CurrentItem = row.Item;
                                    e.Handled = true;
                                }
                            }
                            else if (_hasAutoScrolled)
                            {
                                // The mouse is outside the grid, and we've started auto-scrolling.
                                // The user has moved the mouse and would like a quick update.
                                if (DoAutoScroll())
                                {
                                    e.Handled = true;
                                }
                            }
                            else
                            {
                                // Ensure that the auto-scroll timer has started
                                //call NativeMethods.GetDoubleClickTime() may throw exception, so comment out this function.
                                //StartAutoScroll();
                            }
                        }
                    }
                }
                else
                {
                    // The mouse button is up, end the drag operation
                    EndDragging();
                }
            }
        }

        private static void OnAnyMouseUpThunk(object sender, MouseButtonEventArgs e)
        {
            ((EGC.DataGrid)sender).OnAnyMouseUp(e);
        }

        /// <summary>
        ///     Ends a drag selection.
        /// </summary>
        private void OnAnyMouseUp(MouseButtonEventArgs e)
        {
            EndDragging();
        }

        /// <summary>
        ///     When a ContextMenu opens on a cell that isn't selected, it should 
        ///     become selected.
        /// </summary>
        protected override void OnContextMenuOpening(ContextMenuEventArgs e)
        {
            EGC.DataGridCell cell = null;
            EGC.DataGridRowHeader rowHeader = null;
            UIElement sourceElement = e.OriginalSource as UIElement;
            while (sourceElement != null)
            {
                cell = sourceElement as EGC.DataGridCell;
                if (cell != null)
                {
                    break;
                }

                rowHeader = sourceElement as EGC.DataGridRowHeader;
                if (rowHeader != null)
                {
                    break;
                }

                sourceElement = VisualTreeHelper.GetParent(sourceElement) as UIElement;
            }

            if ((cell != null) && !cell.IsSelected && !cell.IsKeyboardFocusWithin)
            {
                cell.Focus();
                HandleSelectionForCellInput(cell, /* startDragging = */ false, /* allowsExtendSelect = */ true, /* allowsMinimalSelect = */ true);
            }

            if (rowHeader != null)
            {
                EGC.DataGridRow parentRow = rowHeader.ParentRow;
                if (parentRow != null)
                {
                    HandleSelectionForRowHeaderAndDetailsInput(parentRow, /* startDragging = */ false);
                }
            }
        }

        /// <summary>
        ///     Finds the row that contains the mouse's Y coordinate.
        /// </summary>
        /// <remarks>
        ///     Relies on InternalItemsHost.
        ///     Meant to be used when the mouse is outside the DataGrid.
        /// </remarks>
        private EGC.DataGridRow GetRowNearMouse()
        {
            Debug.Assert(RelativeMousePosition != RelativeMousePositions.Over, "The mouse is not supposed to be over the DataGrid.");

            Panel itemsHost = InternalItemsHost;
            if (itemsHost != null)
            {
                // Iterate from the end to the beginning since it is more common
                // to drag toward the end.
                int count = itemsHost.Children.Count;
                for (int i = count - 1; i >= 0; i--)
                {
                    EGC.DataGridRow row = itemsHost.Children[i] as EGC.DataGridRow;
                    if (row != null)
                    {
                        Point pt = Mouse.GetPosition(row);
                        Rect rowBounds = new Rect(new Point(), row.RenderSize);
                        if ((pt.Y >= rowBounds.Top) && (pt.Y <= rowBounds.Bottom))
                        {
                            // The mouse cursor's Y position is within the Y bounds of the row
                            return row;
                        }
                    }
                }
            }

            return null;
        }

        /// <summary>
        ///     Finds the cell that is nearest to the mouse.
        /// </summary>
        /// <remarks>
        ///     Relies on InternalItemsHost.
        /// </remarks>
        protected EGC.DataGridCell GetCellNearMouse()
        {
            Panel itemsHost = InternalItemsHost;
            if (itemsHost != null)
            {
                Rect itemsHostBounds = new Rect(new Point(), itemsHost.RenderSize);
                double closestDistance = Double.PositiveInfinity;
                EGC.DataGridCell closestCell = null;
                bool isMouseInCorner = IsMouseInCorner(RelativeMousePosition);

                // Iterate from the end to the beginning since it is more common
                // to drag toward the end.
                int count = itemsHost.Children.Count;
                for (int i = count - 1; i >= 0; i--)
                {
                    EGC.DataGridRow row = itemsHost.Children[i] as EGC.DataGridRow;
                    if (row != null)
                    {
                        EGC.DataGridCellsPresenter cellsPresenter = row.CellsPresenter;
                        if (cellsPresenter != null)
                        {
                            // Go through all of the instantiated cells and find the closest cell
                            EGC.ContainerTracking<EGC.DataGridCell> cellTracker = cellsPresenter.CellTrackingRoot;
                            while (cellTracker != null)
                            {
                                EGC.DataGridCell cell = cellTracker.Container;

                                double cellDistance;
                                if (CalculateCellDistance(cell, row, itemsHost, itemsHostBounds, isMouseInCorner, out cellDistance))
                                {
                                    if ((closestCell == null) || (cellDistance < closestDistance))
                                    {
                                        // This cell's distance is less, so make it the closest cell
                                        closestDistance = cellDistance;
                                        closestCell = cell;
                                    }
                                }

                                cellTracker = cellTracker.Next;
                            }

                            // Check if the header is close
                            EGC.DataGridRowHeader rowHeader = row.RowHeader;
                            if (rowHeader != null)
                            {
                                double cellDistance;
                                if (CalculateCellDistance(rowHeader, row, itemsHost, itemsHostBounds, isMouseInCorner, out cellDistance))
                                {
                                    if ((closestCell == null) || (cellDistance < closestDistance))
                                    {
                                        // If the header is the closest, then use the first cell from the row
                                        EGC.DataGridCell cell = row.TryGetCell(DisplayIndexMap[0]);
                                        if (cell != null)
                                        {
                                            closestDistance = cellDistance;
                                            closestCell = cell;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                return closestCell;
            }

            return null;
        }

        /// <summary>
        ///     Determines if a cell meets the criteria for being chosen. If it does, it
        ///     calculates its a "distance" that can be compared to other cells.
        /// </summary>
        /// <param name="distance">
        ///     A value that represents the distance between the mouse and the cell.
        ///     This is not necessarily an accurate pixel number in some cases.
        /// </param>
        /// <returns>
        ///     true if the cell can be a drag target. false otherwise.
        /// </returns>
        private static bool CalculateCellDistance(FrameworkElement cell, EGC.DataGridRow rowOwner, Panel itemsHost, Rect itemsHostBounds, bool isMouseInCorner, out double distance)
        {
            GeneralTransform transform = cell.TransformToAncestor(itemsHost);
            Rect cellBounds = new Rect(new Point(), cell.RenderSize);

            // Limit to only cells that are entirely visible
            if (itemsHostBounds.Contains(transform.TransformBounds(cellBounds)))
            {
                Point pt = Mouse.GetPosition(cell);
                if (isMouseInCorner)
                {
                    // When the mouse is in the corner, go by distance from center of the cell
                    Vector v = new Vector(pt.X - (cellBounds.Width * 0.5), pt.Y - (cellBounds.Height * 0.5));
                    distance = v.Length;
                    return true;
                }
                else
                {
                    Point rowPt = Mouse.GetPosition(rowOwner);
                    Rect rowBounds = new Rect(new Point(), rowOwner.RenderSize);

                    // The mouse should overlap a row or column
                    if ((pt.X >= cellBounds.Left) && (pt.X <= cellBounds.Right))
                    {
                        // The mouse is within a column
                        if ((rowPt.Y >= rowBounds.Top) && (rowPt.Y <= rowBounds.Bottom))
                        {
                            // Mouse is within the cell
                            distance = 0.0;
                        }
                        else
                        {
                            // Mouse is outside but is within a columns horizontal bounds
                            distance = Math.Abs(pt.Y - cellBounds.Top);
                        }

                        return true;
                    }
                    else if ((rowPt.Y >= rowBounds.Top) && (rowPt.Y <= rowBounds.Bottom))
                    {
                        // Mouse is outside but is within a row's vertical bounds
                        distance = Math.Abs(pt.X - cellBounds.Left);
                        return true;
                    }
                }
            }

            distance = Double.PositiveInfinity;
            return false;
        }

        /// <summary>
        ///     The row that the mouse is over.
        /// </summary>
        private static EGC.DataGridRow MouseOverRow
        {
            get
            {
                return EGC.DataGridHelper.FindVisualParent<EGC.DataGridRow>(Mouse.DirectlyOver as UIElement);
            }
        }

        // The cell that the mouse is over.
        protected static EGC.DataGridCell MouseOverCell
        {
            get
            {
                return EGC.DataGridHelper.FindVisualParent<EGC.DataGridCell>(Mouse.DirectlyOver as UIElement);
            }
        }

        /// <summary>
        ///     The mouse position relative to the ItemsHost.
        /// </summary>
        /// <remarks>
        ///     Relies on InternalItemsHost.
        /// </remarks>
        private RelativeMousePositions RelativeMousePosition
        {
            get
            {
                RelativeMousePositions position = RelativeMousePositions.Over;

                Panel itemsHost = InternalItemsHost;
                if (itemsHost != null)
                {
                    Point pt = Mouse.GetPosition(itemsHost);
                    Rect bounds = new Rect(new Point(), itemsHost.RenderSize);

                    if (pt.X < bounds.Left)
                    {
                        position |= RelativeMousePositions.Left;
                    }
                    else if (pt.X > bounds.Right)
                    {
                        position |= RelativeMousePositions.Right;
                    }

                    if (pt.Y < bounds.Top)
                    {
                        position |= RelativeMousePositions.Above;
                    }
                    else if (pt.Y > bounds.Bottom)
                    {
                        position |= RelativeMousePositions.Below;
                    }
                }

                return position;
            }
        }

        private static bool IsMouseToLeft(RelativeMousePositions position)
        {
            return (position & RelativeMousePositions.Left) == RelativeMousePositions.Left;
        }

        private static bool IsMouseToRight(RelativeMousePositions position)
        {
            return (position & RelativeMousePositions.Right) == RelativeMousePositions.Right;
        }

        private static bool IsMouseAbove(RelativeMousePositions position)
        {
            return (position & RelativeMousePositions.Above) == RelativeMousePositions.Above;
        }

        private static bool IsMouseBelow(RelativeMousePositions position)
        {
            return (position & RelativeMousePositions.Below) == RelativeMousePositions.Below;
        }

        private static bool IsMouseToLeftOrRightOnly(RelativeMousePositions position)
        {
            return (position == RelativeMousePositions.Left) || (position == RelativeMousePositions.Right);
        }

        private static bool IsMouseInCorner(RelativeMousePositions position)
        {
            return (position != RelativeMousePositions.Over) &&
                (position != RelativeMousePositions.Above) &&
                (position != RelativeMousePositions.Below) &&
                (position != RelativeMousePositions.Left) &&
                (position != RelativeMousePositions.Right);
        }

        [Flags]
        private enum RelativeMousePositions
        {
            Over    = 0x00,
            Above   = 0x01,
            Below   = 0x02,
            Left    = 0x04,
            Right   = 0x08,
        }

        #endregion

        #region Automation

        protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
        {
            return new EGC.DataGridAutomationPeer(this);
        }

        #endregion

        #region Cell Info

        protected EGC.DataGridCell TryFindCell(EGC.DataGridCellInfo info)
        {
            // Does not de-virtualize cells
            return TryFindCell(info.Item, info.Column);
        }

        internal EGC.DataGridCell TryFindCell(object item, EGC.DataGridColumn column)
        {
            // Does not de-virtualize cells
            EGC.DataGridRow row = (EGC.DataGridRow)ItemContainerGenerator.ContainerFromItem(item);
            int columnIndex = _columns.IndexOf(column);
            if ((row != null) && (columnIndex >= 0))
            {
                return row.TryGetCell(columnIndex);
            }

            return null;
        }

        #endregion

        #region Auto Sort

        /// <summary>
        /// Dependecy property for CanUserSortColumns Property
        /// </summary>
        public static readonly DependencyProperty CanUserSortColumnsProperty =
            DependencyProperty.Register(
                "CanUserSortColumns",
                typeof(bool),
                typeof(EGC.DataGrid),
                new FrameworkPropertyMetadata(true));

        /// <summary>
        /// The property which determines whether the datagrid can be sorted by 
        /// cells in the columns or not
        /// </summary>
        public bool CanUserSortColumns
        {
            get { return (bool)GetValue(CanUserSortColumnsProperty); }
            set { SetValue(CanUserSortColumnsProperty, value); }
        }

        public event DataGridSortingEventHandler Sorting;

        /// <summary>
        /// Protected method which raises the sorting event and does default sort
        /// </summary>
        /// <param name="eventArgs"></param>
        protected virtual void OnSorting(EGC.DataGridSortingEventArgs eventArgs)
        {
            eventArgs.Handled = false;
            if (Sorting != null)
            {
                Sorting(this, eventArgs);
            }

            if (!eventArgs.Handled)
            {
                DefaultSort(
                    eventArgs.Column,
                    /* clearExistinSortDescriptions */ 
                    (Keyboard.Modifiers & ModifierKeys.Shift) != ModifierKeys.Shift);
            }
        }

        /// <summary>
        /// Method to perform sorting on datagrid
        /// </summary>
        /// <param name="sortColumn"></param>
        internal void PerformSort(EGC.DataGridColumn sortColumn)
        {
            Debug.Assert(sortColumn != null, "column should not be null");

            if (!CanUserSortColumns || !sortColumn.CanUserSort)
            {
                return;
            }

            if (CommitAnyEdit())
            {
                PrepareForSort(sortColumn);

                EGC.DataGridSortingEventArgs eventArgs = new EGC.DataGridSortingEventArgs(sortColumn);
                OnSorting(eventArgs);

                if (Items.NeedsRefresh)
                {
                    try
                    {
                        Items.Refresh();
                    }
                    catch (InvalidOperationException invalidOperationException)
                    {
                        Items.SortDescriptions.Clear();
                        throw new InvalidOperationException(SR.Get(SRID.DataGrid_ProbableInvalidSortDescription), invalidOperationException);
                    }
                }
            }
        }

        /// <summary>
        /// Clears the sort directions for all the columns except the column to be sorted upon
        /// </summary>
        /// <param name="sortColumn"></param>
        private void PrepareForSort(EGC.DataGridColumn sortColumn)
        {
            if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
            {
                return;
            }

            if (Columns != null)
            {
                foreach (EGC.DataGridColumn column in Columns)
                {
                    if (column != sortColumn)
                    {
                        column.SortDirection = null;
                    }
                }
            }
        }

        /// <summary>
        /// Determines the sort direction and sort property name and adds a sort
        /// description to the Items>SortDescriptions Collection. Clears all the 
        /// existing sort descriptions.
        /// </summary>
        /// <param name="column"></param>
        /// <param name="clearExistingSortDescriptions"></param>
        private void DefaultSort(EGC.DataGridColumn column, bool clearExistingSortDescriptions)
        {
            ListSortDirection sortDirection = ListSortDirection.Ascending;
            Nullable<ListSortDirection> currentSortDirection = column.SortDirection;
            if (currentSortDirection.HasValue &&
                currentSortDirection.Value == ListSortDirection.Ascending)
            {
                sortDirection = ListSortDirection.Descending;
            }

            string sortPropertyName = column.SortMemberPath;
            if (!string.IsNullOrEmpty(sortPropertyName))
            {
                int descriptorIndex = -1;
                if (clearExistingSortDescriptions)
                {
                    // clear the sortdesriptions collection
                    Items.SortDescriptions.Clear();
                }
                else
                {
                    // get the index of existing descriptor to replace it
                    for (int i = 0; i < Items.SortDescriptions.Count; i++)
                    {
                        if (string.Compare(Items.SortDescriptions[i].PropertyName, sortPropertyName, StringComparison.Ordinal) == 0 &&
                            (GroupingSortDescriptionIndices == null ||
                            !GroupingSortDescriptionIndices.Contains(i)))
                        {
                            descriptorIndex = i;
                            break;
                        }
                    }
                }

                SortDescription sortDescription = new SortDescription(sortPropertyName, sortDirection);
                try
                {
                    if (descriptorIndex >= 0)
                    {
                        Items.SortDescriptions[descriptorIndex] = sortDescription;
                    }
                    else
                    {
                        Items.SortDescriptions.Add(sortDescription);
                    }

                    if (clearExistingSortDescriptions || !_sortingStarted)
                    {
                        RegenerateGroupingSortDescriptions();
                        _sortingStarted = true;
                    }
                }
                catch (InvalidOperationException invalidOperationException)
                {
                    Items.SortDescriptions.Clear();
                    throw new InvalidOperationException(SR.Get(SRID.DataGrid_InvalidSortDescription), invalidOperationException);
                }

                column.SortDirection = sortDirection;
            }
        }

        /// <summary>
        /// List which holds all the indices of SortDescriptions which were
        /// added for the sake of GroupDescriptions
        /// </summary>
        private List<int> GroupingSortDescriptionIndices
        {
            get
            {
                return _groupingSortDescriptionIndices;
            }

            set
            {
                _groupingSortDescriptionIndices = value;
            }
        }

        /// <summary>
        /// SortDescription collection changed listener. Ensures that GroupingSortDescriptionIndices
        /// is in sync with SortDescriptions.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnItemsSortDescriptionsChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (_ignoreSortDescriptionsChange || GroupingSortDescriptionIndices == null)
            {
                return;
            }

            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    Debug.Assert(e.NewItems.Count == 1, "SortDescriptionCollection should handle one element at a time");
                    for (int i = 0, count = GroupingSortDescriptionIndices.Count; i < count; i++)
                    {
                        if (GroupingSortDescriptionIndices[i] >= e.NewStartingIndex)
                        {
                            GroupingSortDescriptionIndices[i]++;
                        }
                    }

                    break;
                case NotifyCollectionChangedAction.Remove:
                    Debug.Assert(e.OldItems.Count == 1, "SortDescriptionCollection should handle one element at a time");
                    for (int i = 0, count = GroupingSortDescriptionIndices.Count; i < count; i++)
                    {
                        if (GroupingSortDescriptionIndices[i] > e.OldStartingIndex)
                        {
                            GroupingSortDescriptionIndices[i]--;
                        }
                        else if (GroupingSortDescriptionIndices[i] == e.OldStartingIndex)
                        {
                            GroupingSortDescriptionIndices.RemoveAt(i);
                            i--;
                            count--;
                        }
                    }

                    break;
                case NotifyCollectionChangedAction.Move:
                    // SortDescriptionCollection doesnt support move, atleast as an atomic operation. Hence Do nothing.
                    break;
                case NotifyCollectionChangedAction.Replace:
                    Debug.Assert(e.OldItems.Count == 1 && e.NewItems.Count == 1, "SortDescriptionCollection should handle one element at a time");
                    GroupingSortDescriptionIndices.Remove(e.OldStartingIndex);
                    break;
                case NotifyCollectionChangedAction.Reset:
                    GroupingSortDescriptionIndices.Clear();
                    break;
            }
        }

        /// <summary>
        /// Method to remove all the SortDescriptions which were added based on GroupDescriptions
        /// </summary>
        private void RemoveGroupingSortDescriptions()
        {
            if (GroupingSortDescriptionIndices == null)
            {
                return;
            }

            bool originalIgnoreSortDescriptionChanges = _ignoreSortDescriptionsChange;
            _ignoreSortDescriptionsChange = true;
            try
            {
                for (int i = 0, count = GroupingSortDescriptionIndices.Count; i < count; i++)
                {
                    Items.SortDescriptions.RemoveAt(GroupingSortDescriptionIndices[i] - i);
                }

                GroupingSortDescriptionIndices.Clear();
            }
            finally
            {
                _ignoreSortDescriptionsChange = originalIgnoreSortDescriptionChanges;
            }
        }

        /// <summary>
        /// Helper method which determines if one can create a SortDescription out of
        /// a GroupDescription.
        /// </summary>
        /// <param name="propertyGroupDescription"></param>
        /// <returns></returns>
        private static bool CanConvertToSortDescription(PropertyGroupDescription propertyGroupDescription)
        {
            if (propertyGroupDescription != null &&
                propertyGroupDescription.Converter == null &&
                propertyGroupDescription.StringComparison == StringComparison.Ordinal)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Method to add SortDescriptions based on GroupDescriptions.
        /// Only PropertGroupDescriptions with no ValueConverter and with
        /// Oridinal comparison are considered suitable.
        /// </summary>
        private void AddGroupingSortDescriptions()
        {
            bool originalIgnoreSortDescriptionChanges = _ignoreSortDescriptionsChange;
            _ignoreSortDescriptionsChange = true;
            try
            {
                int insertIndex = 0;
                foreach (GroupDescription groupDescription in Items.GroupDescriptions)
                {
                    PropertyGroupDescription propertyGroupDescription = groupDescription as PropertyGroupDescription;
                    if (CanConvertToSortDescription(propertyGroupDescription))
                    {
                        SortDescription sortDescription = new SortDescription(propertyGroupDescription.PropertyName, ListSortDirection.Ascending);
                        Items.SortDescriptions.Insert(insertIndex, sortDescription);
                        if (GroupingSortDescriptionIndices == null)
                        {
                            GroupingSortDescriptionIndices = new List<int>();
                        }

                        GroupingSortDescriptionIndices.Add(insertIndex++);
                    }
                }
            }
            finally
            {
                _ignoreSortDescriptionsChange = originalIgnoreSortDescriptionChanges;
            }
        }

        /// <summary>
        /// Method to regenrated the SortDescriptions based on the GroupDescriptions
        /// </summary>
        private void RegenerateGroupingSortDescriptions()
        {
            RemoveGroupingSortDescriptions();
            AddGroupingSortDescriptions();
        }

        /// <summary>
        /// CollectionChanged listener for GroupDescriptions of DataGrid.
        /// Regenerates Grouping based sort descriptions is required.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnItemsGroupDescriptionsChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (!_sortingStarted)
            {
                return;
            }

            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    Debug.Assert(e.NewItems.Count == 1, "GroupDescriptionCollection should handle one element at a time");
                    if (CanConvertToSortDescription(e.NewItems[0] as PropertyGroupDescription))
                    {
                        RegenerateGroupingSortDescriptions();
                    }

                    break;
                case NotifyCollectionChangedAction.Remove:
                    Debug.Assert(e.OldItems.Count == 1, "GroupDescriptionCollection should handle one element at a time");
                    if (CanConvertToSortDescription(e.OldItems[0] as PropertyGroupDescription))
                    {
                        RegenerateGroupingSortDescriptions();
                    }

                    break;
                case NotifyCollectionChangedAction.Move:
                    // Do Nothing
                    break;
                case NotifyCollectionChangedAction.Replace:
                    Debug.Assert(e.OldItems.Count == 1 && e.NewItems.Count == 1, "GroupDescriptionCollection should handle one element at a time");
                    if (CanConvertToSortDescription(e.OldItems[0] as PropertyGroupDescription) || 
                        CanConvertToSortDescription(e.NewItems[0] as PropertyGroupDescription))
                    {
                        RegenerateGroupingSortDescriptions();
                    }

                    break;
                case NotifyCollectionChangedAction.Reset:
                    RemoveGroupingSortDescriptions();
                    break;
            }
        }

        #endregion

        #region Column Auto Generation

        /// <summary>
        /// This event will be raised whenever auto generation of columns gets completed
        /// </summary>
        public event EventHandler AutoGeneratedColumns;

        /// <summary>
        /// This event will be raised for each column getting auto generated
        /// </summary>
        public event EventHandler<EGC.DataGridAutoGeneratingColumnEventArgs> AutoGeneratingColumn;

        /// <summary>
        ///     The DependencyProperty that represents the AutoGenerateColumns property.
        /// </summary>
        public static readonly DependencyProperty AutoGenerateColumnsProperty =
            DependencyProperty.Register("AutoGenerateColumns", typeof(bool), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnAutoGenerateColumnsPropertyChanged)));

        /// <summary>
        /// The property which determines whether the columns are to be auto generated or not.
        /// Setting of the property actually generates or deletes columns.
        /// </summary>
        public bool AutoGenerateColumns
        {
            get { return (bool)GetValue(AutoGenerateColumnsProperty); }
            set { SetValue(AutoGenerateColumnsProperty, value); }
        }

        /// <summary>
        /// The polumorphic method which raises the AutoGeneratedColumns event
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnAutoGeneratedColumns(EventArgs e)
        {
            if (AutoGeneratedColumns != null)
            {
                AutoGeneratedColumns(this, e);
            }
        }

        /// <summary>
        /// The polymorphic method which raises the AutoGeneratingColumn event
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnAutoGeneratingColumn(EGC.DataGridAutoGeneratingColumnEventArgs e)
        {
            if (AutoGeneratingColumn != null)
            {
                AutoGeneratingColumn(this, e);
            }
        }

        /// <summary>
        ///     Determines the desired size of the control given a constraint.
        /// </summary>
        /// <remarks>
        ///     On the first measure:
        ///     - Performs auto-generation of columns if needed.
        ///     - Coerces CanUserAddRows and CanUserDeleteRows.
        ///     - Updates the NewItemPlaceholder.
        /// </remarks>
        /// <param name="availableSize">The available space.</param>
        /// <returns>The desired size of the control.</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            if (_measureNeverInvoked)
            {
                _measureNeverInvoked = false;
                if (AutoGenerateColumns)
                {
                    AddAutoColumns();
                }

                InternalColumns.InitializeDisplayIndexMap();

                // FrozenColumns rely on column DisplayIndex
                CoerceValue(FrozenColumnCountProperty);

                // These properties rely on a variety of properties. This is necessary since
                // our default (true) is actually incorrect initially (when ItemsSource is null).
                // So, we delay to this point, in case ItemsSource is never set, to coerce them
                // to their correct values. If ItemsSource did change, then they will have their
                // correct values already and this is extra work.
                CoerceValue(CanUserAddRowsProperty);
                CoerceValue(CanUserDeleteRowsProperty);

                // We need to call this in case CanUserAddRows has remained true (the default value)
                // since startup and no one has set the placeholder position.
                UpdateNewItemPlaceholder(/* isAddingNewItem = */ false);
            }

            return base.MeasureOverride(availableSize);
        }

        /// <summary>
        ///     Coercion callback for ItemsSource property
        /// </summary>
        /// <remarks>
        ///     SortDescriptions and GroupDescriptions are supposed to be
        ///     cleared in PropertyChangedCallback or OnItemsSourceChanged
        ///     virtual. But it seems that the SortDescriptions are applied
        ///     to the new CollectionView due to new ItemsSource in 
        ///     PropertyChangedCallback of base class (which would execute
        ///     before PropertyChangedCallback of this class) and before calling
        ///     OnItemsSourceChanged virtual. Hence handling it in Coercion callback.
        /// </remarks>
        private static object OnCoerceItemsSourceProperty(DependencyObject d, object baseValue)
        {
            EGC.DataGrid dataGrid = (EGC.DataGrid)d;
            if (baseValue != dataGrid._cachedItemsSource && dataGrid._cachedItemsSource != null)
            {
                dataGrid.Items.SortDescriptions.Clear();
                dataGrid.Items.GroupDescriptions.Clear();
            }

            return baseValue;
        }

        /// <summary>
        /// The polymorphic method which gets called whenever the ItemsSource gets changed.
        /// We regenerate columns if required when ItemsSource gets changed.
        /// </summary>
        /// <param name="oldValue"></param>
        /// <param name="newValue"></param>
        protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
        {
            base.OnItemsSourceChanged(oldValue, newValue);

            // ItemsControl calls a ClearValue on ItemsSource property
            // whenever it is set to null. So Coercion is not called
            // in such case. Hence clearing the SortDescriptions and 
            // GroupDescriptions here when new value is null.
            if (newValue == null)
            {
                Items.SortDescriptions.Clear();
                Items.GroupDescriptions.Clear();
            }

            _cachedItemsSource = newValue;

            INotifyCollectionChanged oldNotifyCollection = oldValue as INotifyCollectionChanged;
            if (oldNotifyCollection != null && DeferAutoGeneration)
            {
                oldNotifyCollection.CollectionChanged -= new NotifyCollectionChangedEventHandler(OnItemsSourceCollectionChanged);
            }

            using (UpdateSelectedCells())
            {
                // Selector will try to maintain the previous row selection.
                // Keep SelectedCells in sync.
                _selectedCells.RestoreOnlyFullRows(SelectedItems);
            }

            if (AutoGenerateColumns == true)
            {
                RegenerateAutoColumns();
            }

            InternalColumns.InvalidateColumnWidthsComputation();

            CoerceValue(CanUserAddRowsProperty);
            CoerceValue(CanUserDeleteRowsProperty);

            ResetRowHeaderActualWidth();

            UpdateNewItemPlaceholder(/* isAddingNewItem = */ false);

            HasCellValidationError = false;
            HasRowValidationError = false;
        }

        /// <summary>
        /// Private property to hook and unhook collection changed event on ItemsSources CollectionChanged
        /// event when ever _deferAutoGeneration flag changes.
        /// </summary>
        private bool DeferAutoGeneration
        {
            get
            {
                return _deferAutoGeneration;
            }

            set
            {
                bool oldValue = _deferAutoGeneration;
                _deferAutoGeneration = value;
                if (oldValue != value)
                {
                    INotifyCollectionChanged notifyCollection = ItemsSource as INotifyCollectionChanged;
                    if (notifyCollection != null)
                    {
                        if (value)
                        {
                            notifyCollection.CollectionChanged += new NotifyCollectionChangedEventHandler(OnItemsSourceCollectionChanged);
                        }
                        else
                        {
                            notifyCollection.CollectionChanged -= new NotifyCollectionChangedEventHandler(OnItemsSourceCollectionChanged);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// The event listener to ItemsSource collection changed event which performs deffered auto generation
        /// and also unhooks it self as needed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                AddAutoColumns();
                DeferAutoGeneration = false;
            }
            else if ((e.Action == NotifyCollectionChangedAction.Remove) || (e.Action == NotifyCollectionChangedAction.Replace))
            {
                if (HasRowValidationError || HasCellValidationError)
                {
                    foreach (object item in e.OldItems)
                    {
                        if (IsAddingOrEditingRowItem(item))
                        {
                            HasRowValidationError = false;
                            HasCellValidationError = false;
                            break;
                        }
                    }
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                ResetRowHeaderActualWidth();
                HasRowValidationError = false;
                HasCellValidationError = false;
            }
        }

        /// <summary>
        /// Method which generated auto columns and adds to the data grid.
        /// </summary>
        private void AddAutoColumns()
        {
            if (ItemsSource != null &&
                ItemsSource is INotifyCollectionChanged &&
                DataItemsCount == 0)
            {
                // do deferred generation
                DeferAutoGeneration = true;
            }
            else if (!_measureNeverInvoked)
            {
                EGC.DataGrid.GenerateColumns(
                    (IItemProperties)Items,
                    this,
                    null);
  
                OnAutoGeneratedColumns(EventArgs.Empty);
            }
        }

        /// <summary>
        /// Method which deletes all the auto generated columns.
        /// </summary>
        private void DeleteAutoColumns()
        {
            if (!DeferAutoGeneration && !_measureNeverInvoked)
            {
                for (int columnIndex = Columns.Count - 1; columnIndex >= 0; --columnIndex)
                {
                    if (Columns[columnIndex].IsAutoGenerated)
                    {
                        Columns.RemoveAt(columnIndex);
                    }
                }
            }
            else
            {
                DeferAutoGeneration = false;
            }
        }

        /// <summary>
        /// Method which regenerates the columns for the datagrid
        /// </summary>
        private void RegenerateAutoColumns()
        {
            DeleteAutoColumns();
            AddAutoColumns();
        }

        /// <summary>
        /// Helper method which generates columns for a given IItemProperties
        /// </summary>
        /// <param name="iItemProperties"></param>
        /// <returns></returns>
        public static Collection<EGC.DataGridColumn> GenerateColumns(IItemProperties itemProperties)
        {
            if (itemProperties == null)
            {
                throw new ArgumentNullException("itemProperties");
            }

            Collection<EGC.DataGridColumn> columnCollection = new Collection<EGC.DataGridColumn>();
            EGC.DataGrid.GenerateColumns(
                itemProperties,
                null,
                columnCollection);
            return columnCollection;
        }

        /// <summary>
        /// Helper method which generates columns for a given IItemProperties and adds
        /// them either to a datagrid or to a collection of columns as specified by the flag.
        /// </summary>
        /// <param name="dataGrid"></param>
        /// <param name="iItemProperties"></param>
        /// <param name="columnCollection"></param>
        private static void GenerateColumns(
            IItemProperties iItemProperties,
            EGC.DataGrid dataGrid,
            Collection<EGC.DataGridColumn> columnCollection)
        {
            Debug.Assert(iItemProperties != null, "iItemProperties should not be null");
            Debug.Assert(dataGrid != null || columnCollection != null, "Both dataGrid and columnCollection cannot not be null at the same time");
            
            ReadOnlyCollection<ItemPropertyInfo> itemProperties = iItemProperties.ItemProperties;

            if (itemProperties != null &&
                itemProperties.Count > 0)
            {
                foreach (ItemPropertyInfo itemProperty in itemProperties)
                {
                    EGC.DataGridColumn dataGridColumn = EGC.DataGridColumn.CreateDefaultColumn(itemProperty);

                    if (dataGrid != null)
                    {
                        // AutoGeneratingColumn event is raised before generating and adding column to datagrid
                        // and the column returned by the event handler is used instead of the original column.
                        EGC.DataGridAutoGeneratingColumnEventArgs eventArgs = new EGC.DataGridAutoGeneratingColumnEventArgs(dataGridColumn, itemProperty);
                        dataGrid.OnAutoGeneratingColumn(eventArgs);

                        if (!eventArgs.Cancel && eventArgs.Column != null)
                        {
                            eventArgs.Column.IsAutoGenerated = true;
                            dataGrid.Columns.Add(eventArgs.Column);
                        }
                    }
                    else
                    {
                        columnCollection.Add(dataGridColumn);
                    }
                }
            }
        }

        /// <summary>
        /// The event listener which listens to the change in the AutoGenerateColumns flag
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void OnAutoGenerateColumnsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            bool newValue = (bool)e.NewValue;

            EGC.DataGrid dataGrid = (EGC.DataGrid)d;

            if (newValue)
            {
                dataGrid.AddAutoColumns();
            }
            else
            {
                dataGrid.DeleteAutoColumns();
            }
        }

        #endregion

        #region Frozen Columns

        /// <summary>
        /// Dependency Property fro FrozenColumnCount Property
        /// </summary>
        public static readonly DependencyProperty FrozenColumnCountProperty =
            DependencyProperty.Register(
                "FrozenColumnCount", 
                typeof(int),
                typeof(EGC.DataGrid), 
                new FrameworkPropertyMetadata(0, new PropertyChangedCallback(OnFrozenColumnCountPropertyChanged), new CoerceValueCallback(OnCoerceFrozenColumnCount)),
                new ValidateValueCallback(ValidateFrozenColumnCount));

        /// <summary>
        /// Property which determines the number of columns which are frozen from the beginning in order of display
        /// </summary>
        public int FrozenColumnCount
        {
            get { return (int)GetValue(FrozenColumnCountProperty); }
            set { SetValue(FrozenColumnCountProperty, value); }
        }

        /// <summary>
        /// Coercion call back for FrozenColumnCount property, which ensures that it is never more that column count
        /// </summary>
        /// <param name="d"></param>
        /// <param name="baseValue"></param>
        /// <returns></returns>
        private static object OnCoerceFrozenColumnCount(DependencyObject d, object baseValue)
        {
            EGC.DataGrid dataGrid = (EGC.DataGrid)d;
            int frozenColumnCount = (int)baseValue;

            if (frozenColumnCount > dataGrid.Columns.Count)
            {
                return dataGrid.Columns.Count;
            }

            return baseValue;
        }

        /// <summary>
        /// Property changed callback fro FrozenColumnCount
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void OnFrozenColumnCountPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.ColumnCollection | NotificationTarget.ColumnHeadersPresenter | NotificationTarget.CellsPresenter);
        }

        /// <summary>
        /// Validation call back for frozen column count
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private static bool ValidateFrozenColumnCount(object value)
        {
            int frozenCount = (int)value;
            return frozenCount >= 0;
        }

        /// <summary>
        /// Dependency Property key for NonFrozenColumnsViewportHorizontalOffset Property
        /// </summary>
        private static readonly DependencyPropertyKey NonFrozenColumnsViewportHorizontalOffsetPropertyKey =
                DependencyProperty.RegisterReadOnly(
                        "NonFrozenColumnsViewportHorizontalOffset",
                        typeof(double),
                        typeof(EGC.DataGrid),
                        new FrameworkPropertyMetadata(0.0));

        /// <summary>
        /// Dependency property for NonFrozenColumnsViewportHorizontalOffset Property
        /// </summary>
        public static readonly DependencyProperty NonFrozenColumnsViewportHorizontalOffsetProperty = NonFrozenColumnsViewportHorizontalOffsetPropertyKey.DependencyProperty;

        /// <summary>
        /// Property which gets/sets the start x coordinate of non frozen columns in view port
        /// </summary>
        public double NonFrozenColumnsViewportHorizontalOffset
        {
            get
            {
                return (double)GetValue(NonFrozenColumnsViewportHorizontalOffsetProperty);
            }

            internal set
            {
                SetValue(NonFrozenColumnsViewportHorizontalOffsetPropertyKey, value);
            }
        }

        /// <summary>
        /// Override of OnApplyTemplate which clear the scroll host member
        /// </summary>
        public override void OnApplyTemplate()
        {
            CleanUpInternalScrollControls();
            base.OnApplyTemplate();
        }

        #endregion

        #region Container Virtualization

        /// <summary>
        ///     Property which determines if row virtualization is enabled or disabled
        /// </summary>
        public bool EnableRowVirtualization
        {
            get { return (bool)GetValue(EnableRowVirtualizationProperty); }
            set { SetValue(EnableRowVirtualizationProperty, value); }
        }

        /// <summary>
        ///     Dependency property for EnableRowVirtualization
        /// </summary>
        public static readonly DependencyProperty EnableRowVirtualizationProperty = DependencyProperty.Register(
            "EnableRowVirtualization",
            typeof(bool),
            typeof(EGC.DataGrid),
            new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnEnableRowVirtualizationChanged)));

        /// <summary>
        ///     Property changed callback for EnableRowVirtualization.
        ///     Keeps VirtualizingStackPanel.IsVirtualizingProperty in sync.
        /// </summary>
        private static void OnEnableRowVirtualizationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            EGC.DataGrid dataGrid = (EGC.DataGrid)d;
            dataGrid.CoerceValue(VirtualizingStackPanel.IsVirtualizingProperty);

            Panel itemsHost = dataGrid.InternalItemsHost;
            if (itemsHost != null)
            {
                itemsHost.InvalidateMeasure();
                itemsHost.InvalidateArrange();
            }
        }

        /// <summary>
        ///     Coercion callback for VirtualizingStackPanel.IsVirtualizingProperty
        /// </summary>
        private static object OnCoerceIsVirtualizingProperty(DependencyObject d, object baseValue)
        {
            if (!EGC.DataGridHelper.IsDefaultValue(d, EGC.DataGrid.EnableRowVirtualizationProperty))
            {
                return d.GetValue(EGC.DataGrid.EnableRowVirtualizationProperty);
            }

            return baseValue;
        }

        /// <summary>
        ///     Property which determines if column virtualization is enabled or disabled
        /// </summary>
        public bool EnableColumnVirtualization
        {
            get { return (bool)GetValue(EnableColumnVirtualizationProperty); }
            set { SetValue(EnableColumnVirtualizationProperty, value); }
        }

        /// <summary>
        ///     Dependency property for EnableColumnVirtualization
        /// </summary>
        public static readonly DependencyProperty EnableColumnVirtualizationProperty = DependencyProperty.Register(
            "EnableColumnVirtualization",
            typeof(bool),
            typeof(EGC.DataGrid),
            new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnEnableColumnVirtualizationChanged)));

        /// <summary>
        ///     Property changed callback for EnableColumnVirtualization.
        ///     Gets VirtualizingStackPanel.IsVirtualizingProperty for cells presenter and
        ///     headers presenter in sync.
        /// </summary>
        private static void OnEnableColumnVirtualizationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.CellsPresenter | NotificationTarget.ColumnHeadersPresenter | NotificationTarget.ColumnCollection);
        }

        #endregion

        #region Column Reordering

        /// <summary>
        /// Dependency Property for CanUserReorderColumns Property
        /// </summary>
        public static readonly DependencyProperty CanUserReorderColumnsProperty =
            DependencyProperty.Register("CanUserReorderColumns", typeof(bool), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(true));

        /// <summary>
        /// The property which determines if an end user can re-order columns or not.
        /// </summary>
        public bool CanUserReorderColumns
        {
            get { return (bool)GetValue(CanUserReorderColumnsProperty); }
            set { SetValue(CanUserReorderColumnsProperty, value); }
        }

        /// <summary>
        /// Dependency Property for DragIndicatorStyle property
        /// </summary>
        public static readonly DependencyProperty DragIndicatorStyleProperty =
            DependencyProperty.Register("DragIndicatorStyle", typeof(Style), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null, OnNotifyColumnPropertyChanged));

        /// <summary>
        /// The style property which would be applied on the column header drag indicator
        /// </summary>
        public Style DragIndicatorStyle
        {
            get { return (Style)GetValue(DragIndicatorStyleProperty); }
            set { SetValue(DragIndicatorStyleProperty, value); }
        }

        /// <summary>
        /// Dependency Property for DropLocationIndicatorStyle property
        /// </summary>
        public static readonly DependencyProperty DropLocationIndicatorStyleProperty =
            DependencyProperty.Register("DropLocationIndicatorStyle", typeof(Style), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(null));

        /// <summary>
        /// The style property which would be applied on the column header drop location indicator.
        /// </summary>
        public Style DropLocationIndicatorStyle
        {
            get { return (Style)GetValue(DropLocationIndicatorStyleProperty); }
            set { SetValue(DropLocationIndicatorStyleProperty, value); }
        }

        public event EventHandler<EGC.DataGridColumnReorderingEventArgs> ColumnReordering;

        public event EventHandler<DragStartedEventArgs> ColumnHeaderDragStarted;

        public event EventHandler<DragDeltaEventArgs> ColumnHeaderDragDelta;

        public event EventHandler<DragCompletedEventArgs> ColumnHeaderDragCompleted;

        public event EventHandler<EGC.DataGridColumnEventArgs> ColumnReordered;

        protected internal virtual void OnColumnHeaderDragStarted(DragStartedEventArgs e)
        {
            if (ColumnHeaderDragStarted != null)
            {
                ColumnHeaderDragStarted(this, e);
            }
        }

        protected internal virtual void OnColumnReordering(EGC.DataGridColumnReorderingEventArgs e)
        {
            if (ColumnReordering != null)
            {
                ColumnReordering(this, e);
            }
        }

        protected internal virtual void OnColumnHeaderDragDelta(DragDeltaEventArgs e)
        {
            if (ColumnHeaderDragDelta != null)
            {
                ColumnHeaderDragDelta(this, e);
            }
        }

        protected internal virtual void OnColumnHeaderDragCompleted(DragCompletedEventArgs e)
        {
            if (ColumnHeaderDragCompleted != null)
            {
                ColumnHeaderDragCompleted(this, e);
            }
        }

        protected internal virtual void OnColumnReordered(EGC.DataGridColumnEventArgs e)
        {
            if (ColumnReordered != null)
            {
                ColumnReordered(this, e);
            }
        }

        #endregion

        #region Clipboard Copy

        /// <summary>
        ///     The DependencyProperty that represents the ClipboardCopyMode property.
        /// </summary>
        public static readonly DependencyProperty ClipboardCopyModeProperty =
            DependencyProperty.Register("ClipboardCopyMode", typeof(EGC.DataGridClipboardCopyMode), typeof(EGC.DataGrid), new FrameworkPropertyMetadata(EGC.DataGridClipboardCopyMode.ExcludeHeader, new PropertyChangedCallback(OnClipboardCopyModeChanged)));

        private static void OnClipboardCopyModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // The Copy command needs to have CanExecute run
            CommandManager.InvalidateRequerySuggested();
        }

        /// <summary>
        /// The property which determines how DataGrid content is copied to the Clipboard.
        /// </summary>
        public EGC.DataGridClipboardCopyMode ClipboardCopyMode
        {
            get { return (EGC.DataGridClipboardCopyMode)GetValue(ClipboardCopyModeProperty); }
            set { SetValue(ClipboardCopyModeProperty, value); }
        }

        private static void OnCanExecuteCopy(object target, CanExecuteRoutedEventArgs args)
        {
            ((EGC.DataGrid)target).OnCanExecuteCopy(args);
        }

        /// <summary>
        /// This virtual method is called when ApplicationCommands.Copy command query its state.
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnCanExecuteCopy(CanExecuteRoutedEventArgs args)
        {
            args.CanExecute = ClipboardCopyMode != EGC.DataGridClipboardCopyMode.None && _selectedCells.Count > 0;
            args.Handled = true;
        }

        private static void OnExecutedCopy(object target, ExecutedRoutedEventArgs args)
        {
            ((EGC.DataGrid)target).OnExecutedCopy(args);
        }

        /// <summary>
        /// This virtual method is called when ApplicationCommands.Copy command is executed.
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnExecutedCopy(ExecutedRoutedEventArgs args)
        {
            if (ClipboardCopyMode == EGC.DataGridClipboardCopyMode.None)
            {
                throw new NotSupportedException(SR.Get(SRID.ClipboardCopyMode_Disabled));
            }

            args.Handled = true;

            // Supported default formats: Html, Text, UnicodeText and CSV
            Collection<string> formats = new Collection<string>(new string[] { DataFormats.Html, DataFormats.Text, DataFormats.UnicodeText, DataFormats.CommaSeparatedValue });
            Dictionary<string, StringBuilder> dataGridStringBuilders = new Dictionary<string, StringBuilder>(formats.Count);
            foreach (string format in formats)
            {
                dataGridStringBuilders[format] = new StringBuilder();
            }

            int minRowIndex;
            int maxRowIndex;
            int minColumnDisplayIndex;
            int maxColumnDisplayIndex;

            // Get the bounding box of the selected cells
            if (_selectedCells.GetSelectionRange(out minColumnDisplayIndex, out maxColumnDisplayIndex, out minRowIndex, out maxRowIndex))
            {
                // Add column headers if enabled
                if (ClipboardCopyMode == EGC.DataGridClipboardCopyMode.IncludeHeader)
                {
                    EGC.DataGridRowClipboardEventArgs preparingRowClipboardContentEventArgs = new EGC.DataGridRowClipboardEventArgs(null, minColumnDisplayIndex, maxColumnDisplayIndex, true /*IsColumnHeadersRow*/);
                    OnCopyingRowClipboardContent(preparingRowClipboardContentEventArgs);

                    foreach (string format in formats)
                    {
                        dataGridStringBuilders[format].Append(preparingRowClipboardContentEventArgs.FormatClipboardCellValues(format));
                    }
                }

                // Add each selected row
                for (int i = minRowIndex; i <= maxRowIndex; i++)
                {
                    object row = Items[i];

                    // Row has a selecion
                    if (_selectedCells.Intersects(i)) 
                    {
                        EGC.DataGridRowClipboardEventArgs preparingRowClipboardContentEventArgs = new EGC.DataGridRowClipboardEventArgs(row, minColumnDisplayIndex, maxColumnDisplayIndex, false /*IsColumnHeadersRow*/, i);
                        OnCopyingRowClipboardContent(preparingRowClipboardContentEventArgs);

                        foreach (string format in formats)
                        {
                            dataGridStringBuilders[format].Append(preparingRowClipboardContentEventArgs.FormatClipboardCellValues(format));
                        }
                    }
                }
            }

            EGC.ClipboardHelper.GetClipboardContentForHtml(dataGridStringBuilders[DataFormats.Html]);

            try
            {
                DataObject dataObject = new DataObject();
                foreach (string format in formats)
                {
                    dataObject.SetData(format, dataGridStringBuilders[format].ToString(), false /*autoConvert*/);
                }

                Clipboard.SetDataObject(dataObject);
            }
            catch (SecurityException)
            {
                // In partial trust we will have a security exception because clipboard operations require elevated permissions
                // Bug: Once the security team fix Clipboard.SetText - we can remove this catch
                // Temp: Use TextBox.Copy to have at least Text format in the clipboard
                TextBox textBox = new TextBox();
                textBox.Text = dataGridStringBuilders[DataFormats.Text].ToString();
                textBox.SelectAll();
                textBox.Copy();
            }
        }

        /// <summary>
        /// This method is called to prepare the clipboard content for each selected row.
        /// If ClipboardCopyMode is set to ClipboardCopyMode, then it is also called to prepare the column headers
        /// </summary>
        /// <param name="args">Contains the necessary information for generating the row clipboard content.</param>
        protected virtual void OnCopyingRowClipboardContent(EGC.DataGridRowClipboardEventArgs args)
        {
            if (args.IsColumnHeadersRow)
            {
                for (int i = args.StartColumnDisplayIndex; i <= args.EndColumnDisplayIndex; i++)
                {
                    EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
                    if (!column.IsVisible)
                    {
                        continue;
                    }

                    args.ClipboardRowContent.Add(new EGC.DataGridClipboardCellContent(args.Item, column, column.Header));
                }
            }
            else
            {
                int rowIndex = args.RowIndexHint;
                if (rowIndex < 0)
                {
                    rowIndex = Items.IndexOf(args.Item);
                }

                // If row has selection
                if (_selectedCells.Intersects(rowIndex)) 
                {
                    for (int i = args.StartColumnDisplayIndex; i <= args.EndColumnDisplayIndex; i++)
                    {
                        EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
                        if (!column.IsVisible)
                        {
                            continue;
                        }

                        object cellValue = null;

                        // Get cell value only if the cell is selected - otherwise leave it null
                        if (_selectedCells.Contains(rowIndex, i))
                        {
                            cellValue = column.OnCopyingCellClipboardContent(args.Item);
                        }

                        args.ClipboardRowContent.Add(new EGC.DataGridClipboardCellContent(args.Item, column, cellValue));
                    }
                }
            }

            // Raise the event to give a chance to external listeners to modify row clipboard content (e.ClipboardRow)
            if (CopyingRowClipboardContent != null)
            {
                CopyingRowClipboardContent(this, args);
            }
        }

        /// <summary>
        /// This event is raised by OnCopyingRowClipboardContent method after the default row content is prepared.
        /// Event listeners can modify or add to the row clipboard content
        /// </summary>
        public event EventHandler<EGC.DataGridRowClipboardEventArgs> CopyingRowClipboardContent;
        #endregion

        #region Cells Panel Width

        /// <summary>
        /// Dependency Property for CellsPanelActualWidth property
        /// </summary>
        internal static readonly DependencyProperty CellsPanelActualWidthProperty =
            DependencyProperty.Register(
                        "CellsPanelActualWidth", 
                        typeof(double),
                        typeof(EGC.DataGrid), 
                        new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(CellsPanelActualWidthChanged)));

        /// <summary>
        /// The property which represents the actual width of the cells panel,
        /// to be used by headers presenter
        /// </summary>
        internal double CellsPanelActualWidth
        {
            get
            {
                return (double)GetValue(CellsPanelActualWidthProperty);
            }

            set
            {
                SetValue(CellsPanelActualWidthProperty, value);
            }
        }

        /// <summary>
        /// Property changed callback for CellsPanelActualWidth property
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void CellsPanelActualWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            double oldValue = (double)e.OldValue;
            double newValue = (double)e.NewValue;
            if (!DoubleUtil.AreClose(oldValue, newValue))
            {
                ((EGC.DataGrid)d).NotifyPropertyChanged(d, e, NotificationTarget.ColumnHeadersPresenter);
            }
        }

        #endregion

        #region Column Width Computations

        /// <summary>
        ///     Dependency Property Key for CellsPanelHorizontalOffset property
        /// </summary>
        private static readonly DependencyPropertyKey CellsPanelHorizontalOffsetPropertyKey =
            DependencyProperty.RegisterReadOnly(
                "CellsPanelHorizontalOffset", 
                typeof(double),
                typeof(EGC.DataGrid),
                new FrameworkPropertyMetadata(0d, OnNotifyHorizontalOffsetPropertyChanged));

        /// <summary>
        ///     Dependency Property for CellsPanelHorizontalOffset
        /// </summary>
        public static readonly DependencyProperty CellsPanelHorizontalOffsetProperty = CellsPanelHorizontalOffsetPropertyKey.DependencyProperty;

        /// <summary>
        ///     Property which caches the cells panel horizontal offset
        /// </summary>
        public double CellsPanelHorizontalOffset
        {
            get { return (double)GetValue(CellsPanelHorizontalOffsetProperty); }
            private set { SetValue(CellsPanelHorizontalOffsetPropertyKey, value); }
        }

        /// <summary>
        ///     Property which indicates whether a request to
        ///     invalidate CellsPanelOffset is already in queue or not.
        /// </summary>
        private bool CellsPanelHorizontalOffsetComputationPending
        {
            get;
            set;
        }

        /// <summary>
        ///     Helper method which queue a request to dispatcher to
        ///     invalidate the cellspanel offset if not already queued
        /// </summary>
        internal void QueueInvalidateCellsPanelHorizontalOffset()
        {
            if (!CellsPanelHorizontalOffsetComputationPending)
            {
                Dispatcher.BeginInvoke(new DispatcherOperationCallback(InvalidateCellsPanelHorizontalOffset), DispatcherPriority.Loaded, this);
                CellsPanelHorizontalOffsetComputationPending = true;
            }
        }

        /// <summary>
        ///     Dispatcher call back method which recomputes the CellsPanelOffset
        /// </summary>
        private object InvalidateCellsPanelHorizontalOffset(object args)
        {
            if (!CellsPanelHorizontalOffsetComputationPending)
            {
                return null;
            }

            EGC.IProvideDataGridColumn cell = GetAnyCellOrColumnHeader();
            if (cell != null)
            {
                CellsPanelHorizontalOffset = EGC.DataGridHelper.GetParentCellsPanelHorizontalOffset(cell);
            }

            CellsPanelHorizontalOffsetComputationPending = false;
            return null;
        }

        /// <summary>
        /// Helper method which return any one of the cells or column headers
        /// </summary>
        /// <returns></returns>
        internal EGC.IProvideDataGridColumn GetAnyCellOrColumnHeader()
        {
            if (_rowTrackingRoot != null)
            {
                EGC.DataGridRow row = _rowTrackingRoot.Container;
                EGC.DataGridCellsPresenter cellsPresenter = row.CellsPresenter;
                if (cellsPresenter != null)
                {
                    EGC.ContainerTracking<EGC.DataGridCell> cellTracker = cellsPresenter.CellTrackingRoot;
                    if (cellTracker != null)
                    {
                        return cellTracker.Container;
                    }
                }
            }

            if (ColumnHeadersPresenter != null)
            {
                EGC.ContainerTracking<EGC.DataGridColumnHeader> headerTracker = ColumnHeadersPresenter.HeaderTrackingRoot;
                if (headerTracker != null)
                {
                    return headerTracker.Container;
                }
            }

            return null;
        }

        /// <summary>
        /// Helper method which returns the width of the viewport which is available for the columns to render
        /// </summary>
        /// <returns></returns>
        internal double GetViewportWidthForColumns()
        {
            if (InternalScrollHost == null)
            {
                return 0.0;
            }

            double totalAvailableWidth = InternalScrollHost.ViewportWidth;
            totalAvailableWidth -= CellsPanelHorizontalOffset;
            return totalAvailableWidth;
        }

        #endregion

        #region Helpers

        // TODO: Consider making this public.
        // Used as an alternate data item to CollectionView.NewItemPlaceholder so that
        // CellsPresenter's ItemContainerGenerator does not get confused.
        internal static object NewItemPlaceholder
        {
            get { return _newItemPlaceholder; }
        }

        #endregion

        #region Data

        private static ComponentResourceKey _focusBorderBrushKey;           // Used in styles
        private static IValueConverter _headersVisibilityConverter;         // Used to convert DataGridHeadersVisibility to Visibility in styles
        private static IValueConverter _rowDetailsScrollingConverter;       // Used to convert boolean (DataGrid.RowDetailsAreFrozen) into a SelectiveScrollingMode
        private static object _newItemPlaceholder = new object();           // Used as an alternate data item to CollectionView.NewItemPlaceholder

        private EGC.DataGridColumnCollection _columns;                          // Stores the columns
        private EGC.ContainerTracking<EGC.DataGridRow> _rowTrackingRoot;            // Root of a linked list of active row containers
        private EGC.DataGridColumnHeadersPresenter _columnHeadersPresenter;     // headers presenter for sending down notifications
        private EGC.DataGridCell _currentCellContainer;                         // Reference to the cell container corresponding to CurrentCell (use CurrentCellContainer property instead)
        private EGC.DataGridCell _pendingCurrentCellContainer;                  // Reference to the cell container that will become the current cell
        private EGC.SelectedCellsCollection _selectedCells;                     // Stores the selected cells
        protected Nullable<EGC.DataGridCellInfo> _selectionAnchor;                // For doing extended selection
        private bool _isDraggingSelection;                                  // Whether a drag select is being performed
        private bool _isRowDragging;                                        // Whether a drag select is being done on rows
        private Panel _internalItemsHost;                                   // Workaround for not having access to ItemsHost
        protected ScrollViewer _internalScrollHost;                           // Scroll viewer of the datagrid
        private ScrollContentPresenter _internalScrollContentPresenter = null; // Scroll Content Presenter of DataGrid's ScrollViewer
        private DispatcherTimer _autoScrollTimer;                           // Timer to tick auto-scroll
        private bool _hasAutoScrolled;                                      // Whether an auto-scroll has occurred since starting the tick
        private EGC.VirtualizedCellInfoCollection _pendingSelectedCells;        // Cells that were selected that haven't gone through SelectedCellsChanged
        private EGC.VirtualizedCellInfoCollection _pendingUnselectedCells;      // Cells that were unselected that haven't gone through SelectedCellsChanged
        private bool _deferAutoGeneration = false;                          // The flag which determines whether the columns generation is deferred
        private bool _measureNeverInvoked = true;                           // Flag used to track if measure was invoked atleast once. Particularly used for AutoGeneration.
        private bool _updatingSelectedCells = false;                        // Whether to defer notifying that SelectedCells changed.
        private Visibility _placeholderVisibility = Visibility.Collapsed;   // The visibility used for the Placeholder container.  It may not exist at all times, so it's stored on the DG.
        private Point _dragPoint;                                           // Used to detect if a drag actually occurred
        private List<int> _groupingSortDescriptionIndices = null;           // List to hold the indices of SortDescriptions added for the sake of GroupDescriptions.
        private bool _ignoreSortDescriptionsChange = false;                 // Flag used to neglect the SortDescriptionCollection changes in the CollectionChanged listener.
        private bool _sortingStarted = false;                               // Flag used to track if Sorting ever started or not.
        private ObservableCollection<ValidationRule> _rowValidationRules;   // Stores the row ValidationRule's
        private BindingGroup _rowValidationBindingGroup;                    // Cached copy of the BindingGroup created for row validation...so we dont stomp on user set ItemBindingGroup
        private object _editingRowItem = null;                              // Current editing row item
        private int _editingRowIndex = -1;                                  // Current editing row index
        private bool _hasCellValidationError;                               // An unsuccessful cell commit occurred
        private bool _hasRowValidationError;                                // An unsuccessful row commit occurred
        private IEnumerable _cachedItemsSource = null;                      // Reference to the ItemsSource instance, used to clear SortDescriptions on ItemsSource change
        private EGC.DataGridItemAttachedStorage _itemAttachedStorage = new EGC.DataGridItemAttachedStorage(); // Holds data about the items that's need for row virtualization
        private bool _viewportWidthChangeNotificationPending = false;       // Flag to indicate if a viewport width change reuest is already queued.
        private double _originalViewportWidth = 0.0;                        // Holds the original viewport width between multiple viewport width changes
        private double _finalViewportWidth = 0.0;                           // Holds the final viewport width between multiple viewport width changes

        #endregion

        #region Constants
        private const string ItemsPanelPartName = "PART_RowsPresenter";
        #endregion
    }

    internal class NativeMethods
    {
        // Private constructor for performance only
        private NativeMethods()
        {
        }

        // Used for AutoScrollTimeout
        [DllImport("User32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
        internal static extern int GetDoubleClickTime();
    }
}
