File: System\Windows\Controls\DataGrid.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
 
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.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using MS.Internal;
using MS.Internal.Data;
using MS.Internal.Telemetry.PresentationFramework;
 
namespace System.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(DataGrid);
 
            DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(typeof(DataGrid)));
            FrameworkElementFactory dataGridRowPresenterFactory = new FrameworkElementFactory(typeof(DataGridRowsPresenter));
            dataGridRowPresenterFactory.SetValue(FrameworkElement.NameProperty, ItemsPanelPartName);
            ItemsPanelProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new ItemsPanelTemplate(dataGridRowPresenterFactory)));
            VirtualizingPanel.IsVirtualizingProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(true, null, new CoerceValueCallback(OnCoerceIsVirtualizingProperty)));
            VirtualizingPanel.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)));
            IsKeyboardFocusWithinPropertyKey.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnIsKeyboardFocusWithinChanged)));
            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.RegisterClassCommandBinding(ownerType, new CommandBinding(SelectAllCommand, new ExecutedRoutedEventHandler(OnExecutedSelectAll), new CanExecuteRoutedEventHandler(OnCanExecuteSelectAll)));
 
            CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(DeleteCommand, new ExecutedRoutedEventHandler(OnExecutedDelete), new CanExecuteRoutedEventHandler(OnCanExecuteDelete)));
 
            // Default Clipboard handling
            CommandManager.RegisterClassCommandBinding(typeof(DataGrid), new CommandBinding(ApplicationCommands.Copy, new ExecutedRoutedEventHandler(OnExecutedCopy), new CanExecuteRoutedEventHandler(OnCanExecuteCopy)));
 
            EventManager.RegisterClassHandler(typeof(DataGrid), MouseUpEvent, new MouseButtonEventHandler(OnAnyMouseUpThunk), true);
 
            ControlsTraceLogger.AddControl(TelemetryControls.DataGrid);
        }
 
        /// <summary>
        ///     Instantiates a new instance of this class.
        /// </summary>
        public DataGrid()
        {
            _columns = new DataGridColumnCollection(this);
            _columns.CollectionChanged += new NotifyCollectionChangedEventHandler(OnColumnsChanged);
 
            _rowValidationRules = new ObservableCollection<ValidationRule>();
            _rowValidationRules.CollectionChanged += new NotifyCollectionChangedEventHandler(OnRowValidationRulesChanged);
 
            _selectedCells = new 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<DataGridColumn> Columns
        {
            get { return _columns; }
        }
 
        /// <summary>
        ///     Returns the column collection without having to upcast from ObservableCollection
        /// </summary>
        internal 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(DataGrid), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnNotifyColumnAndColumnHeaderPropertyChanged)));
 
        /// <summary>
        ///     Specifies the width of the header and cells within all the columns.
        /// </summary>
        public DataGridLength ColumnWidth
        {
            get { return (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(DataGridLength), typeof(DataGrid), new FrameworkPropertyMetadata(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(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(DataGrid),
                new FrameworkPropertyMetadata(double.PositiveInfinity, new PropertyChangedCallback(OnColumnSizeConstraintChanged)),
                new ValidateValueCallback(ValidateMaxColumnWidth));
 
        private static void OnColumnSizeConstraintChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.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 || double.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 || double.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++)
            {
                DataGridColumn column = (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 = (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 (DataGridColumn column in columns)
                {
                    if (column.IsVisible)
                    {
                        return true;
                    }
                }
            }
 
            return false;
        }
 
        private static readonly UncommonField<int> BringColumnIntoViewRetryCountField
            = new UncommonField<int>(0);
        const int MaxBringColumnIntoViewRetries = 4;
 
        /// <summary>
        ///     Called from DataGridCellsPanel.BringIndexIntoView to request a
        ///     retry, to handle column width changes due to deferred data binding.
        ///     To prevent infinite loops, simply give up after a fixed number
        ///     of retries, leaving the desired column possibly out of view.
        ///     (One retry was enough to bring the column into view
        ///     in all the cases we tested - two if you include the "layout demoted"
        ///     scenario described in BringIndexIntoView.  More complex data could
        ///     need more retries.  In theory there is no upper bound, but in practice
        ///     the max declared here should suffice.)
        /// </summary>
        internal bool RetryBringColumnIntoView(bool retryRequested)
        {
            if (retryRequested)
            {
                // if the number of retries hasn't exceeded the limit,
                // update the count and allow the retry
                int retries = BringColumnIntoViewRetryCountField.GetValue(this);
                if (retries < MaxBringColumnIntoViewRetries)
                {
                    BringColumnIntoViewRetryCountField.SetValue(this, retries + 1);
                    return true;
                }
            }
 
            // we're not going to retry
            BringColumnIntoViewRetryCountField.ClearValue(this);
            return false;
        }
 
        #endregion
 
        #region Display Index
 
        /// <summary>
        ///     Returns the DataGridColumn with the given DisplayIndex
        /// </summary>
        public DataGridColumn ColumnFromDisplayIndex(int displayIndex)
        {
            if (displayIndex < 0 || displayIndex >= Columns.Count)
            {
                throw new ArgumentOutOfRangeException("displayIndex", displayIndex, SR.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<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(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(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 DataGridColumnHeader ColumnHeaderFromDisplayIndex(int displayIndex)
        {
            int columnIndex = ColumnIndexFromDisplayIndex(displayIndex);
 
            if (columnIndex != -1)
            {
                if (ColumnHeadersPresenter != null && ColumnHeadersPresenter.ItemContainerGenerator != null)
                {
                    return (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)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.CellsPresenter);
        }
 
        /// <summary>
        ///     Notifies each Column and Cell about property changes.
        /// </summary>
        private static void OnNotifyColumnAndCellPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Columns | DataGridNotificationTarget.Cells);
        }
 
        /// <summary>
        ///     Notifies each Column about property changes.
        /// </summary>
        private static void OnNotifyColumnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Columns);
        }
 
        /// <summary>
        ///     Notifies the Column & Column Headers about property changes.
        /// </summary>
        private static void OnNotifyColumnAndColumnHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Columns | DataGridNotificationTarget.ColumnHeaders);
        }
 
        /// <summary>
        ///     Notifies the Column Headers about property changes.
        /// </summary>
        private static void OnNotifyColumnHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.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)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.ColumnHeaders | DataGridNotificationTarget.RowHeaders);
        }
 
        /// <summary>
        ///     Notifies the DataGrid and each Row about property changes.
        /// </summary>
        private static void OnNotifyDataGridAndRowPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Rows | DataGridNotificationTarget.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)
            {
                ((DataGrid)d).OnItemTemplateChanged(null, null);
            }
        }
 
        /// <summary>
        ///     Notifies each Row about property changes.
        /// </summary>
        private static void OnNotifyRowPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Rows);
        }
 
        /// <summary>
        ///     Notifies the Row Headers about property changes.
        /// </summary>
        private static void OnNotifyRowHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.RowHeaders);
        }
 
        /// <summary>
        ///     Notifies the Row & Row Headers about property changes.
        /// </summary>
        private static void OnNotifyRowAndRowHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Rows | DataGridNotificationTarget.RowHeaders);
        }
 
        /// <summary>
        ///     Notifies the Row & Details about property changes.
        /// </summary>
        private static void OnNotifyRowAndDetailsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Rows | DataGridNotificationTarget.DetailsPresenter);
        }
 
        /// <summary>
        ///     Notifies HorizontalOffset change to columns collection, cellspresenter and column headers presenter
        /// </summary>
        private static void OnNotifyHorizontalOffsetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.ColumnCollection | DataGridNotificationTarget.CellsPresenter | DataGridNotificationTarget.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, DataGridNotificationTarget 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, DataGridNotificationTarget target)
        {
            if (DataGridHelper.ShouldNotifyDataGrid(target))
            {
                if (e.Property == AlternatingRowBackgroundProperty)
                {
                    // If the alternate row background is set, the count may be coerced to 2
                    CoerceValue(AlternationCountProperty);
                }
                else if ((e.Property == DataGridColumn.VisibilityProperty) || (e.Property == DataGridColumn.WidthProperty) || (e.Property == DataGridColumn.DisplayIndexProperty))
                {
                    // DataGridCellsPanel needs to be re-measured when column visibility changes
                    // Recyclable containers may not be fully remeasured when they are brought in
                    foreach (DependencyObject container in ItemContainerGenerator.RecyclableContainers)
                    {
                        DataGridRow row = container as DataGridRow;
                        if (row != null)
                        {
                            var cellsPresenter = row.CellsPresenter;
                            if (cellsPresenter != null)
                            {
                                cellsPresenter.InvalidateDataGridCellsPanelMeasureAndArrange();
                            }
                        }
                    }
                }
            }
 
            // Rows, Cells, CellsPresenter, DetailsPresenter or RowHeaders
            if (DataGridHelper.ShouldNotifyRowSubtree(target))
            {
                // Notify the Rows about the property change
                ContainerTracking<DataGridRow> tracker = _rowTrackingRoot;
                while (tracker != null)
                {
                    tracker.Container.NotifyPropertyChanged(d, propertyName, e, target);
                    tracker = tracker.Next;
                }
            }
 
            if (DataGridHelper.ShouldNotifyColumnCollection(target) || DataGridHelper.ShouldNotifyColumns(target))
            {
                InternalColumns.NotifyPropertyChanged(d, propertyName, e, target);
            }
 
            if ((DataGridHelper.ShouldNotifyColumnHeadersPresenter(target) || 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, 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 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;
        }
 
        #endregion
 
        #region GridLines
 
        /// <summary>
        ///     GridLinesVisibility Dependency Property
        /// </summary>
        public static readonly DependencyProperty GridLinesVisibilityProperty =
            DependencyProperty.Register(
                "GridLinesVisibility",
                typeof(DataGridGridLinesVisibility),
                typeof(DataGrid),
                new FrameworkPropertyMetadata(DataGridGridLinesVisibility.All, new PropertyChangedCallback(OnNotifyGridLinePropertyChanged)));
 
        /// <summary>
        ///     Specifies the visibility of the DataGrid's grid lines
        /// </summary>
        public DataGridGridLinesVisibility GridLinesVisibility
        {
            get { return (DataGridGridLinesVisibility)GetValue(GridLinesVisibilityProperty); }
            set { SetValue(GridLinesVisibilityProperty, value); }
        }
 
        /// <summary>
        /// HorizontalGridLinesBrush Dependency Property
        /// </summary>
        public static readonly DependencyProperty HorizontalGridLinesBrushProperty =
            DependencyProperty.Register(
                "HorizontalGridLinesBrush",
                typeof(Brush),
                typeof(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(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(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(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 DataGridRow;
        }
 
        /// <summary>
        ///     Instantiates an instance of a container.
        /// </summary>
        /// <returns>A new DataGridRow.</returns>
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new 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);
 
            DataGridRow row = (DataGridRow)element;
            if (row.DataGridOwner != this)
            {
                row.Tracker.StartTracking(ref _rowTrackingRoot);
                if (item == CollectionView.NewItemPlaceholder ||
                    (IsAddingNewItem && item == EditableItems.CurrentAddItem))
                {
                    row.IsNewItem = true;
                }
                else
                {
                    row.ClearValue(DataGridRow.IsNewItemPropertyKey);
                }
                EnsureInternalScrollControls();
                EnqueueNewItemMarginComputation();
            }
 
            row.PrepareRow(item, this);
            OnLoadingRow(new 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);
 
            DataGridRow row = (DataGridRow)element;
            if (row.DataGridOwner == this)
            {
                row.Tracker.StopTracking(ref _rowTrackingRoot);
                row.ClearValue(DataGridRow.IsNewItemPropertyKey);
                EnqueueNewItemMarginComputation();
            }
 
            OnUnloadingRow(new 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)
        {
            ContainerTracking<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(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 (!DataGridHelper.IsDefaultValue(d, DataGrid.RowStyleProperty))
            {
                return d.GetValue(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(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)
        {
            EnsureItemBindingGroup();
 
            // only update the ItemBindingGroup if it's not user created.
            if (_defaultBindingGroup != null)
            {
                if (object.ReferenceEquals(ItemBindingGroup, _defaultBindingGroup))
                {
                    switch (e.Action)
                    {
                        case NotifyCollectionChangedAction.Add:
                            foreach (ValidationRule rule in e.NewItems)
                            {
                                _defaultBindingGroup.ValidationRules.Add(rule);
                            }
 
                            break;
 
                        case NotifyCollectionChangedAction.Remove:
                            foreach (ValidationRule rule in e.OldItems)
                            {
                                _defaultBindingGroup.ValidationRules.Remove(rule);
                            }
 
                            break;
 
                        case NotifyCollectionChangedAction.Replace:
                            foreach (ValidationRule rule in e.OldItems)
                            {
                                _defaultBindingGroup.ValidationRules.Remove(rule);
                            }
 
                            foreach (ValidationRule rule in e.NewItems)
                            {
                                _defaultBindingGroup.ValidationRules.Add(rule);
                            }
 
                            break;
 
                        case NotifyCollectionChangedAction.Reset:
                            _defaultBindingGroup.ValidationRules.Clear();
                            break;
                    }
                }
                else
                {
                    _defaultBindingGroup = 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(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 (!DataGridHelper.IsDefaultValue(d, DataGrid.RowStyleSelectorProperty))
            {
                return d.GetValue(DataGrid.RowStyleSelectorProperty);
            }
 
            return baseValue;
        }
 
        private static object OnCoerceIsSynchronizedWithCurrentItem(DependencyObject d, object baseValue)
        {
            DataGrid dataGrid = (DataGrid)d;
            if (dataGrid.SelectionUnit == 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(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(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)
            {
                DataGrid dataGrid = (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(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(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<DataGridRowEventArgs> LoadingRow;
 
        /// <summary>
        ///     Event that is fired just before a row is cleared.
        /// </summary>
        public event EventHandler<DataGridRowEventArgs> UnloadingRow;
 
        /// <summary>
        ///     Invokes the LoadingRow event
        /// </summary>
        protected virtual void OnLoadingRow(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).
                Dispatcher.CurrentDispatcher.BeginInvoke(new DispatcherOperationCallback(DelayedOnLoadingRowDetails), DispatcherPriority.Loaded, row);
            }
        }
 
        internal static object DelayedOnLoadingRowDetails(object arg)
        {
            var row = (DataGridRow)arg;
            var dataGrid = row.DataGridOwner;
 
            if (dataGrid != null)
            {
                dataGrid.OnLoadingRowDetailsWrapper(row);
            }
 
            return null;
        }
 
        /// <summary>
        ///     Invokes the UnloadingRow event
        /// </summary>
        protected virtual void OnUnloadingRow(DataGridRowEventArgs e)
        {
            if (UnloadingRow != null)
            {
                UnloadingRow(this, e);
            }
 
            var row = e.Row;
            OnUnloadingRowDetailsWrapper(row);
        }
 
        #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(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(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(DataGrid), new FrameworkPropertyMetadata(double.NaN, OnNotifyColumnHeaderPropertyChanged));
 
        /// <summary>
        ///     A property that specifies the visibility of the column & row headers.
        /// </summary>
        public DataGridHeadersVisibility HeadersVisibility
        {
            get { return (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(DataGridHeadersVisibility), typeof(DataGrid), new FrameworkPropertyMetadata(DataGridHeadersVisibility.All));
 
        /// <summary>
        ///     Updates RowHeaderActualWidth to reflect changes to RowHeaderWidth
        /// </summary>
        private static void OnNotifyRowHeaderWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var dataGrid = ((DataGrid)d);
            var newValue = (double)e.NewValue;
 
            if (!double.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 (double.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, DataGridRow.DetailsVisibilityProperty, detailsVisibility);
 
            var row = (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, DataGridRow.DetailsVisibilityProperty, out detailsVisibility))
            {
                return (Visibility)detailsVisibility;
            }
 
            var row = (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 DataGridRowDetailsVisibilityMode.VisibleWhenSelected:
                    return SelectedItems.Contains(item) ? Visibility.Visible : Visibility.Collapsed;
                case 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, DataGridRow.DetailsVisibilityProperty);
 
            var row = (DataGridRow)ItemContainerGenerator.ContainerFromItem(item);
            if (row != null)
            {
                row.ClearValue(DataGridRow.DetailsVisibilityProperty);
            }
        }
 
        internal DataGridItemAttachedStorage ItemAttachedStorage
        {
            get { return _itemAttachedStorage; }
        }
 
        /// <summary>
        ///     Determines whether the selection change caused by keyboard input should select a full row (or full rows).
        /// </summary>
        private bool ShouldSelectRowHeader
        {
            get
            {
                return _selectionAnchor != null && SelectedItems.Contains(_selectionAnchor.Value.Item) &&
                       SelectionUnit == DataGridSelectionUnit.CellOrRowHeader &&
                       (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
            }
        }
 
        #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(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(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(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(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(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 { return SystemResourceKey.DataGridFocusBorderBrushKey; }
        }
 
        /// <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 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 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(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(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)
        {
            ArgumentNullException.ThrowIfNull(item);
 
            ScrollIntoView(NewItemInfo(item));
        }
 
        internal void ScrollIntoView(ItemInfo info)
        {
            if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
            {
                OnBringItemIntoView(info);
            }
            else
            {
                // The items aren't generated, try at a later time
                Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(OnBringItemIntoView), info);
            }
        }
 
        /// <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, DataGridColumn column)
        {
            ItemInfo info = (item == null) ? null : NewItemInfo(item);
            ScrollIntoView(info, column);
        }
 
        private void ScrollIntoView(ItemInfo info, DataGridColumn column)
        {
            if (column == null)
            {
                ScrollIntoView(info);
                return;
            }
 
            if (!column.IsVisible)
            {
                return;
            }
 
            if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
            {
                // Scroll by column only
                if (info == null)
                {
                    ScrollColumnIntoView(column);
                }
                else
                {
                    ScrollCellIntoView(info, column);
                }
            }
            else
            {
                // The items aren't generated, try at a later time
                Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(OnScrollIntoView), new object[] { info, 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((ItemInfo)arguments[0], (DataGridColumn)arguments[1]);
                }
                else
                {
                    ScrollColumnIntoView((DataGridColumn)arguments[1]);
                }
            }
            else
            {
                OnBringItemIntoView((ItemInfo)arg);
            }
 
            return null;
        }
 
        private void ScrollColumnIntoView(DataGridColumn column)
        {
            if (_rowTrackingRoot != null)
            {
                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 ScrollCellIntoView(ItemInfo info, DataGridColumn column)
        {
            Debug.Assert(info != null, "info is null.");
            Debug.Assert(column != null, "column is null.");
 
            if (!column.IsVisible)
            {
                return;
            }
 
            // Devirtualize the concerned row if it is not already
            DataGridRow row = ContainerFromItemInfo(info) as DataGridRow;
            if (row == null)
            {
                OnBringItemIntoView(info);
                UpdateLayout();
                row = ContainerFromItemInfo(info) as DataGridRow;
            }
            else
            {
                // Ensures that row is brought into viewport
                row.BringIntoView();
 
                // There happens to be a subtle implication of calling BringIntoView
                // on the row but then not flushing the ScrollViewer's command queue,
                // which is that any subsequent MakeVisible call gets coalesced over
                // this one. Eg. In cases like Ctrl+Home/Key.Up/Key.Down there is
                // a synchronous call to give keyboard focus to the cell which causes
                // a MakeVisible to be enqueued for the cell. This essentially eats up
                // the previous MakeVisible on the row and thus the row never entirely
                // comes into view. Some margin between the cell and the row bounds
                // is clipped off screen. This issue was discovered building the
                // DrtContainerVirtualization suite for DRTControls where we accurately
                // verify Arrange positions.
                UpdateLayout();
            }
 
            // 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);
        }
 
        /// <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 new 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.
                DataGridCell cell = GetCellNearMouse();
                if (cell != null)
                {
                    DataGridColumn column = cell.Column;
                    ItemInfo info = ItemInfoFromContainer(cell.RowOwner);
 
                    // Based on the position of the mouse relative to the field
                    // of cells, choose the cell that is towards 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 = info.Index;
                        if (rowIndex > 0)
                        {
                            info = ItemInfoFromIndex(rowIndex - 1);
                        }
                    }
                    else if (IsMouseBelow(position))
                    {
                        int rowIndex = info.Index;
                        if (rowIndex < (Items.Count - 1))
                        {
                            info = ItemInfoFromIndex(rowIndex + 1);
                        }
                    }
 
                    if (_isRowDragging)
                    {
                        // Perform a row header drag-select
                        OnBringItemIntoView(info);
                        DataGridRow row = (DataGridRow)ItemContainerGenerator.ContainerFromIndex(info.Index);
                        if (row != null)
                        {
                            _hasAutoScrolled = true;
                            HandleSelectionForRowHeaderAndDetailsInput(row, /* startDragging = */ false);
                            SetCurrentItem(info.Item);
                            return true;
                        }
                    }
                    else
                    {
                        // Perform a cell drag-select
                        ScrollCellIntoView(info, column);
                        cell = TryFindCell(info, 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 internal 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)
                    {
                        DetermineItemsHostStarBehavior();
                        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;
            }
        }
 
        private void DetermineItemsHostStarBehavior()
        {
            VirtualizingStackPanel panel = _internalItemsHost as VirtualizingStackPanel;
            if (panel != null)
            {
                panel.IgnoreMaxDesiredSize = InternalColumns.HasVisibleStarColumns;
            }
        }
 
        /// <summary>
        ///     Helper method which ensures the initialization of scroll controls.
        /// </summary>
        private void EnsureInternalScrollControls()
        {
            if (_internalScrollContentPresenter == null)
            {
                if (_internalItemsHost != null)
                {
                    _internalScrollContentPresenter = DataGridHelper.FindVisualParent<ScrollContentPresenter>(_internalItemsHost);
                }
                else if (_rowTrackingRoot != null)
                {
                    DataGridRow row = _rowTrackingRoot.Container;
                    _internalScrollContentPresenter = DataGridHelper.FindVisualParent<ScrollContentPresenter>(row);
                }
                if (_internalScrollContentPresenter != null)
                {
                    _internalScrollContentPresenter.SizeChanged += new SizeChangedEventHandler(OnInternalScrollContentPresenterSizeChanged);
                }
            }
 
            if (_internalScrollHost == null)
            {
                if (_internalItemsHost != null)
                {
                    _internalScrollHost = DataGridHelper.FindVisualParent<ScrollViewer>(_internalItemsHost);
                }
                else if (_rowTrackingRoot != null)
                {
                    DataGridRow row = _rowTrackingRoot.Container;
                    _internalScrollHost = 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(),
                    DataGridNotificationTarget.CellsPresenter | DataGridNotificationTarget.ColumnHeadersPresenter | DataGridNotificationTarget.ColumnCollection);
 
                double totalAvailableWidth = _finalViewportWidth;
                totalAvailableWidth -= CellsPanelHorizontalOffset;
                InternalColumns.RedistributeColumnWidthsOnAvailableSpaceChange(widthChange, totalAvailableWidth);
            }
            _viewportWidthChangeNotificationPending = false;
            return null;
        }
 
        internal void OnHasVisibleStarColumnsChanged()
        {
            DetermineItemsHostStarBehavior();
        }
 
        /// <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(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("BeginEdit", typeof(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("CommitEdit", typeof(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("CancelEdit", typeof(DataGrid));
 
        /// <summary>
        ///     A command that, when invoked, will delete the current row.
        /// </summary>
        public static RoutedUICommand DeleteCommand
        {
            get
            {
                return ApplicationCommands.Delete;
            }
        }
 
        private static void OnCanExecuteBeginEdit(object sender, CanExecuteRoutedEventArgs e)
        {
            ((DataGrid)sender).OnCanExecuteBeginEdit(e);
        }
 
        private static void OnExecutedBeginEdit(object sender, ExecutedRoutedEventArgs e)
        {
            ((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)
            {
                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;
                }
            }
 
            if (canExecute)
            {
                e.CanExecute = true;
                e.Handled = true;
            }
            else
            {
                e.ContinueRouting = true;
            }
        }
 
        /// <summary>
        ///     Invoked when the BeginEdit command is executed.
        /// </summary>
        protected virtual void OnExecutedBeginEdit(ExecutedRoutedEventArgs e)
        {
            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(NewItemInfo(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 = ItemContainerGenerator.IndexFromContainer(cell.RowOwner);
                        reselectPlaceholderCells = ((newItemIndex >= 0) && _selectedCells.Intersects(newItemIndex, out columnIndexRanges));
                    }
 
                    newItem = AddNewItem();
                    SetCurrentCellToNewItem(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(NewItemInfo(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;
                DataGridBeginningEditEventArgs beginningEditEventArgs = null;
 
                if (cell != null)
                {
                    // Give the callback an opportunity to cancel edit mode
                    beginningEditEventArgs = new 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(NewItemInfo(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(NewItemInfo(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();
                        }
 
                        _editingRowInfo = ItemInfoFromContainer(cell.RowOwner);
                    }
 
                    cell.BeginEdit(editingEventArgs);
                    cell.RowOwner.IsEditing = true;
 
                    // Create a CellAutomationValueHolder object that has a binding to the content of the CurrentCell being  edited.
                    // This is required to raise PropertyChanged AutomationEvent when cell is being edited manually.
                    EnsureCellAutomationValueHolder(cell);
                }
            }
 
            // CancelEdit and CommitEdit rely on IsAddingNewItem and IsEditingRowItem
            CommandManager.InvalidateRequerySuggested();
 
            e.Handled = true;
        }
 
        private static void OnCanExecuteCommitEdit(object sender, CanExecuteRoutedEventArgs e)
        {
            ((DataGrid)sender).OnCanExecuteCommitEdit(e);
        }
 
        private static void OnExecutedCommitEdit(object sender, ExecutedRoutedEventArgs e)
        {
            ((DataGrid)sender).OnExecutedCommitEdit(e);
        }
 
        private 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 : DataGridHelper.FindVisualParent<DataGridCell>(source);
        }
 
        private bool CanEndEdit(CanExecuteRoutedEventArgs e, bool commit)
        {
            DataGridCell cellContainer = GetEventCellOrCurrentCell(e);
            if (cellContainer == null)
            {
                // If there is no cell, then nothing can be determined. So, no edit could end.
                return false;
            }
 
            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 ||
                   (!HasCellValidationError &&
                   IsAddingOrEditingRowItem(editingUnit, rowItem));
        }
 
        /// <summary>
        ///     Invoked to determine if the CommitEdit command can be executed.
        /// </summary>
        protected virtual void OnCanExecuteCommitEdit(CanExecuteRoutedEventArgs e)
        {
            if (CanEndEdit(e, /* commit = */ true))
            {
                e.CanExecute = true;
                e.Handled = true;
            }
            else
            {
                e.ContinueRouting = true;
            }
        }
 
        /// <summary>
        ///     Invoked when the CommitEdit command is executed.
        /// </summary>
        protected virtual void OnExecutedCommitEdit(ExecutedRoutedEventArgs e)
        {
            DataGridCell cell = CurrentCellContainer;
            bool validationPassed = true;
            if (cell != null)
            {
                DataGridEditingUnit editingUnit = GetEditingUnit(e.Parameter);
 
                bool eventCanceled = false;
                if (cell.IsEditing)
                {
                    DataGridCellEditEndingEventArgs cellEditEndingEventArgs = new DataGridCellEditEndingEventArgs(cell.Column, cell.RowOwner, cell.EditingElement, DataGridEditAction.Commit);
                    OnCellEditEnding(cellEditEndingEventArgs);
 
                    eventCanceled = cellEditEndingEventArgs.Cancel;
                    if (!eventCanceled)
                    {
                        validationPassed = cell.CommitEdit();
                        HasCellValidationError = !validationPassed;
                        UpdateCellAutomationValueHolder(cell);
                    }
                }
 
                // 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 is being edited or added and being targeted directly.
                if (validationPassed &&
                    !eventCanceled &&
                    IsAddingOrEditingRowItem(editingUnit, cell.RowDataItem))
                {
                    DataGridRowEditEndingEventArgs rowEditEndingEventArgs = new DataGridRowEditEndingEventArgs(cell.RowOwner, 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);
 
                    if (!cell.RowOwner.IsEditing)
                    {
                        ReleaseCellAutomationValueHolders();
                    }
                }
 
                // 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 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 DataGridEditingUnit)) ?
                    (DataGridEditingUnit)parameter :
                    IsEditingCurrentCell ? DataGridEditingUnit.Cell : DataGridEditingUnit.Row;
        }
 
        /// <summary>
        ///     Raised just before row editing is ended.
        ///     Gives handlers the opportunity to cancel the operation.
        /// </summary>
        public event EventHandler<DataGridRowEditEndingEventArgs> RowEditEnding;
 
        /// <summary>
        ///     Called just before row editing is ended.
        ///     Gives subclasses the opportunity to cancel the operation.
        /// </summary>
        protected virtual void OnRowEditEnding(DataGridRowEditEndingEventArgs e)
        {
            if (RowEditEnding != null)
            {
                RowEditEnding(this, e);
            }
 
            if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked))
            {
                DataGridAutomationPeer peer = DataGridAutomationPeer.FromElement(this) as 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<DataGridCellEditEndingEventArgs> CellEditEnding;
 
        /// <summary>
        ///     Called just before cell editing is ended.
        ///     Gives subclasses the opportunity to cancel the operation.
        /// </summary>
        protected virtual void OnCellEditEnding(DataGridCellEditEndingEventArgs e)
        {
            if (CellEditEnding != null)
            {
                CellEditEnding(this, e);
            }
 
            if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked))
            {
                DataGridAutomationPeer peer = DataGridAutomationPeer.FromElement(this) as DataGridAutomationPeer;
                if (peer != null)
                {
                    peer.RaiseAutomationCellInvokeEvents(e.Column, e.Row);
                }
            }
        }
 
        private static void OnCanExecuteCancelEdit(object sender, CanExecuteRoutedEventArgs e)
        {
            ((DataGrid)sender).OnCanExecuteCancelEdit(e);
        }
 
        private static void OnExecutedCancelEdit(object sender, ExecutedRoutedEventArgs e)
        {
            ((DataGrid)sender).OnExecutedCancelEdit(e);
        }
 
        /// <summary>
        ///     Invoked to determine if the CancelEdit command can be executed.
        /// </summary>
        protected virtual void OnCanExecuteCancelEdit(CanExecuteRoutedEventArgs e)
        {
            if (CanEndEdit(e, /* commit = */ false))
            {
                e.CanExecute = true;
                e.Handled = true;
            }
            else
            {
                e.ContinueRouting = true;
            }
        }
 
        /// <summary>
        ///     Invoked when the CancelEdit command is executed.
        /// </summary>
        protected virtual void OnExecutedCancelEdit(ExecutedRoutedEventArgs e)
        {
            DataGridCell cell = CurrentCellContainer;
            if (cell != null)
            {
                DataGridEditingUnit editingUnit = GetEditingUnit(e.Parameter);
 
                bool eventCanceled = false;
                if (cell.IsEditing)
                {
                    DataGridCellEditEndingEventArgs cellEditEndingEventArgs = new DataGridCellEditEndingEventArgs(cell.Column, cell.RowOwner, cell.EditingElement, DataGridEditAction.Cancel);
                    OnCellEditEnding(cellEditEndingEventArgs);
 
                    eventCanceled = cellEditEndingEventArgs.Cancel;
                    if (!eventCanceled)
                    {
                        cell.CancelEdit();
                        HasCellValidationError = false;
                        UpdateCellAutomationValueHolder(cell);
                    }
                }
 
                if (!eventCanceled &&
                    IsAddingOrEditingRowItem(editingUnit, cell.RowDataItem))
                {
                    bool cancelAllowed = true;
 
                    DataGridRowEditEndingEventArgs rowEditEndingEventArgs = new DataGridRowEditEndingEventArgs(cell.RowOwner, DataGridEditAction.Cancel);
                    OnRowEditEnding(rowEditEndingEventArgs);
                    cancelAllowed = !rowEditEndingEventArgs.Cancel;
 
                    if (cancelAllowed)
                    {
                        var bindingGroup = cell.RowOwner.BindingGroup;
                        if (bindingGroup != null)
                        {
                            bindingGroup.CancelEdit();
                        }
 
                        CancelRowItem();
                    }
                }
 
                // 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;
 
                    ReleaseCellAutomationValueHolders();
                }
 
                // CancelEdit and CommitEdit rely on IsAddingNewItem and IsEditingRowItem
                CommandManager.InvalidateRequerySuggested();
            }
 
            e.Handled = true;
        }
 
        private static void OnCanExecuteDelete(object sender, CanExecuteRoutedEventArgs e)
        {
            ((DataGrid)sender).OnCanExecuteDelete(e);
        }
 
        private static void OnExecutedDelete(object sender, ExecutedRoutedEventArgs e)
        {
            ((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
                (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(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,
                    // determine a new item to select once the items are removed.
                    int numSelected = SelectedItems.Count;
                    int indexToSelect = -1;
                    ItemInfo currentInfo = CurrentInfo;
 
                    // The current item is in the selection
                    if (SelectedItems.Contains(currentInfo.Item))
                    {
                        // Choose the smaller index between the anchor and the current item
                        // as the index to select after the items are removed.
                        indexToSelect = currentInfo.Index;
                        if (_selectionAnchor != null)
                        {
                            int anchorIndex = _selectionAnchor.Value.ItemInfo.Index;
                            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.
                        SetCurrentItem(itemToSelect);
 
                        // Since the current cell should be in view, there should be a container
                        DataGridCell cell = CurrentCellContainer;
                        if (cell != null)
                        {
                            _selectionAnchor = null;
                            HandleSelectionForCellInput(cell, /* startDragging = */ false, /* allowsExtendSelect = */ false, /* allowsMinimalSelect = */ false);
                        }
                    }
                }
            }
 
            e.Handled = true;
        }
 
        // set the CurrentCell to the newly-added item, using an accurate ItemInfo
        private void SetCurrentCellToNewItem(object newItem)
        {
            ItemInfo info = null;
            int index;
 
            // usually we know where to find the new item
            switch (EditableItems.NewItemPlaceholderPosition)
            {
                case NewItemPlaceholderPosition.AtEnd:
                    index = Items.Count - 2;
                    if (index >= 0 && ItemsControl.EqualsEx(newItem, Items[index]))
                    {
                        info = ItemInfoFromIndex(index);
                    }
                    break;
 
                case NewItemPlaceholderPosition.AtBeginning:
                    index = 1;
                    if (index < Items.Count && ItemsControl.EqualsEx(newItem, Items[index]))
                    {
                        info = ItemInfoFromIndex(index);
                    }
                    break;
            }
 
            // but if it's not where we expect, find it the hard way
            if (info == null)
            {
                info = ItemInfoFromIndex(Items.IndexOf(newItem));
            }
 
            // set CurrentCell to the corresponding cell info
            DataGridCellInfo currentCell = CurrentCell;
            currentCell = (info != null) ? new DataGridCellInfo(info, currentCell.Column, this)
                                        : DataGridCellInfo.CreatePossiblyPartialCellInfo(newItem, currentCell.Column, this);
            SetCurrentValueInternal(CurrentCellProperty, currentCell);
        }
 
        #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(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
                ((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(DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnCurrentItemChanged)));
 
        private static void OnCurrentItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DataGrid dataGrid = (DataGrid)d;
            DataGridCellInfo currentCell = dataGrid.CurrentCell;
            object newItem = e.NewValue;
 
            if (currentCell.Item != newItem)
            {
                // Update the CurrentCell structure with the new item
                dataGrid.SetCurrentValueInternal(CurrentCellProperty, DataGridCellInfo.CreatePossiblyPartialCellInfo(newItem, currentCell.Column, dataGrid));
            }
 
            // the RowHeaders need to know when the current item changes so they can update their Visual State.
            OnNotifyRowHeaderPropertyChanged(d, e);
        }
 
        private void SetCurrentItem(object item)
        {
            // don't set UnsetValue - it's equivalent to ClearValue, which removes data-binding
            if (item == DependencyProperty.UnsetValue)
                item = null;
 
            SetCurrentValueInternal(CurrentItemProperty, item);
        }
 
        /// <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 DataGridColumn CurrentColumn
        {
            get { return (DataGridColumn)GetValue(CurrentColumnProperty); }
            set { SetValue(CurrentColumnProperty, value); }
        }
 
        /// <summary>
        ///     The DependencyProperty for CurrentColumn.
        /// </summary>
        public static readonly DependencyProperty CurrentColumnProperty =
            DependencyProperty.Register("CurrentColumn", typeof(DataGridColumn), typeof(DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnCurrentColumnChanged)));
 
        private static void OnCurrentColumnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DataGrid dataGrid = (DataGrid)d;
            DataGridCellInfo currentCell = dataGrid.CurrentCell;
            DataGridColumn newColumn = (DataGridColumn)e.NewValue;
 
            if (currentCell.Column != newColumn)
            {
                // Update the CurrentCell structure with the new column
                dataGrid.SetCurrentValueInternal(CurrentCellProperty, 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 DataGridCellInfo CurrentCell
        {
            get { return (DataGridCellInfo)GetValue(CurrentCellProperty); }
            set { SetValue(CurrentCellProperty, value); }
        }
 
        /// <summary>
        ///     The DependencyProperty for CurrentCell.
        /// </summary>
        public static readonly DependencyProperty CurrentCellProperty =
            DependencyProperty.Register("CurrentCell", typeof(DataGridCellInfo), typeof(DataGrid), new FrameworkPropertyMetadata(DataGridCellInfo.Unset, new PropertyChangedCallback(OnCurrentCellChanged)));
 
        private static void OnCurrentCellChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DataGrid dataGrid = (DataGrid)d;
            DataGridCellInfo oldCell = (DataGridCellInfo)e.OldValue;
            DataGridCellInfo currentCell = (DataGridCellInfo)e.NewValue;
 
            if (dataGrid.CurrentItem != currentCell.Item)
            {
                dataGrid.SetCurrentItem(currentCell.Item);
            }
 
            if (dataGrid.CurrentColumn != currentCell.Column)
            {
                dataGrid.SetCurrentValueInternal(CurrentColumnProperty, 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, DataGridEditingUnit.Row, /* exitEditingMode = */ true);
                }
                else if (dataGrid._currentCellContainer.IsEditing)
                {
                    // Only the cell needs to commit.
                    dataGrid.EndEdit(CommitEditCommand, dataGrid._currentCellContainer, DataGridEditingUnit.Cell, /* exitEditingMode = */ true);
                }
            }
 
            var oldCellContainer = dataGrid._currentCellContainer;
            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.
                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.ItemInfo, currentCell.Column);
                        cell = dataGrid.CurrentCellContainer;
                    }
                }
 
                if ((cell != null))
                {
                    if (!cell.IsKeyboardFocusWithin)
                    {
                        cell.Focus();
                    }
 
                    if (oldCellContainer != cell)
                    {
                        if (oldCellContainer != null)
                        {
                            oldCellContainer.NotifyCurrentCellContainerChanged();
                        }
 
                        cell.NotifyCurrentCellContainerChanged();
                    }
                }
                else if (oldCellContainer != null)
                {
                    oldCellContainer.NotifyCurrentCellContainerChanged();
                }
            }
 
            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(DataGridCell cell, bool isFocusWithinCell)
        {
            if (isFocusWithinCell)
            {
                // 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.
        }
 
        internal DataGridCell CurrentCellContainer
        {
            get
            {
                if (_currentCellContainer == null)
                {
                    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)
                    {
                        SetCurrentValueInternal(CurrentCellProperty, DataGridCellInfo.Unset);   // ClearValue
                    }
                    else
                    {
                        SetCurrentValueInternal(CurrentCellProperty, new DataGridCellInfo(value));
                    }
 
                    _pendingCurrentCellContainer = null;
                    _currentCellContainer = value;
                    CommandManager.InvalidateRequerySuggested();
                }
            }
        }
 
        private bool IsEditingCurrentCell
        {
            get
            {
                DataGridCell cell = CurrentCellContainer;
                if (cell != null)
                {
                    return cell.IsEditing;
                }
 
                return false;
            }
        }
 
        private bool IsCurrentCellReadOnly
        {
            get
            {
                DataGridCell cell = CurrentCellContainer;
                if (cell != null)
                {
                    return cell.IsReadOnly;
                }
 
                return false;
            }
        }
 
        internal ItemInfo CurrentInfo
        {
            get { return LeaseItemInfo(CurrentCell.ItemInfo); }
        }
 
        internal bool IsCurrent(DataGridRow row, DataGridColumn column = null)
        {
            DataGridCellInfo currentCell = CurrentCell;
            if (currentCell.ItemInfo == null)
            {
                // It's possible for the user to create a DGCellInfo with ItemInfo==null,
                // by calling the implicit default ctor.  It's too late
                // to define an explicit default ctor, so just test for it here and
                // treat it as Unset;
                currentCell = DataGridCellInfo.Unset;
            }
            DependencyObject currentContainer = currentCell.ItemInfo.Container;
            int currentIndex = currentCell.ItemInfo.Index;
            return (column == null || column == currentCell.Column) &&             // columns match
                    ((currentContainer != null && currentContainer == row) ||    // rows match (the easy way)
                        (ItemsControl.EqualsEx(CurrentItem, row.Item) &&         // rows match (the hard way)
                            (currentIndex < 0 || currentIndex == ItemContainerGenerator.IndexFromContainer(row))
                        )
                    );
        }
 
        /// <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<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(DataGridBeginningEditEventArgs e)
        {
            if (BeginningEdit != null)
            {
                BeginningEdit(this, e);
            }
 
            if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked))
            {
                DataGridAutomationPeer peer = DataGridAutomationPeer.FromElement(this) as 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<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(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 lead 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 lead 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 bool BeginEdit(RoutedEventArgs editingEventArgs)
        {
            if (!IsReadOnly)
            {
                DataGridCell cellContainer = CurrentCellContainer;
                if (cellContainer != null)
                {
                    if (!cellContainer.IsEditing &&
                        BeginEditCommand.CanExecute(editingEventArgs, cellContainer))
                    {
                        BeginEditCommand.Execute(editingEventArgs, cellContainer);
 
                        // When editing the NewItemPlaceHolder row the place holder row gets
                        // replaced with a real row and thus causes a new cellContainer to be
                        // generated. So we should be checking the new cellContainer to decide
                        // if this operation succeeded. 
 
                        cellContainer = CurrentCellContainer;
 
                        // the BeginEditCommand may move focus off this DataGrid,
                        // which sets CurrentCellContainer to null.  In that case,
                        // return false. 
                        if (cellContainer == null)
                        {
                            return false;
                        }
                    }
 
                    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(DataGridEditingUnit.Cell);
            }
            else if (IsEditingRowItem || IsAddingNewItem)
            {
                return CancelEdit(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(DataGridCell cell)
        {
            DataGridCell currentCell = CurrentCellContainer;
            if (currentCell != null && currentCell == cell && currentCell.IsEditing)
            {
                return CancelEdit(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(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(DataGridEditingUnit.Row);
            }
            else if (IsEditingCurrentCell)
            {
                // Cancel the current cell edit.
                CancelEdit(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(DataGridEditingUnit.Cell, true);
            }
            else if (IsEditingRowItem || IsAddingNewItem)
            {
                return CommitEdit(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(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(DataGridEditingUnit.Row, /* exitEditingMode = */ true);
            }
            else if (IsEditingCurrentCell)
            {
                // Commit the current cell edit.
                return CommitEdit(DataGridEditingUnit.Cell, /* exitEditingMode = */ true);
            }
 
            return true;
        }
 
        private bool EndEdit(RoutedCommand command, DataGridCell cellContainer, 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 == 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 == 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();
                }
            }
        }
 
        /// <summary>
        ///     Cell in DataGrid which has logical focus
        /// </summary>
        internal DataGridCell FocusedCell
        {
            get
            {
                return _focusedCell;
            }
            set
            {
                if (_focusedCell != value)
                {
                    if (_focusedCell != null)
                    {
                        UpdateCurrentCell(_focusedCell, false);
                    }
                    _focusedCell = value;
                    if (_focusedCell != null)
                    {
                        UpdateCurrentCell(_focusedCell, true);
                    }
                }
            }
        }
 
        #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(DataGrid), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnCanUserAddRowsChanged), new CoerceValueCallback(OnCoerceCanUserAddRows)));
 
        private static void OnCanUserAddRowsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).UpdateNewItemPlaceholder(/* isAddingNewItem = */ false);
        }
 
        private static object OnCoerceCanUserAddRows(DependencyObject d, object baseValue)
        {
            return OnCoerceCanUserAddOrDeleteRows((DataGrid)d, (bool)baseValue, /* canUserAddRowsProperty = */ true);
        }
 
        private static bool OnCoerceCanUserAddOrDeleteRows(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(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((DataGrid)d, (bool)baseValue, /* canUserAddRowsProperty = */ false);
        }
 
        /// <summary>
        ///     An event that is raised before a new item is created so that
        ///     developers can participate in the construction of the new item.
        /// </summary>
        public event EventHandler<AddingNewItemEventArgs> AddingNewItem;
 
        /// <summary>
        ///     A method that is called before a new item is created so that
        ///     overrides can participate in the construction of the new item.
        /// </summary>
        /// <remarks>
        ///     The default implementation raises the AddingNewItem event.
        /// </remarks>
        /// <param name="e">Event arguments that provide access to the new item.</param>
        protected virtual void OnAddingNewItem(AddingNewItemEventArgs e)
        {
            if (AddingNewItem != null)
            {
                AddingNewItem(this, e);
            }
        }
 
        /// <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 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(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);
 
            // Create the new item (with app's help, or not)
            object newItem = null;
            IEditableCollectionViewAddNewItem ani = (IEditableCollectionViewAddNewItem)Items;
 
            if (ani.CanAddNewItem)
            {
                AddingNewItemEventArgs e = new AddingNewItemEventArgs();
                OnAddingNewItem(e);
                newItem = e.NewItem;
            }
 
            newItem = (newItem != null) ? ani.AddNewItem(newItem) : EditableItems.AddNew();
 
            // initialize the new item
            if (newItem != null)
            {
                OnInitializingNewItem(new InitializingNewItemEventArgs(newItem));
            }
 
            // 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)
            {
                if (EditableItems.CanCancelEdit)
                {
                    EditableItems.CancelEdit();
                }
                else
                {
                    // we haven't changed the data item, so this merely exits edit-mode
                    EditableItems.CommitEdit();
                }
            }
            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(NewItemInfo(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
                    SetCurrentItem(CollectionView.NewItemPlaceholder);
                }
 
                if (wasSelected)
                {
                    // Re-select the placeholder if it was selected before
                    SelectItem(NewItemInfo(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(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;
                _editingRowInfo = null;
            }
        }
 
        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 IsAddingOrEditingRowItem(DataGridEditingUnit editingUnit, object item)
        {
            return (editingUnit == DataGridEditingUnit.Row) &&
                    IsAddingOrEditingRowItem(item);
        }
 
        private bool IsEditingItem(object item)
        {
            return IsEditingRowItem && (EditableItems.CurrentEditItem == item);
        }
 
        private void UpdateNewItemPlaceholder(bool isAddingNewItem)
        {
            var editableItems = EditableItems;
            bool canUserAddRows = CanUserAddRows;
 
            if (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
            DataGridRow newItemPlaceholderRow = (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)
                {
                    SetCurrentItem(Items[itemCount - 1]);
                }
            }
            else if (position == NewItemPlaceholderPosition.AtBeginning)
            {
                if (Items.Count > 0)
                {
                    SetCurrentItem(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 == DataGrid.NewItemPlaceholder);
        }
 
        #endregion
 
        #region Row Details
 
        /// <summary>
        ///     Determines which visibility mode the Row's details use.
        /// </summary>
        public DataGridRowDetailsVisibilityMode RowDetailsVisibilityMode
        {
            get { return (DataGridRowDetailsVisibilityMode)GetValue(RowDetailsVisibilityModeProperty); }
            set { SetValue(RowDetailsVisibilityModeProperty, value); }
        }
 
        /// <summary>
        ///     DependencyProperty for RowDetailsVisibilityMode.
        /// </summary>
        public static readonly DependencyProperty RowDetailsVisibilityModeProperty =
            DependencyProperty.Register("RowDetailsVisibilityMode", typeof(DataGridRowDetailsVisibilityMode), typeof(DataGrid), new FrameworkPropertyMetadata(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(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(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(DataGrid), new FrameworkPropertyMetadata(null, OnNotifyRowAndDetailsPropertyChanged));
 
        /// <summary>
        ///     Event that is fired just before the details of a Row is shown
        /// </summary>
        public event EventHandler<DataGridRowDetailsEventArgs> LoadingRowDetails;
 
        /// <summary>
        ///     Event that is fired just before the details of a Row is hidden
        /// </summary>
        public event EventHandler<DataGridRowDetailsEventArgs> UnloadingRowDetails;
 
        /// <summary>
        ///     Event that is fired when the visibility of a Rows details changes.
        /// </summary>
        public event EventHandler<DataGridRowDetailsEventArgs> RowDetailsVisibilityChanged;
 
 
        internal void OnLoadingRowDetailsWrapper(DataGridRow row)
        {
            if (row != null &&
                row.DetailsLoaded == false &&
                row.DetailsVisibility == Visibility.Visible &&
                row.DetailsPresenter != null)
            {
                DataGridRowDetailsEventArgs e = new DataGridRowDetailsEventArgs(row, row.DetailsPresenter.DetailsElement);
                OnLoadingRowDetails(e);
                row.DetailsLoaded = true;
            }
        }
 
        internal void OnUnloadingRowDetailsWrapper(DataGridRow row)
        {
            if (row != null &&
                row.DetailsLoaded == true &&
                row.DetailsPresenter != null)
            {
                DataGridRowDetailsEventArgs e = new DataGridRowDetailsEventArgs(row, row.DetailsPresenter.DetailsElement);
                OnUnloadingRowDetails(e);
                row.DetailsLoaded = false;
            }
        }
 
        /// <summary>
        ///     Invokes the LoadingRowDetails event
        /// </summary>
        protected virtual void OnLoadingRowDetails(DataGridRowDetailsEventArgs e)
        {
            if (LoadingRowDetails != null)
            {
                LoadingRowDetails(this, e);
            }
        }
 
        /// <summary>
        ///     Invokes the UnloadingRowDetails event
        /// </summary>
        protected virtual void OnUnloadingRowDetails(DataGridRowDetailsEventArgs e)
        {
            if (UnloadingRowDetails != null)
            {
                UnloadingRowDetails(this, e);
            }
        }
 
        /// <summary>
        ///     Invokes the RowDetailsVisibilityChanged event
        /// </summary>
        protected internal virtual void OnRowDetailsVisibilityChanged(DataGridRowDetailsEventArgs e)
        {
            if (RowDetailsVisibilityChanged != null)
            {
                RowDetailsVisibilityChanged(this, e);
            }
 
            var row = e.Row;
 
            // LoadingRowDetails only needs to be called when row.DetailsVisibility == Visibility.Visible.
            // OnLoadingRowDetailsWrapper already makes this check, so we omit it here.
            //
            // No need to used DelayedOnLoadingRowDetails because OnRowDetailsVisibilityChanged isn't called until after the
            // template is expanded.
            OnLoadingRowDetailsWrapper(row);
        }
 
        #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(DataGrid), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnNotifyRowHeaderPropertyChanged)));
 
        #endregion
 
        #region Row Margin
 
        /// <summary>
        ///     Margin for the new item row.
        /// </summary>
        public Thickness NewItemMargin
        {
            get { return (Thickness)GetValue(NewItemMarginProperty); }
            private set { SetValue(NewItemMarginPropertyKey, value); }
        }
 
        /// <summary>
        ///     Using a DependencyProperty as the backing store for NewItemMargin.  This enables animation, styling, binding, etc...
        /// </summary>
        private static readonly DependencyPropertyKey NewItemMarginPropertyKey =
            DependencyProperty.RegisterReadOnly("NewItemMargin",
                typeof(Thickness),
                typeof(DataGrid),
                new FrameworkPropertyMetadata(new Thickness(0)));
 
        /// <summary>
        ///     DependencyProperty for NewItemMargin property
        /// </summary>
        public static readonly DependencyProperty NewItemMarginProperty =
            NewItemMarginPropertyKey.DependencyProperty;
 
        private void EnqueueNewItemMarginComputation()
        {
            if (!_newItemMarginComputationPending)
            {
                _newItemMarginComputationPending = true;
                Dispatcher.BeginInvoke((Action)delegate ()
                {
                    double marginLeft = 0;
                    if (IsGrouping && InternalScrollHost != null)
                    {
                        ContainerTracking<DataGridRow> tracker = _rowTrackingRoot;
                        while (tracker != null)
                        {
                            DataGridRow row = tracker.Container;
                            if (!row.IsNewItem)
                            {
                                // If grouping is enabled and the current row is grouped,
                                // then use this to row to determine its left coordinate.
                                GeneralTransform transform = row.TransformToAncestor(InternalScrollHost);
                                if (transform != null)
                                {
                                    marginLeft = transform.Transform(new Point()).X;
                                }
                                break;
                            }
                            tracker = tracker.Next;
                        }
                    }
                    NewItemMargin = new Thickness(marginLeft, 0, 0, 0);
                    _newItemMarginComputationPending = false;
                },
                DispatcherPriority.Input);
            }
        }
 
        internal override void OnIsGroupingChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnIsGroupingChanged(e);
            EnqueueNewItemMarginComputation();
        }
 
        #endregion
 
        #region Selection
 
        internal SelectedItemCollection SelectedItemCollection
        {
            get { return (SelectedItemCollection)SelectedItems; }
        }
 
        /// <summary>
        ///     The currently selected cells.
        /// </summary>
        public IList<DataGridCellInfo> SelectedCells
        {
            get { return _selectedCells; }
        }
 
        internal 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, VirtualizedCellInfoCollection oldItems, VirtualizedCellInfoCollection newItems)
        {
            DataGridSelectionMode selectionMode = SelectionMode;
            DataGridSelectionUnit selectionUnit = SelectionUnit;
            if (!IsUpdatingSelectedCells && (selectionUnit == DataGridSelectionUnit.FullRow))
            {
                throw new InvalidOperationException(SR.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)
                {
                    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)
                {
                    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 == 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 == 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 (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
                SelectedCellsChangedEventArgs e = new 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(SelectedCellsChangedEventArgs e)
        {
            if (SelectedCellsChanged != null)
            {
                SelectedCellsChanged(this, e);
            }
 
            // Raise automation events
            if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected) ||
                AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementAddedToSelection) ||
                AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection))
            {
                DataGridAutomationPeer peer = DataGridAutomationPeer.FromElement(this) as DataGridAutomationPeer;
                if (peer != null)
                {
                    peer.RaiseAutomationCellSelectedEvent(e);
                }
            }
        }
 
        /// <summary>
        ///     A command that, when invoked, will select all items in the DataGrid.
        /// </summary>
        public static RoutedUICommand SelectAllCommand
        {
            get
            {
                return ApplicationCommands.SelectAll;
            }
        }
 
        private static void OnCanExecuteSelectAll(object sender, CanExecuteRoutedEventArgs e)
        {
            DataGrid dataGrid = (DataGrid)sender;
            e.CanExecute = (dataGrid.SelectionMode == DataGridSelectionMode.Extended) && dataGrid.IsEnabled;
            e.Handled = true;
        }
 
        private static void OnExecutedSelectAll(object sender, ExecutedRoutedEventArgs e)
        {
            DataGrid dataGrid = (DataGrid)sender;
            if (dataGrid.SelectionUnit == DataGridSelectionUnit.Cell)
            {
                dataGrid.SelectAllCells();
            }
            else
            {
                dataGrid.SelectAll();
            }
 
            e.Handled = true;
        }
 
        /// <summary>
        ///     Called from the public SelectAll API.
        /// </summary>
        internal override void SelectAllImpl()
        {
            int numItems = Items.Count;
            int numColumns = _columns.Count;
            if ((numColumns > 0) && (numItems > 0))
            {
                using (UpdateSelectedCells())
                {
                    // Selecting the cells first is an optimization.
                    _selectedCells.AddRegion(0, 0, numItems, numColumns);
                    base.SelectAllImpl();
                }
            }
        }
 
        internal void SelectOnlyThisCell(DataGridCellInfo currentCellInfo)
        {
            using (UpdateSelectedCells())
            {
                _selectedCells.Clear();
                _selectedCells.Add(currentCellInfo);
            }
        }
 
        /// <summary>
        ///     Selects all cells.
        /// </summary>
        public void SelectAllCells()
        {
            if (SelectionUnit == DataGridSelectionUnit.FullRow)
            {
                SelectAll();
            }
            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()
        {
            using (UpdateSelectedCells())
            {
                // Unselect all of the cells
                _selectedCells.Clear();
 
                if (SelectionUnit != 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 DataGridSelectionMode SelectionMode
        {
            get { return (DataGridSelectionMode)GetValue(SelectionModeProperty); }
            set { SetValue(SelectionModeProperty, value); }
        }
 
        /// <summary>
        ///     The DependencyProperty for the SelectionMode property.
        /// </summary>
        public static readonly DependencyProperty SelectionModeProperty =
            DependencyProperty.Register("SelectionMode", typeof(DataGridSelectionMode), typeof(DataGrid), new FrameworkPropertyMetadata(DataGridSelectionMode.Extended, new PropertyChangedCallback(OnSelectionModeChanged)));
 
        private static void OnSelectionModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DataGrid dataGrid = (DataGrid)d;
            DataGridSelectionMode newSelectionMode = (DataGridSelectionMode)e.NewValue;
            bool changingToSingleMode = newSelectionMode == DataGridSelectionMode.Single;
            DataGridSelectionUnit selectionUnit = dataGrid.SelectionUnit;
 
            if (changingToSingleMode && (selectionUnit == 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 != DataGridSelectionMode.Single);
 
            if (changingToSingleMode && (selectionUnit == 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.InternalSelectedInfo.Index);
                    }
                }
                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 DataGridSelectionUnit SelectionUnit
        {
            get { return (DataGridSelectionUnit)GetValue(SelectionUnitProperty); }
            set { SetValue(SelectionUnitProperty, value); }
        }
 
        /// <summary>
        ///     The DependencyProperty for the SelectionUnit property.
        /// </summary>
        public static readonly DependencyProperty SelectionUnitProperty =
            DependencyProperty.Register("SelectionUnit", typeof(DataGridSelectionUnit), typeof(DataGrid), new FrameworkPropertyMetadata(DataGridSelectionUnit.FullRow, new PropertyChangedCallback(OnSelectionUnitChanged)));
 
        private static void OnSelectionUnitChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DataGrid dataGrid = (DataGrid)d;
            DataGridSelectionUnit oldUnit = (DataGridSelectionUnit)e.OldValue;
 
            // Full wipe on unit change
            if (oldUnit != DataGridSelectionUnit.Cell)
            {
                dataGrid.UnselectAll();
            }
 
            if (oldUnit != 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.RemovedInfos.Count;
                    for (int i = 0; i < count; i++)
                    {
                        ItemInfo rowInfo = e.RemovedInfos[i];
                        UpdateSelectionOfCellsInRow(rowInfo, /* isSelected = */ false);
                    }
 
                    // Add cells of rows that were selected
                    count = e.AddedInfos.Count;
                    for (int i = 0; i < count; i++)
                    {
                        ItemInfo rowInfo = e.AddedInfos[i];
                        UpdateSelectionOfCellsInRow(rowInfo, /* 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))
            {
                DataGridAutomationPeer peer = DataGridAutomationPeer.FromElement(this) as 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(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;
 
                        ContainerTracking<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)
                    {
                        ContainerTracking<DataGridRow> rowTracker = _rowTrackingRoot;
                        while (rowTracker != null)
                        {
                            DataGridRow row = rowTracker.Container;
                            DataGridCellsPresenter cellsPresenter = row.CellsPresenter;
                            if (cellsPresenter != null)
                            {
                                ContainerTracking<DataGridCell> cellTracker = cellsPresenter.CellTrackingRoot;
                                while (cellTracker != null)
                                {
                                    DataGridCell cell = cellTracker.Container;
                                    DataGridCellInfo cellInfo = new DataGridCellInfo(cell);
                                    if (cells.Contains(cellInfo))
                                    {
                                        cell.SyncIsSelected(isSelected);
                                    }
 
                                    cellTracker = cellTracker.Next;
                                }
                            }
 
                            rowTracker = rowTracker.Next;
                        }
                    }
                    else
                    {
                        foreach (DataGridCellInfo cellInfo in cells)
                        {
                            DataGridCell cell = TryFindCell(cellInfo);
                            if (cell != null)
                            {
                                cell.SyncIsSelected(isSelected);
                            }
                        }
                    }
                }
            }
        }
 
        private void UpdateSelectionOfCellsInRow(ItemInfo rowInfo, bool isSelected)
        {
            int columnCount = _columns.Count;
            if (columnCount > 0)
            {
                if (!isSelected && _pendingInfos != null)
                {
                    // deselecting an item - remove it from the pending list
                    // regardless of whether its index is known
                    _pendingInfos.Remove(rowInfo);
                }
 
                int rowIndex = rowInfo.Index;
                if (rowIndex >= 0)
                {
                    if (isSelected)
                    {
                        _selectedCells.AddRegion(rowIndex, 0, 1, columnCount);
                    }
                    else
                    {
                        _selectedCells.RemoveRegion(rowIndex, 0, 1, columnCount);
                    }
                }
                else
                {
                    // the index isn't known yet.  Mark the ItemInfo as pending
                    if (isSelected)
                    {
                        EnsurePendingInfos();
                        _pendingInfos.Add(rowInfo);
                    }
                }
            }
        }
 
        private void EnsurePendingInfos()
        {
            if (_pendingInfos == null)
            {
                _pendingInfos = new List<ItemInfo>();
            }
        }
 
        /// <summary>
        ///     Notification that a particular cell's IsSelected property changed.
        /// </summary>
        internal void CellIsSelectedChanged(DataGridCell cell, bool isSelected)
        {
            if (!IsUpdatingSelectedCells)
            {
                DataGridCellInfo cellInfo = new 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(DataGridCell cell, bool startDragging, bool allowsExtendSelect, bool allowsMinimalSelect)
        {
            DataGridSelectionUnit selectionUnit = SelectionUnit;
 
            // If the mode is None, then no selection will occur
            if (selectionUnit == DataGridSelectionUnit.FullRow)
            {
                // In FullRow mode, items are selected
                MakeFullRowSelection(ItemInfoFromContainer(cell.RowOwner), allowsExtendSelect, allowsMinimalSelect);
            }
            else
            {
                // In the other modes, cells can be individually selected
                MakeCellSelection(new 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(DataGridRow row, bool startDragging)
        {
            ItemInfo rowInfo = ItemInfoFromContainer(row);
 
            // 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();
                }
 
                DataGridCellInfo currentCell = CurrentCell;
                if (currentCell.ItemInfo != rowInfo)
                {
                    // Change the CurrentCell if the row is different
                    SetCurrentValueInternal(CurrentCellProperty, new DataGridCellInfo(rowInfo, ColumnFromDisplayIndex(0), this));
                }
                else
                {
                    if (_currentCellContainer != null && _currentCellContainer.IsEditing)
                    {
                        // End the pending edit even for the same row
                        EndEdit(CommitEditCommand, _currentCellContainer, DataGridEditingUnit.Cell, /* exitEditingMode = */ true);
                    }
                }
            }
 
            // Select a row when the mode is not None and the unit allows selecting rows
            if (CanSelectRows)
            {
                MakeFullRowSelection(rowInfo, /* 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>
        private void MakeFullRowSelection(ItemInfo info, 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)
                        {
                            int startIndex = _selectionAnchor.Value.ItemInfo.Index;
                            int endIndex = info.Index;
                            if (startIndex > endIndex)
                            {
                                // Ensure that startIndex is before endIndex
                                int temp = startIndex;
                                startIndex = endIndex;
                                endIndex = temp;
                            }
 
                            if ((startIndex >= 0) && (endIndex >= 0))
                            {
                                int numItemsSelected = _selectedItems.Count;
 
                                if (!minimalModify)
                                {
                                    bool clearedCells = false;
 
                                    // Unselect items not within the selection range
                                    for (int index = 0; index < numItemsSelected; index++)
                                    {
                                        ItemInfo itemInfo = _selectedItems[index];
                                        int itemIndex = itemInfo.Index;
 
                                        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.
                                            SelectionChange.Unselect(itemInfo);
 
                                            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 = CurrentCell.ItemInfo.Index;
                                    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++)
                                        {
                                            ItemInfo itemInfo = _selectedItems[index];
                                            int itemIndex = itemInfo.Index;
 
                                            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.
                                                SelectionChange.Unselect(itemInfo);
                                            }
                                        }
 
                                        _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)
                                    {
                                        SelectionChange.Select(ItemInfoFromIndex(index), true);
                                    }
                                }
 
                                IDisposable d = enumerator as IDisposable;
                                if (d != null)
                                {
                                    d.Dispose();
                                }
 
                                _selectedCells.AddRegion(startIndex, 0, endIndex - startIndex + 1, _columns.Count);
                            }
                        }
                    }
                    else
                    {
                        if (minimalModify && _selectedItems.Contains(info))
                        {
                            // Unselect the one item
                            UnselectItem(info);
                        }
                        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 (_editingRowInfo == info)
                            {
                                // ADO.Net bug workaround, see remarks.
                                int numColumns = _columns.Count;
                                if (numColumns > 0)
                                {
                                    _selectedCells.AddRegion(_editingRowInfo.Index, 0, 1, numColumns);
                                }
 
                                SelectItem(info, false);
                            }
                            else
                            {
                                // Select the item
                                SelectItem(info);
                            }
                        }
 
                        _selectionAnchor = new DataGridCellInfo(info.Clone(), 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>
        private void MakeCellSelection(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 = _selectionAnchor.Value.ItemInfo.Index;
                    int endIndex = cellInfo.ItemInfo.Index;
 
                    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 = CurrentCell.ItemInfo.Index;
                            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 == 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 = (_editingRowInfo != null && _editingRowInfo.Index == cellInfo.ItemInfo.Index);
                    if (!selectedCellsContainsCellInfo &&
                        singleRowOperation)
                    {
                        // ADO.Net bug workaround, see remarks.
                        selectedCellsContainsCellInfo = _selectedCells.Contains(_editingRowInfo.Index, cellInfoColumnIndex);
                    }
 
                    if (minimalModify && selectedCellsContainsCellInfo)
                    {
                        // Unselect the one cell
                        if (singleRowOperation)
                        {
                            // ADO.Net bug workaround, see remarks.
                            _selectedCells.RemoveRegion(_editingRowInfo.Index, cellInfoColumnIndex, 1, 1);
                        }
                        else
                        {
                            _selectedCells.Remove(cellInfo);
                        }
 
                        if ((SelectionUnit == 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 workaround, see remarks.
                            _selectedCells.AddRegion(_editingRowInfo.Index, cellInfoColumnIndex, 1, 1);
                        }
                        else
                        {
                            // Select the cell
                            _selectedCells.AddValidatedCell(cellInfo);
                        }
                    }
 
                    _selectionAnchor = new DataGridCellInfo(cellInfo);
                }
            }
        }
 
        private void SelectItem(ItemInfo info)
        {
            SelectItem(info, true);
        }
 
        private void SelectItem(ItemInfo info, bool selectCells)
        {
            if (selectCells)
            {
                using (UpdateSelectedCells())
                {
                    int itemIndex = info.Index;
                    int numColumns = _columns.Count;
                    if ((itemIndex >= 0) && (numColumns > 0))
                    {
                        _selectedCells.AddRegion(itemIndex, 0, 1, numColumns);
                    }
                }
            }
 
            UpdateSelectedItems(info, /* Add = */ true);
        }
 
        private void UnselectItem(ItemInfo info)
        {
            using (UpdateSelectedCells())
            {
                int itemIndex = info.Index;
                int numColumns = _columns.Count;
                if ((itemIndex >= 0) && (numColumns > 0))
                {
                    _selectedCells.RemoveRegion(itemIndex, 0, 1, numColumns);
                }
            }
 
            UpdateSelectedItems(info, /*Add = */ false);
        }
 
        /// <summary>
        ///     Adds or Removes from SelectedItems when deferred selection is not handled by the caller.
        /// </summary>
        private void UpdateSelectedItems(ItemInfo info, bool add)
        {
            bool updatingSelectedItems = IsUpdatingSelectedItems;
            if (!updatingSelectedItems)
            {
                BeginUpdateSelectedItems();
            }
 
            try
            {
                if (add)
                {
                    SelectedItemCollection.Add(info.Clone());
                }
                else
                {
                    SelectedItemCollection.Remove(info);
                }
            }
            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(DataGrid dataGrid)
            {
                _dataGrid = dataGrid;
                _wasUpdatingSelectedCells = _dataGrid.IsUpdatingSelectedCells;
                if (!_wasUpdatingSelectedCells)
                {
                    _dataGrid.BeginUpdateSelectedCells();
                }
            }
 
            public void Dispose()
            {
                GC.SuppressFinalize(this);
                if (!_wasUpdatingSelectedCells)
                {
                    _dataGrid.EndUpdateSelectedCells();
                }
            }
 
            private 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 DataGridSelectionUnit.FullRow:
                    case DataGridSelectionUnit.CellOrRowHeader:
                        return true;
 
                    case DataGridSelectionUnit.Cell:
                        return false;
                }
 
                Debug.Fail("Unknown SelectionUnit encountered.");
                return false;
            }
        }
 
        private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            _currentCellContainer = null;
            List<Tuple<int, int>> ranges = null;
 
            using (UpdateSelectedCells())
            {
                if (e.Action == NotifyCollectionChangedAction.Reset)
                {
                    ranges = new List<Tuple<int, int>>();
                    LocateSelectedItems(ranges);
                }
 
                // Send the change notification to the selected cells collection
                _selectedCells.OnItemsCollectionChanged(e, ranges);
            }
 
            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();
 
            ((DataGrid)d).UpdateVisualState();
        }
 
        /// <summary>
        /// Cells need to update their visual state when this property changes.
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void OnIsKeyboardFocusWithinChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Rows | DataGridNotificationTarget.RowHeaders | DataGridNotificationTarget.Cells);
        }
 
        /// <summary>
        ///     Called when a TextInput event is received.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnTextInput(TextCompositionEventArgs e)
        {
            base.OnTextInput(e);
 
            // Only handle text from ourselves or an item container
            if (!e.Handled && !String.IsNullOrEmpty(e.Text) && IsTextSearchEnabled)
            {
                bool shouldProcess = (e.OriginalSource == this);
                if (!shouldProcess)
                {
                    ItemsControl itemsControl = ItemsControlFromItemContainer(e.OriginalSource as DependencyObject);
                    shouldProcess = (itemsControl == this);
                    if (!shouldProcess)
                    {
                        DataGridCellsPresenter cellsPresenter = itemsControl as DataGridCellsPresenter;
                        if (cellsPresenter != null)
                        {
                            shouldProcess = (cellsPresenter.DataGridOwner == this);
                        }
                    }
                }
 
                if (shouldProcess)
                {
                    TextSearch instance = TextSearch.EnsureInstance(this);
 
                    if (instance != null)
                    {
                        instance.DoSearch(e.Text);
                        // Note: we always want to handle the event to denote that we
                        // actually did something.  We wouldn't want an AccessKey
                        // to get invoked just because there wasn't a match here.
                        e.Handled = true;
                    }
                }
            }
        }
 
        /// <summary>
        ///     Called when an item is being focused
        /// </summary>
        internal override bool FocusItem(ItemInfo info, ItemNavigateArgs itemNavigateArgs)
        {
            object item = info.Item;
            bool returnValue = false;
            if (item != null)
            {
                DataGridColumn column = CurrentColumn;
                if (column == null)
                {
                    SetCurrentItem(item);
                }
                else
                {
                    DataGridCell cell = TryFindCell(info, column);
                    if (cell != null)
                    {
                        cell.Focus();
                        if (ShouldSelectRowHeader)
                        {
                            HandleSelectionForRowHeaderAndDetailsInput(cell.RowOwner, /* startDragging = */ false);
                        }
                        else
                        {
                            HandleSelectionForCellInput(cell, /* startDragging = */ false, /* allowsExtendSelect = */ false, /* allowsMinimalSelect = */ false);
                        }
                    }
                }
            }
            if (itemNavigateArgs.DeviceUsed is KeyboardDevice)
            {
                KeyboardNavigation.ShowFocusVisual();
            }
            return returnValue;
        }
 
        /// <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;
            }
        }
 
        /// <summary>
        ///     Helper method which handles the arrow key down
        /// </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>
        private void OnArrowKeyDown(KeyEventArgs e)
        {
            DataGridCell currentCellContainer = CurrentCellContainer;
            if (currentCellContainer != null)
            {
                e.Handled = true;
                bool wasEditing = currentCellContainer.IsEditing;
                KeyboardNavigation keyboardNavigation = KeyboardNavigation.Current;
 
                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 && !keyboardNavigation.IsAncestorOfEx(this, nextFocusTarget))
                            {
                                Keyboard.Focus(nextFocusTarget as IInputElement);
                            }
 
                            return;
                        }
 
                        int currentDisplayIndex = this.CurrentColumn.DisplayIndex;
                        ItemInfo currentInfo = CurrentInfo;
                        int currentRowIndex = currentInfo.Index;
 
                        int nextDisplayIndex = currentDisplayIndex;
                        int nextRowIndex = currentRowIndex;
                        bool controlModifier = ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control);
 
                        // if we're at a boundary, try using default navigation - it can
                        // go to an element in a header/footer, rather than in another row.
                        if (!controlModifier && (e.Key == Key.Up || e.Key == Key.Down))
                        {
                            bool tryDefaultNavigation = false;
 
                            if (currentInfo.Item == CollectionView.NewItemPlaceholder)
                            {
                                tryDefaultNavigation = true;
                            }
                            else if (IsGrouping)
                            {
                                GroupItem currentGroupItem = DataGridHelper.FindVisualParent<GroupItem>(currentCellContainer);
                                if (currentGroupItem != null)
                                {
                                    CollectionViewGroupInternal cvg = ItemContainerGenerator.ItemFromContainer(currentGroupItem) as CollectionViewGroupInternal;
                                    if (cvg != null && cvg.Items.Count > 0)
                                    {
                                        // Try default navigation if current item is first or last item of a group.
                                        if ((e.Key == Key.Up && ItemsControl.EqualsEx(cvg.Items[0], currentInfo.Item)) ||
                                            (e.Key == Key.Down && ItemsControl.EqualsEx(cvg.Items[cvg.Items.Count - 1], currentInfo.Item)))
                                        {
                                            // there might be duplicate items, so double-check the index
                                            // (this is mildly expensive, which is why we put it off until necessary)
                                            int indexOfGroupBoundary = cvg.LeafIndexFromItem(null, 0);
                                            if (e.Key == Key.Down)
                                            {
                                                indexOfGroupBoundary += cvg.ItemCount - 1;
                                            }
                                            if (currentRowIndex == indexOfGroupBoundary)
                                            {
                                                tryDefaultNavigation = true;
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if ((e.Key == Key.Up && currentRowIndex == 0) ||
                                    (e.Key == Key.Down && currentRowIndex == Items.Count - 1))
                                {
                                    tryDefaultNavigation = true;
                                }
                            }
 
                            if (tryDefaultNavigation)
                            {
                                if (TryDefaultNavigation(e, currentInfo))
                                    return;
                            }
                        }
 
                        // 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)
                                    {
                                        DataGridColumn column = ColumnFromDisplayIndex(nextDisplayIndex);
                                        if (column.IsVisible)
                                        {
                                            break;
                                        }
 
                                        nextDisplayIndex--;
                                    }
 
                                    if (nextDisplayIndex < 0)
                                    {
                                        if (keyboardNavigationMode == KeyboardNavigationMode.Cycle)
                                        {
                                            nextDisplayIndex = InternalColumns.LastVisibleDisplayIndex;
                                        }
                                        else if (keyboardNavigationMode == KeyboardNavigationMode.Contained)
                                        {
                                            DependencyObject nextFocusTarget = keyboardNavigation.PredictFocusedElement(currentCellContainer, KeyToTraversalDirection(rtlKey),
                                                treeViewNavigation: false, considerDescendants: false);
                                            if (nextFocusTarget != null && keyboardNavigation.IsAncestorOfEx(this, nextFocusTarget))
                                            {
                                                Keyboard.Focus(nextFocusTarget as IInputElement);
                                            }
                                            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)
                                    {
                                        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)
                                        {
                                            DependencyObject nextFocusTarget = keyboardNavigation.PredictFocusedElement(currentCellContainer, KeyToTraversalDirection(rtlKey),
                                                treeViewNavigation: false, considerDescendants: false);
                                            if (nextFocusTarget != null && keyboardNavigation.IsAncestorOfEx(this, nextFocusTarget))
                                            {
                                                Keyboard.Focus(nextFocusTarget as IInputElement);
                                            }
                                            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)
                                        {
                                            DependencyObject nextFocusTarget = keyboardNavigation.PredictFocusedElement(currentCellContainer, KeyToTraversalDirection(rtlKey),
                                                treeViewNavigation: false, considerDescendants: false);
                                            if (nextFocusTarget != null && keyboardNavigation.IsAncestorOfEx(this, nextFocusTarget))
                                            {
                                                Keyboard.Focus(nextFocusTarget as IInputElement);
                                            }
                                            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)
                                        {
                                            DependencyObject nextFocusTarget = keyboardNavigation.PredictFocusedElement(currentCellContainer, KeyToTraversalDirection(rtlKey),
                                                treeViewNavigation: false, considerDescendants: false);
                                            if (nextFocusTarget != null && keyboardNavigation.IsAncestorOfEx(this, nextFocusTarget))
                                            {
                                                Keyboard.Focus(nextFocusTarget as IInputElement);
                                            }
                                            return;
                                        }
                                        else // Continue, Local, None - move focus out of the datagrid
                                        {
                                            MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
                                            return;
                                        }
                                    }
                                }
 
                                break;
                        }
 
                        DataGridColumn nextColumn = ColumnFromDisplayIndex(nextDisplayIndex);
                        ItemInfo nextInfo = ItemInfoFromIndex(nextRowIndex);
                        ScrollCellIntoView(nextInfo, nextColumn);
                        DataGridCell nextCellContainer = TryFindCell(nextInfo, nextColumn);
 
                        if (nextCellContainer == null || nextCellContainer == currentCellContainer || !nextCellContainer.Focus())
                        {
                            return;
                        }
                    }
 
                    else
                    {
                        if (TryDefaultNavigation(e, null))
                            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);
                    }
                }
            }
        }
 
        private bool TryDefaultNavigation(KeyEventArgs e, ItemInfo currentInfo)
        {
            FrameworkElement focusContainer;
            FrameworkElement startingElement = Keyboard.FocusedElement as FrameworkElement;
 
            if (startingElement != null && ItemsHost.IsAncestorOf(startingElement))
            {
                PrepareNavigateByLine(currentInfo,
                    startingElement,
                    (e.Key == Key.Up ? FocusNavigationDirection.Up : FocusNavigationDirection.Down),
                    new ItemNavigateArgs(e.KeyboardDevice, Keyboard.Modifiers),
                    out focusContainer);
 
                if (focusContainer != null)
                {
                    DataGridRow focusRow = DataGridHelper.FindVisualParent<DataGridRow>(focusContainer);
                    if (focusRow == null ||
                        focusRow.DataGridOwner != this)
                    {
                        focusContainer.Focus();
                        return true;
                    }
                }
            }
 
            return false;
        }
 
        /// <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.
            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);
 
                    // 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);
                        }
 
                        // 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)
                        {
                            DataGridCell newCell = GetCellForSelectAndEditOnFocusMove();
 
                            if (newCell != null &&
                                newCell.RowDataItem == currentCellContainer.RowDataItem)
                            {
                                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)
        {
            DataGridCell currentCellContainer = CurrentCellContainer;
            if ((currentCellContainer != null) && (_columns.Count > 0))
            {
                e.Handled = true;
 
                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 = CurrentInfo.Index;
                    if (index < 0)
                    {
                        index = Items.IndexOf(CurrentItem);
                    }
                    index = Math.Max(0, Math.Min(numItems - 1, index + (shiftModifier ? -1 : 1)));
 
                    if (index < numItems)
                    {
                        ItemInfo rowInfo = ItemInfoFromIndex(index);
                        ScrollIntoView(rowInfo, column);
 
                        if (!ItemsControl.EqualsEx(CurrentCell.Item, rowInfo.Item))
                        {
                            // Focus the new cell
                            SetCurrentValueInternal(CurrentCellProperty, new DataGridCellInfo(rowInfo, 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 DataGridCell GetCellForSelectAndEditOnFocusMove()
        {
            DataGridCell newCell = Keyboard.FocusedElement as 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, DataGridCell oldCell, bool wasEditing, bool allowsExtendSelect, bool ignoreControlKey)
        {
            DataGridCell newCell = GetCellForSelectAndEditOnFocusMove();
 
            if ((newCell != null) && (newCell.DataGridOwner == this))
            {
                if (ignoreControlKey || ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == 0))
                {
                    if (ShouldSelectRowHeader && allowsExtendSelect)
                    {
                        HandleSelectionForRowHeaderAndDetailsInput(newCell.RowOwner, /* startDragging = */ false);
                    }
                    else
                    {
                        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);
 
                if (controlModifier)
                {
                    ScrollViewer scrollHost = InternalScrollHost;
                    if (scrollHost != null)
                    {
                        // This operation is particularly relevant when the items in the DataGrid are
                        // grouped. We want the Group Headers to be brought into view when Ctrl+Home
                        // is pressed.
 
                        if (homeKey)
                        {
                            scrollHost.ScrollToHome();
                        }
                        else
                        {
                            scrollHost.ScrollToEnd();
                        }
                    }
                }
 
                // Go to the first or last cell
                ItemInfo info = controlModifier ? ItemInfoFromIndex(homeKey ? 0 : Items.Count - 1) : CurrentInfo;
                DataGridColumn column = ColumnFromDisplayIndex(homeKey ? InternalColumns.FirstVisibleDisplayIndex : InternalColumns.LastVisibleDisplayIndex);
 
                ScrollCellIntoView(info, column);
 
                DataGridCell cell = TryFindCell(info, column);
                if (cell != null)
                {
                    cell.Focus();
                    if (ShouldSelectRowHeader)
                    {
                        HandleSelectionForRowHeaderAndDetailsInput(cell.RowOwner, /* startDragging = */ false);
                    }
                    else
                    {
                        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)
            {
                e.Handled = true;
 
                ItemInfo currentInfo = CurrentInfo;
 
                if (VirtualizingPanel.GetScrollUnit(this) == ScrollUnit.Item && !IsGrouping)
                {
                    int rowIndex = currentInfo.Index;
                    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
                        ItemInfo targetInfo = ItemInfoFromIndex(targetIndex);
                        DataGridColumn currentColumn = CurrentColumn;
 
                        if (currentColumn == null)
                        {
                            OnBringItemIntoView(targetInfo);
                            SetCurrentItem(targetInfo.Item);
                        }
                        else
                        {
                            ScrollCellIntoView(targetInfo, currentColumn);
                            DataGridCell cell = TryFindCell(targetInfo, currentColumn);
                            if (cell != null)
                            {
                                cell.Focus();
                                if (ShouldSelectRowHeader)
                                {
                                    HandleSelectionForRowHeaderAndDetailsInput(cell.RowOwner, /* startDragging = */ false);
                                }
                                else
                                {
                                    HandleSelectionForCellInput(cell, /* startDragging = */ false, /* allowsExtendSelect = */ true, /* allowsMinimalSelect = */ false);
                                }
                            }
                        }
                    }
                }
                else
                {
                    FrameworkElement targetElement;
                    FocusNavigationDirection direction = e.Key == Key.PageUp ? FocusNavigationDirection.Up : FocusNavigationDirection.Down;
                    ItemInfo startingInfo = currentInfo;
                    FrameworkElement startingElement = null;
                    if (IsGrouping)
                    {
                        startingElement = Keyboard.FocusedElement as FrameworkElement;
                        if (startingElement != null)
                        {
                            startingInfo = null;
                            DataGridRow startingRow = startingElement as DataGridRow;
                            if (startingRow == null)
                            {
                                startingRow = DataGridHelper.FindVisualParent<DataGridRow>(startingElement);
                            }
 
                            if (startingRow != null)
                            {
                                DataGrid focusedDataGrid = ItemsControl.ItemsControlFromItemContainer(startingRow) as DataGrid;
                                if (focusedDataGrid == this)
                                {
                                    startingInfo = ItemInfoFromContainer(startingRow);
                                }
                            }
                        }
                    }
                    PrepareToNavigateByPage(startingInfo, startingElement, direction, new ItemNavigateArgs(Keyboard.PrimaryDevice, Keyboard.Modifiers), out targetElement);
 
                    DataGridRow targetRowElement = targetElement as DataGridRow;
                    if (targetRowElement == null)
                    {
                        targetRowElement = DataGridHelper.FindVisualParent<DataGridRow>(targetElement);
                    }
 
                    if (targetRowElement != null)
                    {
                        // Scroll the target row into view, keeping the current column
                        ItemInfo targetInfo = ItemInfoFromContainer(targetRowElement);
                        DataGridColumn currentColumn = CurrentColumn;
 
                        if (currentColumn == null)
                        {
                            SetCurrentItem(targetInfo.Item);
                        }
                        else
                        {
                            DataGridCell cell = TryFindCell(targetInfo, currentColumn);
                            if (cell != null)
                            {
                                cell.Focus();
                                if (ShouldSelectRowHeader)
                                {
                                    HandleSelectionForRowHeaderAndDetailsInput(cell.RowOwner, /* startDragging = */ false);
                                }
                                else
                                {
                                    HandleSelectionForCellInput(cell, /* startDragging = */ false, /* allowsExtendSelect = */ true, /* allowsMinimalSelect = */ false);
                                }
                            }
                        }
                    }
                    else if (targetElement != null)
                    {
                        targetElement.Focus();
                    }
                }
            }
        }
 
        /// <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)
                            {
                                DataGridRow row = MouseOverRow;
                                if ((row != null) && (row.Item != CurrentItem))
                                {
                                    // Continue a row header drag to the given row
                                    HandleSelectionForRowHeaderAndDetailsInput(row, /* startDragging = */ false);
                                    SetCurrentItem(row.Item);
                                    e.Handled = true;
                                }
                            }
                            else
                            {
                                DataGridCell cell = MouseOverCell;
                                if (cell == null)
                                {
                                    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
                                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);
                                    SetCurrentItem(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
                                StartAutoScroll();
                            }
                        }
                    }
                }
                else
                {
                    // The mouse button is up, end the drag operation
                    EndDragging();
                }
            }
        }
 
        private static void OnAnyMouseUpThunk(object sender, MouseButtonEventArgs e)
        {
            ((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)
        {
            //
            // We do not want to change selection when the DataGrid is disabled.
            //
            if (!IsEnabled)
            {
                return;
            }
 
            DataGridCell cell = null;
            DataGridRowHeader rowHeader = null;
            UIElement sourceElement = e.OriginalSource as UIElement;
            while (sourceElement != null)
            {
                cell = sourceElement as DataGridCell;
                if (cell != null)
                {
                    break;
                }
 
                rowHeader = sourceElement as 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)
            {
                DataGridRow parentRow = rowHeader.ParentRow;
                if (parentRow != null && !parentRow.IsSelected)
                {
                    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 DataGridRow GetRowNearMouse()
        {
            Debug.Assert(RelativeMousePosition != RelativeMousePositions.Over, "The mouse is not supposed to be over the DataGrid.");
 
            Panel itemsHost = InternalItemsHost;
            if (itemsHost != null)
            {
                bool isGrouping = IsGrouping;
 
                // Iterate from the end to the beginning since it is more common
                // to drag toward the end.
                for (int i = (isGrouping ? Items.Count - 1 : itemsHost.Children.Count - 1); i >= 0; i--)
                {
                    DataGridRow row = null;
 
                    if (isGrouping)
                    {
                        // If Grouping is enabled, Children of itemsHost are not always DataGridRows
                        row = ItemContainerGenerator.ContainerFromIndex(i) as DataGridRow;
                    }
                    else
                    {
                        row = itemsHost.Children[i] as 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>
        private DataGridCell GetCellNearMouse()
        {
            Panel itemsHost = InternalItemsHost;
            if (itemsHost != null)
            {
                Rect itemsHostBounds = new Rect(new Point(), itemsHost.RenderSize);
                double closestDistance = Double.PositiveInfinity;
                DataGridCell closestCell = null;
                bool isMouseInCorner = IsMouseInCorner(RelativeMousePosition);
                bool isGrouping = IsGrouping;
 
                // Iterate from the end to the beginning since it is more common
                // to drag toward the end.
                for (int i = (isGrouping ? Items.Count - 1 : itemsHost.Children.Count - 1); i >= 0; i--)
                {
                    DataGridRow row = null;
 
                    if (isGrouping)
                    {
                        // If Grouping is enabled, Children of itemsHost are not always DataGridRows
                        row = ItemContainerGenerator.ContainerFromIndex(i) as DataGridRow;
                    }
                    else
                    {
                        row = itemsHost.Children[i] as DataGridRow;
                    }
 
                    if (row != null)
                    {
                        DataGridCellsPresenter cellsPresenter = row.CellsPresenter;
                        if (cellsPresenter != null)
                        {
                            // Go through all of the instantiated cells and find the closest cell
                            ContainerTracking<DataGridCell> cellTracker = cellsPresenter.CellTrackingRoot;
                            while (cellTracker != null)
                            {
                                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
                            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
                                        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, 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 DataGridRow MouseOverRow
        {
            get
            {
                UIElement element = Mouse.DirectlyOver as UIElement;
                DataGridRow row = null;
 
                while (element != null)
                {
                    row = DataGridHelper.FindVisualParent<DataGridRow>(element);
                    if (row == null || row.DataGridOwner == this)
                        break;
 
                    element = VisualTreeHelper.GetParent(row) as UIElement;
                }
 
                return row;
            }
        }
 
        // The cell that the mouse is over.
        private DataGridCell MouseOverCell
        {
            get
            {
                UIElement element = Mouse.DirectlyOver as UIElement;
                DataGridCell cell = null;
 
                while (element != null)
                {
                    cell = DataGridHelper.FindVisualParent<DataGridCell>(element);
                    if (cell == null || cell.DataGridOwner == this)
                        break;
 
                    element = VisualTreeHelper.GetParent(cell) as UIElement;
                }
 
                return cell;
            }
        }
 
        /// <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 System.Windows.Automation.Peers.DataGridAutomationPeer(this);
        }
 
        private CellAutomationValueHolder GetCellAutomationValueHolder(object item, DataGridColumn column)
        {
            CellAutomationValueHolder cellAutomationValueHolder;
 
            if (_editingRowInfo == null || !ItemsControl.EqualsEx(item, _editingRowInfo.Item) ||
                !_editingCellAutomationValueHolders.TryGetValue(column, out cellAutomationValueHolder))
            {
                DataGridCell cell = TryFindCell(item, column);
                cellAutomationValueHolder =
                    (cell != null) ? new CellAutomationValueHolder(cell)
                                   : new CellAutomationValueHolder(item, column);
            }
 
            return cellAutomationValueHolder;
        }
 
        internal string GetCellAutomationValue(object item, DataGridColumn column)
        {
            CellAutomationValueHolder cellAutomationValueHolder = GetCellAutomationValueHolder(item, column);
            return cellAutomationValueHolder.Value;
        }
 
        internal object GetCellClipboardValue(object item, DataGridColumn column)
        {
            CellAutomationValueHolder cellAutomationValueHolder = GetCellAutomationValueHolder(item, column);
            return cellAutomationValueHolder.GetClipboardValue();
        }
 
        internal void SetCellAutomationValue(object item, DataGridColumn column, string value)
        {
            SetCellValue(item, column, value, false /*clipboard*/);
        }
 
        internal void SetCellClipboardValue(object item, DataGridColumn column, object value)
        {
            SetCellValue(item, column, value, true /*clipboard*/);
        }
 
        private void SetCellValue(object item, DataGridColumn column, object value, bool clipboard)
        {
            // Put focus on the cell
            CurrentCellContainer = TryFindCell(item, column);
            if (CurrentCellContainer == null)
            {
                // If current cell has been virtualized away - scroll it into view
                ScrollCellIntoView(NewItemInfo(item), column);
                CurrentCellContainer = TryFindCell(item, column);
            }
 
            if (CurrentCellContainer == null)
            {
                return;
            }
 
            // Check if trying to edit cell while previously edited cell has an error.
            // BeginEdit will fail if there is a validation error.
            if (BeginEdit())
            {
                CellAutomationValueHolder holder;
                if (_editingCellAutomationValueHolders.TryGetValue(column, out holder))
                {
                    holder.SetValue(this, value, clipboard);   // calls CommitEdit
                }
                else
                {
                    // if there's no automation value-holder, we can't honor the SetValue request,
                    // so just cancel the edit
                    CancelEdit();
                }
            }
        }
 
        // create an automation value-holder for the given cell
        private void EnsureCellAutomationValueHolder(DataGridCell cell)
        {
            if (!_editingCellAutomationValueHolders.ContainsKey(cell.Column))
            {
                _editingCellAutomationValueHolders.Add(cell.Column, new CellAutomationValueHolder(cell));
            }
        }
 
        // update the automation/clipboard value for the given cell
        private void UpdateCellAutomationValueHolder(DataGridCell cell)
        {
            CellAutomationValueHolder holder;
            if (_editingCellAutomationValueHolders.TryGetValue(cell.Column, out holder))
            {
                holder.TrackValue();
            }
        }
 
        // release the automation value-holders, after notifying them one final time.
        // This is called at CommitEdit and CancelEdit time.
        private void ReleaseCellAutomationValueHolders()
        {
            foreach (KeyValuePair<DataGridColumn, CellAutomationValueHolder> kvp in _editingCellAutomationValueHolders)
            {
                kvp.Value.TrackValue();
            }
 
            _editingCellAutomationValueHolders.Clear();
        }
 
        #region CellAutomationHelper
 
        internal class CellAutomationValueHolder
        {
            // Track the value of an actual cell
            public CellAutomationValueHolder(DataGridCell cell)
            {
                _cell = cell;
                Initialize(cell.RowDataItem, cell.Column);
            }
 
            // Track the value of an (item,column) that has no corresponding cell.
            // This is used only to get the value from a virtualized item.
            public CellAutomationValueHolder(object item, DataGridColumn column)
            {
                Initialize(item, column);
            }
 
            private void Initialize(object item, DataGridColumn column)
            {
                _item = item;
                _column = column;
                _value = GetValue();
            }
 
            public string Value
            {
                get { return _value; }
            }
 
            // called by DataGrid when the value has potentially changed
            public void TrackValue()
            {
                string newValue = GetValue();
 
                if (newValue != _value)
                {
                    if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged))
                    {
                        DataGridColumn column = (_cell != null) ? _cell.Column : _column;
                        if (column.DataGridOwner != null)
                        {
                            DataGridAutomationPeer peer = DataGridAutomationPeer.FromElement(column.DataGridOwner) as DataGridAutomationPeer;
                            if (peer != null)
                            {
                                object item = (_cell != null) ? _cell.DataContext : _item;
                                DataGridItemAutomationPeer dataGridItemAutomationPeer = peer.FindOrCreateItemAutomationPeer(item) as DataGridItemAutomationPeer;
                                if (dataGridItemAutomationPeer != null)
                                {
                                    DataGridCellItemAutomationPeer cellPeer = dataGridItemAutomationPeer.GetOrCreateCellItemPeer(column);
                                    if (cellPeer != null)
                                    {
                                        cellPeer.RaisePropertyChangedEvent(ValuePatternIdentifiers.ValueProperty, _value, newValue);
                                    }
                                }
                            }
                        }
                    }
 
                    _value = newValue;
                }
            }
 
            // get the current value
            private string GetValue()
            {
                string value;
                if (_column.ClipboardContentBinding == null)
                {
                    value = null;
                }
                else if (_inSetValue)
                {
                    // when setting the value from automation, there's a re-entrant call:
                    //      SetValue->CommitEdit->TrackValue->GetValue.
                    // Inside this re-entrant call, there's already a binding in place,
                    // and it has the value we want.
                    value = (string)_cell.GetValue(CellContentProperty);
                }
                else
                {
                    FrameworkElement target;
                    if (_cell != null)
                    {
                        // Bind the CellContent property of the cell itself.
                        // The binding will participate in the row's BindingGroup, and
                        // pick up a proposed value (if any).
                        target = _cell;
                    }
                    else
                    {
                        // lacking a cell, bind a dummy element directly to the data item
                        target = new FrameworkElement();
                        target.DataContext = _item;
                    }
 
                    BindingOperations.SetBinding(target, CellContentProperty, _column.ClipboardContentBinding);
                    value = (string)target.GetValue(CellContentProperty);
                    BindingOperations.ClearBinding(target, CellContentProperty);
                }
 
                return value;
            }
 
            // get the current clipboard value.  This is similar to the "content" value,
            // except that it has type Object (rather than String).  We don't track
            // the clipboard value, since no one is interested in changes.  Instead,
            // we recompute it every time it's requested.
            public object GetClipboardValue()
            {
                object value;
                if (_column.ClipboardContentBinding == null)
                {
                    value = null;
                }
                else
                {
                    FrameworkElement target;
                    if (_cell != null)
                    {
                        // Bind the CellClipboard property of the cell itself.
                        // The binding will participate in the row's BindingGroup, and
                        // pick up a proposed value (if any).
                        target = _cell;
                    }
                    else
                    {
                        // lacking a cell, bind a dummy element directly to the data item
                        target = new FrameworkElement();
                        target.DataContext = _item;
                    }
 
                    BindingOperations.SetBinding(target, CellClipboardProperty, _column.ClipboardContentBinding);
                    value = target.GetValue(CellClipboardProperty);
                    BindingOperations.ClearBinding(target, CellClipboardProperty);
                }
 
                return value;
            }
 
            // set the value (used when setting value via automation)
            public void SetValue(DataGrid dataGrid, object value, bool clipboard)
            {
                if (_column.ClipboardContentBinding == null)
                    return;
 
                _inSetValue = true;
 
                // add a two-way binding (it joins the BindingGroup)
                DependencyProperty dp = clipboard ? CellClipboardProperty : CellContentProperty;
                BindingBase binding = _column.ClipboardContentBinding.Clone(BindingMode.TwoWay);
                BindingOperations.SetBinding(_cell, dp, binding);
 
                // set the new value
                _cell.SetValue(dp, value);
 
                // do a cell-level commit - this will validate the new value
                dataGrid.CommitEdit();
 
                // whether valid or not, remove the binding.  The binding group will
                // remember the proposed value
                BindingOperations.ClearBinding(_cell, dp);
 
                _inSetValue = false;
            }
 
            private static DependencyProperty CellContentProperty = DependencyProperty.RegisterAttached("CellContent", typeof(string), typeof(CellAutomationValueHolder));
            private static DependencyProperty CellClipboardProperty = DependencyProperty.RegisterAttached("CellClipboard", typeof(object), typeof(CellAutomationValueHolder));
 
            # region Data
 
            private DataGridCell _cell;
            private DataGridColumn _column;
            private object _item;
            private string _value;
            private bool _inSetValue;
 
            #endregion
        }
 
        #endregion
 
        #endregion
 
        #region Cell Info
 
        internal DataGridCell TryFindCell(DataGridCellInfo info)
        {
            // Does not de-virtualize cells
            return TryFindCell(LeaseItemInfo(info.ItemInfo), info.Column);
        }
 
        internal DataGridCell TryFindCell(ItemInfo info, DataGridColumn column)
        {
            // Does not de-virtualize cells
            DataGridRow row = (DataGridRow)info.Container;
            int columnIndex = _columns.IndexOf(column);
            if ((row != null) && (columnIndex >= 0))
            {
                return row.TryGetCell(columnIndex);
            }
 
            return null;
        }
 
        internal DataGridCell TryFindCell(object item, DataGridColumn column)
        {
            // Does not de-virtualize cells
            DataGridRow row = (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(DataGrid),
                new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnCanUserSortColumnsPropertyChanged), new CoerceValueCallback(OnCoerceCanUserSortColumns)));
 
        /// <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); }
        }
 
        private static object OnCoerceCanUserSortColumns(DependencyObject d, object baseValue)
        {
            DataGrid dataGrid = (DataGrid)d;
            if (DataGridHelper.IsPropertyTransferEnabled(dataGrid, CanUserSortColumnsProperty) &&
                DataGridHelper.IsDefaultValue(dataGrid, CanUserSortColumnsProperty) &&
                dataGrid.Items.CanSort == false)
            {
                return false;
            }
            return baseValue;
        }
 
        private static void OnCanUserSortColumnsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DataGrid dataGrid = (DataGrid)d;
            DataGridHelper.TransferProperty(dataGrid, CanUserSortColumnsProperty);
            OnNotifyColumnPropertyChanged(d, e);
        }
 
        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(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(DataGridColumn sortColumn)
        {
            Debug.Assert(sortColumn != null, "column should not be null");
 
            if (!CanUserSortColumns || !sortColumn.CanUserSort)
            {
                return;
            }
 
            if (CommitAnyEdit())
            {
                PrepareForSort(sortColumn);
 
                DataGridSortingEventArgs eventArgs = new DataGridSortingEventArgs(sortColumn);
                OnSorting(eventArgs);
 
                if (Items.NeedsRefresh)
                {
                    try
                    {
                        Items.Refresh();
                    }
                    catch (InvalidOperationException invalidOperationException)
                    {
                        Items.SortDescriptions.Clear();
                        throw new InvalidOperationException(SR.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(DataGridColumn sortColumn)
        {
            if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
            {
                return;
            }
 
            if (Columns != null)
            {
                foreach (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(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))
            {
                try
                {
                    using (Items.DeferRefresh())
                    {
                        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.Equals(Items.SortDescriptions[i].PropertyName, sortPropertyName, StringComparison.Ordinal) &&
                                    (GroupingSortDescriptionIndices == null ||
                                    !GroupingSortDescriptionIndices.Contains(i)))
                                {
                                    descriptorIndex = i;
                                    break;
                                }
                            }
                        }
 
                        SortDescription sortDescription = new SortDescription(sortPropertyName, sortDirection);
                        if (descriptorIndex >= 0)
                        {
                            Items.SortDescriptions[descriptorIndex] = sortDescription;
                        }
                        else
                        {
                            Items.SortDescriptions.Add(sortDescription);
                        }
 
                        if (clearExistingSortDescriptions || !_sortingStarted)
                        {
                            RegenerateGroupingSortDescriptions();
                            _sortingStarted = true;
                        }
                    }
                    column.SortDirection = sortDirection;
                }
                catch (InvalidOperationException invalidOperationException)
                {
                    TraceData.TraceAndNotify(TraceEventType.Error,
                                    TraceData.CannotSort(sortPropertyName),
                                    invalidOperationException);
                    Items.SortDescriptions.Clear();
                }
            }
        }
 
        /// <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)
        {
            EnqueueNewItemMarginComputation();
            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<DataGridAutoGeneratingColumnEventArgs> AutoGeneratingColumn;
 
        /// <summary>
        ///     The DependencyProperty that represents the AutoGenerateColumns property.
        /// </summary>
        public static readonly DependencyProperty AutoGenerateColumnsProperty =
            DependencyProperty.Register("AutoGenerateColumns", typeof(bool), typeof(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(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);
 
                // always use an ItemBindingGroup
                EnsureItemBindingGroup();
 
                // always turn on SharesProposedValues
                ItemBindingGroup.SharesProposedValues = true;
            }
            else if (DeferAutoGeneration && AutoGenerateColumns)
            {
                // Try to generate auto columns if it was deferred earlier.
                AddAutoColumns();
            }
 
            return base.MeasureOverride(availableSize);
        }
 
        // Set the ItemBindingGroup property, if the user hasn't done so already
        private void EnsureItemBindingGroup()
        {
            if (ItemBindingGroup == null)
            {
                _defaultBindingGroup = new BindingGroup();
                SetCurrentValue(ItemBindingGroupProperty, _defaultBindingGroup);
            }
        }
 
        /// <summary>
        ///     Helper method to clear SortDescriptions and all related
        ///     member when ItemsSource changes
        /// </summary>
        private void ClearSortDescriptionsOnItemsSourceChange()
        {
            Items.SortDescriptions.Clear();
            _sortingStarted = false;
            List<int> groupingSortDescriptionIndices = GroupingSortDescriptionIndices;
            if (groupingSortDescriptionIndices != null)
            {
                groupingSortDescriptionIndices.Clear();
            }
            foreach (DataGridColumn column in Columns)
            {
                column.SortDirection = null;
            }
        }
 
        /// <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)
        {
            DataGrid dataGrid = (DataGrid)d;
            if (baseValue != dataGrid._cachedItemsSource && dataGrid._cachedItemsSource != null)
            {
                dataGrid.ClearSortDescriptionsOnItemsSourceChange();
            }
 
            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)
            {
                ClearSortDescriptionsOnItemsSourceChange();
            }
 
            _cachedItemsSource = newValue;
 
            using (UpdateSelectedCells())
            {
                // Selector will try to maintain the previous row selection.
                // Keep SelectedCells in sync.
                List<Tuple<int, int>> ranges = new List<Tuple<int, int>>();
                LocateSelectedItems(ranges);
                _selectedCells.RestoreOnlyFullRows(ranges);
            }
 
            if (AutoGenerateColumns == true)
            {
                RegenerateAutoColumns();
            }
 
            InternalColumns.RefreshAutoWidthColumns = true;
            InternalColumns.InvalidateColumnWidthsComputation();
 
            CoerceValue(CanUserAddRowsProperty);
            CoerceValue(CanUserDeleteRowsProperty);
            DataGridHelper.TransferProperty(this, CanUserSortColumnsProperty);
 
            ResetRowHeaderActualWidth();
 
            UpdateNewItemPlaceholder(/* isAddingNewItem = */ false);
 
            HasCellValidationError = false;
            HasRowValidationError = false;
        }
 
        /// <summary>
        ///     The flag which determines whether the columns generation is deferred
        /// </summary>
        private bool DeferAutoGeneration
        {
            get;
            set;
        }
 
        /// <summary>
        ///     Performs column auto generation and updates validation flags
        ///     on items change.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);
 
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                if (DeferAutoGeneration)
                {
                    // Add Auto columns only if it was deferred earlier
                    AddAutoColumns();
                }
            }
            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>
        ///     Adjust ItemInfos when the Items property changes.
        /// </summary>
        internal override void AdjustItemInfoOverride(NotifyCollectionChangedEventArgs e)
        {
            List<ItemInfo> list = new List<ItemInfo>();
 
            if (_selectionAnchor != null)
                list.Add(_selectionAnchor.Value.ItemInfo);
 
            if (_editingRowInfo != null)
                list.Add(_editingRowInfo);
 
            if (CellInfoNeedsAdjusting(CurrentCell))
                list.Add(CurrentCell.ItemInfo);
 
            AdjustItemInfos(e, list);
            base.AdjustItemInfoOverride(e);
        }
 
        /// <summary>
        ///     Adjust ItemInfos when the generator finishes.
        /// </summary>
        internal override void AdjustItemInfosAfterGeneratorChangeOverride()
        {
            List<ItemInfo> list = new List<ItemInfo>();
 
            if (_selectionAnchor != null)
                list.Add(_selectionAnchor.Value.ItemInfo);
 
            if (_editingRowInfo != null)
                list.Add(_editingRowInfo);
 
            if (CellInfoNeedsAdjusting(CurrentCell))
                list.Add(CurrentCell.ItemInfo);
 
            AdjustItemInfosAfterGeneratorChange(list, claimUniqueContainer: false);
            base.AdjustItemInfosAfterGeneratorChangeOverride();
            AdjustPendingInfos();
        }
 
        private static bool CellInfoNeedsAdjusting(DataGridCellInfo cellInfo)
        {
            ItemsControl.ItemInfo info = cellInfo.ItemInfo;
            return (info != null) && (info.Index != -1);
        }
 
        // If pending items now have a known index, adjust selected cells
        private void AdjustPendingInfos()
        {
            int columnCount;
            if (_pendingInfos != null && _pendingInfos.Count > 0 && (columnCount = _columns.Count) > 0)
            {
                using (UpdateSelectedCells())
                {
                    for (int i = _pendingInfos.Count - 1; i >= 0; --i)
                    {
                        ItemInfo info = _pendingInfos[i];
                        if (info.Index >= 0)
                        {
                            _pendingInfos.RemoveAt(i);
                            _selectedCells.AddRegion(info.Index, 0, 1, columnCount);
                        }
                    }
                }
            }
        }
 
        /// <summary>
        /// Method which generated auto columns and adds to the data grid.
        /// </summary>
        private void AddAutoColumns()
        {
            ReadOnlyCollection<ItemPropertyInfo> itemProperties = ((IItemProperties)Items).ItemProperties;
            if (itemProperties == null && DataItemsCount == 0)
            {
                // do deferred generation
                DeferAutoGeneration = true;
            }
            else if (!_measureNeverInvoked)
            {
                DataGrid.GenerateColumns(
                    itemProperties,
                    this,
                    null);
 
                DeferAutoGeneration = false;
 
                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<DataGridColumn> GenerateColumns(IItemProperties itemProperties)
        {
            ArgumentNullException.ThrowIfNull(itemProperties);
 
            Collection<DataGridColumn> columnCollection = new Collection<DataGridColumn>();
            DataGrid.GenerateColumns(
                itemProperties.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="itemProperties"></param>
        /// <param name="dataGrid"></param>
        /// <param name="columnCollection"></param>
        private static void GenerateColumns(
            ReadOnlyCollection<ItemPropertyInfo> itemProperties,
            DataGrid dataGrid,
            Collection<DataGridColumn> columnCollection)
        {
            Debug.Assert(dataGrid != null || columnCollection != null, "Both dataGrid and columnCollection cannot not be null at the same time");
 
            if (itemProperties != null &&
                itemProperties.Count > 0)
            {
                foreach (ItemPropertyInfo itemProperty in itemProperties)
                {
                    DataGridColumn dataGridColumn = 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.
                        DataGridAutoGeneratingColumnEventArgs eventArgs = new 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;
 
            DataGrid dataGrid = (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(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)
        {
            DataGrid dataGrid = (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)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.ColumnCollection | DataGridNotificationTarget.ColumnHeadersPresenter | DataGridNotificationTarget.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(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()
        {
            // If a new template has just been generated then
            // be sure to clear any stale ItemsHost references
            if (InternalItemsHost != null && !this.IsAncestorOf(InternalItemsHost))
            {
                InternalItemsHost = null;
            }
 
            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(DataGrid),
            new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnEnableRowVirtualizationChanged)));
 
        /// <summary>
        ///     Property changed callback for EnableRowVirtualization.
        ///     Keeps VirtualizingPanel.IsVirtualizingProperty in sync.
        /// </summary>
        private static void OnEnableRowVirtualizationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DataGrid dataGrid = (DataGrid)d;
            dataGrid.CoerceValue(VirtualizingPanel.IsVirtualizingProperty);
 
            Panel itemsHost = dataGrid.InternalItemsHost;
            if (itemsHost != null)
            {
                itemsHost.InvalidateMeasure();
                itemsHost.InvalidateArrange();
            }
        }
 
        /// <summary>
        ///     Coercion callback for VirtualizingPanel.IsVirtualizingProperty
        /// </summary>
        private static object OnCoerceIsVirtualizingProperty(DependencyObject d, object baseValue)
        {
            if (!DataGridHelper.IsDefaultValue(d, DataGrid.EnableRowVirtualizationProperty))
            {
                return d.GetValue(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(DataGrid),
            new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnEnableColumnVirtualizationChanged)));
 
        /// <summary>
        ///     Property changed callback for EnableColumnVirtualization.
        ///     Gets VirtualizingPanel.IsVirtualizingProperty for cells presenter and
        ///     headers presenter in sync.
        /// </summary>
        private static void OnEnableColumnVirtualizationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.CellsPresenter | DataGridNotificationTarget.ColumnHeadersPresenter | DataGridNotificationTarget.ColumnCollection);
        }
 
        #endregion
 
        #region Column Reordering
 
        /// <summary>
        /// Dependency Property for CanUserReorderColumns Property
        /// </summary>
        public static readonly DependencyProperty CanUserReorderColumnsProperty =
            DependencyProperty.Register("CanUserReorderColumns", typeof(bool), typeof(DataGrid), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnNotifyColumnPropertyChanged)));
 
        /// <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(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(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<DataGridColumnReorderingEventArgs> ColumnReordering;
 
        public event EventHandler<DragStartedEventArgs> ColumnHeaderDragStarted;
 
        public event EventHandler<DragDeltaEventArgs> ColumnHeaderDragDelta;
 
        public event EventHandler<DragCompletedEventArgs> ColumnHeaderDragCompleted;
 
        public event EventHandler<DataGridColumnEventArgs> ColumnReordered;
 
        protected internal virtual void OnColumnHeaderDragStarted(DragStartedEventArgs e)
        {
            if (ColumnHeaderDragStarted != null)
            {
                ColumnHeaderDragStarted(this, e);
            }
        }
 
        protected internal virtual void OnColumnReordering(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(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(DataGridClipboardCopyMode), typeof(DataGrid), new FrameworkPropertyMetadata(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 DataGridClipboardCopyMode ClipboardCopyMode
        {
            get { return (DataGridClipboardCopyMode)GetValue(ClipboardCopyModeProperty); }
            set { SetValue(ClipboardCopyModeProperty, value); }
        }
 
        private static void OnCanExecuteCopy(object target, CanExecuteRoutedEventArgs args)
        {
            ((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 != DataGridClipboardCopyMode.None && _selectedCells.Count > 0;
            args.Handled = true;
        }
 
        private static void OnExecutedCopy(object target, ExecutedRoutedEventArgs args)
        {
            ((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 == DataGridClipboardCopyMode.None)
            {
                throw new NotSupportedException(SR.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 == DataGridClipboardCopyMode.IncludeHeader)
                {
                    DataGridRowClipboardEventArgs preparingRowClipboardContentEventArgs = new 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))
                    {
                        DataGridRowClipboardEventArgs preparingRowClipboardContentEventArgs = new DataGridRowClipboardEventArgs(row, minColumnDisplayIndex, maxColumnDisplayIndex, false /*IsColumnHeadersRow*/, i);
                        OnCopyingRowClipboardContent(preparingRowClipboardContentEventArgs);
 
                        foreach (string format in formats)
                        {
                            dataGridStringBuilders[format].Append(preparingRowClipboardContentEventArgs.FormatClipboardCellValues(format));
                        }
                    }
                }
            }
 
            DataGridClipboardHelper.GetClipboardContentForHtml(dataGridStringBuilders[DataFormats.Html]);
 
            DataObject dataObject = new DataObject();
 
 
            foreach (string format in formats)
            {
                dataObject.SetData(format, dataGridStringBuilders[format].ToString(), false /*autoConvert*/);
            }
 
            try
            {
                Clipboard.CriticalSetDataObject(dataObject, true /* Copy */);
            }
            catch (ExternalException)
            {
                // Clipboard failed to set the data object - fail silently.
                return;
            }
        }
 
        /// <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(DataGridRowClipboardEventArgs args)
        {
            if (args.IsColumnHeadersRow)
            {
                for (int i = args.StartColumnDisplayIndex; i <= args.EndColumnDisplayIndex; i++)
                {
                    DataGridColumn column = ColumnFromDisplayIndex(i);
                    if (!column.IsVisible)
                    {
                        continue;
                    }
 
                    args.ClipboardRowContent.Add(new 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++)
                    {
                        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 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<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(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))
            {
                ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.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(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;
            }
 
            IProvideDataGridColumn cell = GetAnyCellOrColumnHeader();
            if (cell != null)
            {
                CellsPanelHorizontalOffset = DataGridHelper.GetParentCellsPanelHorizontalOffset(cell);
            }
            else if (!Double.IsNaN(RowHeaderWidth))
            {
                CellsPanelHorizontalOffset = RowHeaderWidth;
            }
            else
            {
                CellsPanelHorizontalOffset = 0d;
            }
 
            CellsPanelHorizontalOffsetComputationPending = false;
            return null;
        }
 
 
        /// <summary>
        /// Helper method which return any one of the cells or column headers
        /// </summary>
        /// <returns></returns>
        internal IProvideDataGridColumn GetAnyCellOrColumnHeader()
        {
            // Find the best try at a visible cell from a visible row.
            if (_rowTrackingRoot != null)
            {
                ContainerTracking<DataGridRow> rowTracker = _rowTrackingRoot;
                while (rowTracker != null)
                {
                    if (rowTracker.Container.IsVisible)
                    {
                        DataGridCellsPresenter cellsPresenter = rowTracker.Container.CellsPresenter;
                        if (cellsPresenter != null)
                        {
                            ContainerTracking<DataGridCell> cellTracker = cellsPresenter.CellTrackingRoot;
                            while (cellTracker != null)
                            {
                                if (cellTracker.Container.IsVisible)
                                {
                                    return cellTracker.Container;
                                }
                                cellTracker = cellTracker.Next;
                            }
                        }
                    }
                    rowTracker = rowTracker.Next;
                }
            }
 
            // If the row that we found earlier is not a good choice try a column header.
            // If no good column header is found the fall back will be the cell.
            if (ColumnHeadersPresenter != null)
            {
                ContainerTracking<DataGridColumnHeader> headerTracker = ColumnHeadersPresenter.HeaderTrackingRoot;
                while (headerTracker != null)
                {
                    if (headerTracker.Container.IsVisible)
                    {
                        return headerTracker.Container;
                    }
                    headerTracker = headerTracker.Next;
                }
            }
 
            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 Visual States
 
        internal override void ChangeVisualState(bool useTransitions)
        {
            if (!IsEnabled)
            {
                VisualStates.GoToState(this, useTransitions, VisualStates.StateDisabled, VisualStates.StateNormal);
            }
            else
            {
                VisualStates.GoToState(this, useTransitions, VisualStates.StateNormal);
            }
 
            base.ChangeVisualState(useTransitions);
        }
 
        #endregion
 
        #region Helpers
 
        // 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 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 NamedObject("DataGrid.NewItemPlaceholder"); // Used as an alternate data item to CollectionView.NewItemPlaceholder
 
        private DataGridColumnCollection _columns;                          // Stores the columns
        private ContainerTracking<DataGridRow> _rowTrackingRoot;            // Root of a linked list of active row containers
        private DataGridColumnHeadersPresenter _columnHeadersPresenter;     // headers presenter for sending down notifications
        private DataGridCell _currentCellContainer;                         // Reference to the cell container corresponding to CurrentCell (use CurrentCellContainer property instead)
        private DataGridCell _pendingCurrentCellContainer;                  // Reference to the cell container that will become the current cell
        private SelectedCellsCollection _selectedCells;                     // Stores the selected cells
        private List<ItemInfo> _pendingInfos;                               // Selected items whose index is not yet known
        private Nullable<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
        private 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 VirtualizedCellInfoCollection _pendingSelectedCells;        // Cells that were selected that haven't gone through SelectedCellsChanged
        private VirtualizedCellInfoCollection _pendingUnselectedCells;      // Cells that were unselected that haven't gone through SelectedCellsChanged
        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 _defaultBindingGroup;                          // Cached copy of the BindingGroup created for row validation...so we dont stomp on user set ItemBindingGroup
        private ItemInfo _editingRowInfo = null;                            // Current editing row info
        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 DataGridItemAttachedStorage _itemAttachedStorage = new 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
        private Dictionary<DataGridColumn, CellAutomationValueHolder> _editingCellAutomationValueHolders
                    = new Dictionary<DataGridColumn, CellAutomationValueHolder>(); // Holds the content of edited cells. Required for raising Automation events.
        private DataGridCell _focusedCell = null;                           // Holds the cell which has logical focus.
        private bool _newItemMarginComputationPending = false;           // Flag to indicate if row margin computation request is pending
 
        #endregion
 
        #region Constants
        private const string ItemsPanelPartName = "PART_RowsPresenter";
        #endregion
    }
}