DataGrid.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Controls / DataGrid.cs / 1407647 / DataGrid.cs

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

using System; 
using System.Collections; 
using System.Collections.Generic;
using System.Collections.ObjectModel; 
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices; 
using System.Security;
using System.Security.Permissions; 
using System.Text; 
using System.Windows;
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 System.Windows.Automation; 

namespace System.Windows.Controls 
{
    /// 
    ///     A DataGrid control that displays data in rows and columns and allows
    ///     for the entering and editing of data. 
    /// 
    public class DataGrid : MultiSelector 
    { 
        #region Constructors
 
        /// 
        ///     Instantiates global information.
        /// 
        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)));
            VirtualizingStackPanel.IsVirtualizingProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(true, null, new CoerceValueCallback(OnCoerceIsVirtualizingProperty)));
            VirtualizingStackPanel.VirtualizationModeProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(VirtualizationMode.Recycling)); 
            ItemContainerStyleProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceItemContainerStyle)));
            ItemContainerStyleSelectorProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceItemContainerStyleSelector))); 
            ItemsSourceProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata((PropertyChangedCallback)null, OnCoerceItemsSourceProperty)); 
            AlternationCountProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(0, null, new CoerceValueCallback(OnCoerceAlternationCount)));
            IsEnabledProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnIsEnabledChanged))); 
            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); 
        }
 
        ///  
        ///     Instantiates a new instance of this class.
        ///  
        public DataGrid()
        {
            _columns = new DataGridColumnCollection(this);
            _columns.CollectionChanged += new NotifyCollectionChangedEventHandler(OnColumnsChanged); 

            _rowValidationRules = new ObservableCollection(); 
            _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 
 
        /// 
        ///     A collection of column definitions describing the individual 
        ///     columns of each row.
        /// 
        public ObservableCollection Columns
        { 
            get { return _columns; }
        } 
 
        /// 
        ///     Returns the column collection without having to upcast from ObservableCollection 
        /// 
        internal DataGridColumnCollection InternalColumns
        {
            get { return _columns; } 
        }
 
        ///  
        ///     A property that specifies whether the user can resize columns in the UI by dragging the column headers.
        ///  
        /// 
        ///     This does not affect whether column widths can be changed programmatically via a property such as Column.Width.
        /// 
        public bool CanUserResizeColumns 
        {
            get { return (bool)GetValue(CanUserResizeColumnsProperty); } 
            set { SetValue(CanUserResizeColumnsProperty, value); } 
        }
 
        /// 
        ///     The DependencyProperty that represents the CanUserResizeColumns property.
        /// 
        public static readonly DependencyProperty CanUserResizeColumnsProperty = 
            DependencyProperty.Register("CanUserResizeColumns", typeof(bool), typeof(DataGrid), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnNotifyColumnAndColumnHeaderPropertyChanged)));
 
        ///  
        ///     Specifies the width of the header and cells within all the columns.
        ///  
        public DataGridLength ColumnWidth
        {
            get { return (DataGridLength)GetValue(ColumnWidthProperty); }
            set { SetValue(ColumnWidthProperty, value); } 
        }
 
        ///  
        ///     The DependencyProperty that represents the ColumnWidth property.
        ///  
        public static readonly DependencyProperty ColumnWidthProperty =
            DependencyProperty.Register("ColumnWidth", typeof(DataGridLength), typeof(DataGrid), new FrameworkPropertyMetadata(DataGridLength.SizeToHeader));

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

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

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

        ///  
        ///     The DependencyProperty that represents the  MaxColumnWidth property. 
        /// 
        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);
        }

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

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

        ///  
        ///     Called when the Columns collection changes.
        ///  
        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();
            }
        }
 
        /// 
        ///     Updates the reference to this DataGrid on the list of columns. 
        ///  
        /// The list of affected columns.
        /// Whether to add or remove the reference to this grid. 
        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;
                } 
            } 
        }
 
        /// 
        ///     Updates the transferred size constraints from DataGrid on the columns.
        /// 
        /// The list of affected columns. 
        private static void UpdateColumnSizeConstraints(IList list)
        { 
            var count = list.Count; 
            for (var i = 0; i < count; i++)
            { 
                var column = (DataGridColumn)list[i];
                column.SyncProperties();
            }
        } 

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

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

            return InternalColumns.ColumnFromDisplayIndex(displayIndex); 
        }
 
        ///  
        ///     Event that is fired when the DisplayIndex on one of the DataGrid's Columns changes.
        ///  
        public event EventHandler ColumnDisplayIndexChanged;

        /// 
        ///     Called when the DisplayIndex of a column is modified. 
        /// 
        ///  
        ///     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.
        ///  
        protected internal virtual void OnColumnDisplayIndexChanged(DataGridColumnEventArgs e)
        {
            if (ColumnDisplayIndexChanged != null)
            { 
                ColumnDisplayIndexChanged(this, e);
            } 
        } 

        ///  
        ///     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.
        /// 
        internal List DisplayIndexMap 
        {
            get { return InternalColumns.DisplayIndexMap; } 
        } 

        ///  
        ///     Throws an ArgumentOutOfRangeException if the given displayIndex is invalid.
        /// 
        internal void ValidateDisplayIndex(DataGridColumn column, int displayIndex)
        { 
            InternalColumns.ValidateDisplayIndex(column, displayIndex);
        } 
 
        /// 
        ///     Returns the index of a column from the given DisplayIndex 
        /// 
        internal int ColumnIndexFromDisplayIndex(int displayIndex)
        {
            if (displayIndex >= 0 && displayIndex < DisplayIndexMap.Count) 
            {
                return DisplayIndexMap[displayIndex]; 
            } 

            return -1; 
        }

        /// 
        ///     Given the DisplayIndex of a column returns the DataGridColumnHeader for that column. 
        ///     Used by DataGridColumnHeader to find its previous sibling.
        ///  
        ///  
        /// 
        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
 
        /// 
        ///     Notifies each CellsPresenter about property changes.
        /// 
        private static void OnNotifyCellsPresenterPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.CellsPresenter); 
        } 

        ///  
        ///     Notifies each Column and Cell about property changes.
        /// 
        private static void OnNotifyColumnAndCellPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Columns | DataGridNotificationTarget.Cells);
        } 
 
        /// 
        ///     Notifies each Column about property changes. 
        /// 
        private static void OnNotifyColumnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Columns); 
        }
 
        ///  
        ///     Notifies the Column & Column Headers about property changes.
        ///  
        private static void OnNotifyColumnAndColumnHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Columns | DataGridNotificationTarget.ColumnHeaders);
        } 

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

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

        /// 
        ///     Notifies everyone who cares about GridLine property changes (Row, Cell, RowHeader, ColumnHeader) 
        /// 
        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); 
            }
        } 

        /// 
        ///     Notifies each Row about property changes.
        ///  
        private static void OnNotifyRowPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Rows); 
        }
 
        /// 
        ///     Notifies the Row Headers about property changes.
        /// 
        private static void OnNotifyRowHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.RowHeaders); 
        } 

        ///  
        ///     Notifies the Row & Row Headers about property changes.
        /// 
        private static void OnNotifyRowAndRowHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Rows | DataGridNotificationTarget.RowHeaders);
        } 
 
        /// 
        ///     Notifies the Row & Details about property changes. 
        /// 
        private static void OnNotifyRowAndDetailsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Rows | DataGridNotificationTarget.DetailsPresenter); 
        }
 
        ///  
        ///     Notifies HorizontalOffset change to columns collection, cellspresenter and column headers presenter
        ///  
        private static void OnNotifyHorizontalOffsetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.ColumnCollection | DataGridNotificationTarget.CellsPresenter | DataGridNotificationTarget.ColumnHeadersPresenter);
        } 

        ///  
        ///     General notification for DependencyProperty changes from the grid or from columns. 
        /// 
        ///  
        ///     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. 
        /// 
        internal void NotifyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e, DataGridNotificationTarget target) 
        { 
            NotifyPropertyChanged(d, string.Empty, e, target);
        } 

        /// 
        ///     General notification for DependencyProperty changes from the grid or from columns.
        ///  
        /// 
        ///     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. 
        /// 
        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 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); 
            }
        } 
 
        /// 
        ///     Called by DataGridColumnCollection when columns' DisplayIndex changes 
        /// 
        /// 
        internal void UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction action, int oldDisplayIndex, DataGridColumn oldColumn, int newDisplayIndex)
        { 
            using (UpdateSelectedCells())
            { 
                _selectedCells.OnColumnsChanged(action, oldDisplayIndex, oldColumn, newDisplayIndex, SelectedItems); 
            }
        } 

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

        /// 
        ///     OnTemplateChanged override
        ///  
        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 
 
        /// 
        ///     GridLinesVisibility Dependency Property 
        /// 
        public static readonly DependencyProperty GridLinesVisibilityProperty =
            DependencyProperty.Register(
                "GridLinesVisibility", 
                typeof(DataGridGridLinesVisibility),
                typeof(DataGrid), 
                new FrameworkPropertyMetadata(DataGridGridLinesVisibility.All, new PropertyChangedCallback(OnNotifyGridLinePropertyChanged))); 

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

        ///  
        /// HorizontalGridLinesBrush Dependency Property
        /// 
        public static readonly DependencyProperty HorizontalGridLinesBrushProperty =
            DependencyProperty.Register( 
                "HorizontalGridLinesBrush",
                typeof(Brush), 
                typeof(DataGrid), 
                new FrameworkPropertyMetadata(Brushes.Black, new PropertyChangedCallback(OnNotifyGridLinePropertyChanged)));
 
        /// 
        /// Specifies the Brush used to draw the horizontal grid lines
        /// 
        public Brush HorizontalGridLinesBrush 
        {
            get { return (Brush)GetValue(HorizontalGridLinesBrushProperty); } 
            set { SetValue(HorizontalGridLinesBrushProperty, value); } 
        }
 
        /// 
        /// VerticalGridLinesBrush Dependency Property
        /// 
        public static readonly DependencyProperty VerticalGridLinesBrushProperty = 
            DependencyProperty.Register(
                "VerticalGridLinesBrush", 
                typeof(Brush), 
                typeof(DataGrid),
                new FrameworkPropertyMetadata(Brushes.Black, new PropertyChangedCallback(OnNotifyGridLinePropertyChanged))); 

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

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

        ///  
        /// Specifies the thickness of the vertical grid lines.
        /// 
        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
 
        ///  
        ///     Determines if an item is its own container.
        ///  
        /// The item to test.
        /// true if the item is a DataGridRow, false otherwise.
        protected override bool IsItemItsOwnContainerOverride(object item)
        { 
            return item is DataGridRow;
        } 
 
        /// 
        ///     Instantiates an instance of a container. 
        /// 
        /// A new DataGridRow.
        protected override DependencyObject GetContainerForItemOverride()
        { 
            return new DataGridRow();
        } 
 
        /// 
        ///     Prepares a new container for a given item. 
        /// 
        /// The new container.
        /// The item that the container represents.
        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);
                EnsureInternalScrollControls();
            } 

            row.PrepareRow(item, this); 
            OnLoadingRow(new DataGridRowEventArgs(row)); 
        }
 
        /// 
        ///     Clears a container of references.
        /// 
        /// The container being cleared. 
        /// The data item that the container represented.
        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); 
            }
 
            OnUnloadingRow(new DataGridRowEventArgs(row)); 
            row.ClearRow(this);
        } 

        /// 
        ///     Propagates the collection changed notification on Columns down to
        ///     each active DataGridRow. 
        /// 
        /// The event arguments from the original collection changed event. 
        private void UpdateColumnsOnRows(NotifyCollectionChangedEventArgs e) 
        {
            ContainerTracking tracker = _rowTrackingRoot; 
            while (tracker != null)
            {
                tracker.Container.OnColumnsChanged(_columns, e);
                tracker = tracker.Next; 
            }
        } 
 
        /// 
        ///     Equivalent of ItemContainerStyle. 
        /// 
        /// 
        ///     If this property has a non-null value, it will override the value
        ///     of ItemContainerStyle. 
        /// 
        public Style RowStyle 
        { 
            get { return (Style)GetValue(RowStyleProperty); }
            set { SetValue(RowStyleProperty, value); } 
        }

        /// 
        ///     DependencyProperty for the RowStyle property. 
        /// 
        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;
        } 

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

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

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

        /// 
        ///     Called when the Columns collection changes.
        ///  
        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; 
                }
            } 
        }

        /// 
        ///     Equivalent of ItemContainerStyleSelector. 
        /// 
        ///  
        ///     If this property has a non-null value, it will override the value 
        ///     of ItemContainerStyleSelector.
        ///  
        public StyleSelector RowStyleSelector
        {
            get { return (StyleSelector)GetValue(RowStyleSelectorProperty); }
            set { SetValue(RowStyleSelectorProperty, value); } 
        }
 
        ///  
        ///     DependencyProperty for the RowStyleSelector property.
        ///  
        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;
        }
 
        /// 
        ///     The default row background brush. 
        ///  
        public Brush RowBackground
        { 
            get { return (Brush)GetValue(RowBackgroundProperty); }
            set { SetValue(RowBackgroundProperty, value); }
        }
 
        /// 
        ///     DependencyProperty for RowBackground. 
        ///  
        public static readonly DependencyProperty RowBackgroundProperty =
            DependencyProperty.Register("RowBackground", typeof(Brush), typeof(DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyRowPropertyChanged))); 

        /// 
        ///     The default row background brush for use on every other row.
        ///  
        /// 
        ///     Setting this property to a non-null value will coerce AlternationCount to 2. 
        ///  
        public Brush AlternatingRowBackground
        { 
            get { return (Brush)GetValue(AlternatingRowBackgroundProperty); }
            set { SetValue(AlternatingRowBackgroundProperty, value); }
        }
 
        /// 
        ///     DependencyProperty for AlternatingRowBackground. 
        ///  
        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; 
        }
 
        /// 
        ///     The default height of a row.
        /// 
        public double RowHeight 
        {
            get { return (double)GetValue(RowHeightProperty); } 
            set { SetValue(RowHeightProperty, value); } 
        }
 
        /// 
        ///     The DependencyProperty for RowHeight.
        /// 
        public static readonly DependencyProperty RowHeightProperty = 
            DependencyProperty.Register("RowHeight", typeof(double), typeof(DataGrid), new FrameworkPropertyMetadata(double.NaN, new PropertyChangedCallback(OnNotifyCellsPresenterPropertyChanged)));
 
        ///  
        ///     The default minimum height of a row.
        ///  
        public double MinRowHeight
        {
            get { return (double)GetValue(MinRowHeightProperty); }
            set { SetValue(MinRowHeightProperty, value); } 
        }
 
        ///  
        ///     The DependencyProperty for MinRowHeight.
        ///  
        public static readonly DependencyProperty MinRowHeightProperty =
            DependencyProperty.Register("MinRowHeight", typeof(double), typeof(DataGrid), new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(OnNotifyCellsPresenterPropertyChanged)));

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

        ///  
        ///     Event that is fired just before a row is prepared. 
        /// 
        public event EventHandler LoadingRow; 

        /// 
        ///     Event that is fired just before a row is cleared.
        ///  
        public event EventHandler UnloadingRow;
 
        ///  
        ///     Invokes the LoadingRow event
        ///  
        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;
        } 

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

            var row = e.Row;
            OnUnloadingRowDetailsWrapper(row); 
        }
 
        #endregion 

        #region Row/Column Headers 

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

        /// 
        ///     The DependencyProperty for RowHeaderWidth.
        ///  
        public static readonly DependencyProperty RowHeaderWidthProperty =
            DependencyProperty.Register("RowHeaderWidth", typeof(double), typeof(DataGrid), new FrameworkPropertyMetadata(double.NaN, new PropertyChangedCallback(OnNotifyRowHeaderWidthPropertyChanged))); 
 
        /// 
        ///     The actual width of row headers used for binding.  This is computed from the measure of all the visible row headers. 
        /// 
        public double RowHeaderActualWidth
        {
            get { return (double)GetValue(RowHeaderActualWidthProperty); } 
            internal set { SetValue(RowHeaderActualWidthPropertyKey, value); }
        } 
 
        /// 
        ///     The DependencyPropertyKey for RowHeaderActualWidth. 
        /// 
        private static readonly DependencyPropertyKey RowHeaderActualWidthPropertyKey =
            DependencyProperty.RegisterReadOnly("RowHeaderActualWidth", typeof(double), typeof(DataGrid), new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(OnNotifyRowHeaderPropertyChanged)));
 
        /// 
        ///     The DependencyProperty for RowHeaderActualWidth. 
        ///  
        public static readonly DependencyProperty RowHeaderActualWidthProperty = RowHeaderActualWidthPropertyKey.DependencyProperty;
 
        /// 
        ///     The default height of a column header.
        /// 
        public double ColumnHeaderHeight 
        {
            get { return (double)GetValue(ColumnHeaderHeightProperty); } 
            set { SetValue(ColumnHeaderHeightProperty, value); } 
        }
 
        /// 
        ///     The DependencyProperty for ColumnHeaderHeight.
        /// 
        public static readonly DependencyProperty ColumnHeaderHeightProperty = 
            DependencyProperty.Register("ColumnHeaderHeight", typeof(double), typeof(DataGrid), new FrameworkPropertyMetadata(double.NaN, OnNotifyColumnHeaderPropertyChanged));
 
        ///  
        ///     A property that specifies the visibility of the column & row headers.
        ///  
        public DataGridHeadersVisibility HeadersVisibility
        {
            get { return (DataGridHeadersVisibility)GetValue(HeadersVisibilityProperty); }
            set { SetValue(HeadersVisibilityProperty, value); } 
        }
 
        ///  
        ///     The DependencyProperty that represents the HeadersVisibility property.
        ///  
        public static readonly DependencyProperty HeadersVisibilityProperty =
            DependencyProperty.Register("HeadersVisibility", typeof(DataGridHeadersVisibility), typeof(DataGrid), new FrameworkPropertyMetadata(DataGridHeadersVisibility.All));

        ///  
        ///     Updates RowHeaderActualWidth to reflect changes to RowHeaderWidth
        ///  
        private static void OnNotifyRowHeaderWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            var dataGrid = ((DataGrid)d); 
            var newValue = (double)e.NewValue;

            if (!DoubleUtil.IsNaN(newValue))
            { 
                dataGrid.RowHeaderActualWidth = newValue;
            } 
            else 
            {
                // If we're entering Auto mode we need to reset the RowHeaderActualWidth 
                // because the previous explicit value may have been bigger than the Auto width.
                dataGrid.RowHeaderActualWidth = 0.0;
            }
 
            OnNotifyRowHeaderPropertyChanged(d, e);
        } 
 
        /// 
        /// Resets the RowHeaderActualWidth to 0.0 if in Auto mode 
        /// 
        private void ResetRowHeaderActualWidth()
        {
            if (DoubleUtil.IsNaN(RowHeaderWidth)) 
            {
                RowHeaderActualWidth = 0.0; 
            } 
        }
 
        #endregion

        #region Item Associated Properties
 
        /// 
        ///     Sets the specified item's DetailsVisibility. 
        ///  
        /// 
        ///     This is useful when a DataGridRow may not currently exists to set DetailsVisibility on. 
        /// 
        /// The item that will have its DetailsVisibility set.
        /// The Visibility that the item's details should get.
        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;
            }
        } 

        ///  
        ///     Returns the current DetailsVisibility for an item that's in the DataGrid. 
        /// 
        /// The item who's DetailsVisibility you would like to get 
        /// The DetailsVisibility associated with the specified item.
        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; 
            } 
        }
 
        /// 
        ///     Clears the DetailsVisibility for the specified item
        /// 
        /// The item to clear the DetailsVisibility on. 
        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; } 
        }

        /// 
        ///     Determines whether the selection change caused by keyboard input should select a full row (or full rows). 
        /// 
        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 

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

        /// 
        ///     The DependencyProperty that represents the CellStyle property.
        ///  
        public static readonly DependencyProperty CellStyleProperty =
            DependencyProperty.Register("CellStyle", typeof(Style), typeof(DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyColumnAndCellPropertyChanged))); 
 
        /// 
        ///     A style to apply to all column headers in the DataGrid 
        /// 
        public Style ColumnHeaderStyle
        {
            get { return (Style)GetValue(ColumnHeaderStyleProperty); } 
            set { SetValue(ColumnHeaderStyleProperty, value); }
        } 
 
        /// 
        ///     The DependencyProperty that represents the ColumnHeaderStyle property. 
        /// 
        public static readonly DependencyProperty ColumnHeaderStyleProperty =
            DependencyProperty.Register("ColumnHeaderStyle", typeof(Style), typeof(DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyColumnAndColumnHeaderPropertyChanged)));
 
        /// 
        ///     A style to apply to all row headers in the DataGrid 
        ///  
        public Style RowHeaderStyle
        { 
            get { return (Style)GetValue(RowHeaderStyleProperty); }
            set { SetValue(RowHeaderStyleProperty, value); }
        }
 
        /// 
        ///     The DependencyProperty that represents the RowHeaderStyle property. 
        ///  
        public static readonly DependencyProperty RowHeaderStyleProperty =
            DependencyProperty.Register("RowHeaderStyle", typeof(Style), typeof(DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyRowAndRowHeaderPropertyChanged))); 

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

        /// 
        ///     The DependencyProperty for the RowHeaderTemplate property.
        ///  
        public static readonly DependencyProperty RowHeaderTemplateProperty =
            DependencyProperty.Register("RowHeaderTemplate", typeof(DataTemplate), typeof(DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyRowAndRowHeaderPropertyChanged))); 
 
        /// 
        ///     The object representing the Row Header template selector. 
        /// 
        public DataTemplateSelector RowHeaderTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(RowHeaderTemplateSelectorProperty); } 
            set { SetValue(RowHeaderTemplateSelectorProperty, value); }
        } 
 
        /// 
        ///     The DependencyProperty for the RowHeaderTemplateSelector property. 
        /// 
        public static readonly DependencyProperty RowHeaderTemplateSelectorProperty =
            DependencyProperty.Register("RowHeaderTemplateSelector", typeof(DataTemplateSelector), typeof(DataGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyRowAndRowHeaderPropertyChanged)));
 
        /// 
        ///     The default style references this brush to create a thicker border 
        ///     around the focused cell. 
        /// 
        public static ComponentResourceKey FocusBorderBrushKey 
        {
            get { return SystemResourceKey.DataGridFocusBorderBrushKey; }
        }
 
        /// 
        ///     A converter which converts DataGridHeadersVisibility to VisibilityConverter based on a ConverterParameter. 
        ///  
        /// 
        ///     This can be used in the DataGrid's template to control which parts of the DataGrid are visible for a given DataGridHeadersVisibility. 
        /// 
        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;
            } 
        }
 
        ///  
        ///     A converter which converts bool to SelectiveScrollin----entation based on a ConverterParameter.
        ///  
        /// 
        ///     This can be used in the DataGrid's template to control how the RowDetails selectively scroll based on a bool.
        /// 
        public static IValueConverter RowDetailsScrollingConverter 
        {
            get 
            { 
                // This is delay created in case the template doesn't use it.
                if (_rowDetailsScrollingConverter == null) 
                {
                    _rowDetailsScrollingConverter = new BooleanToSelectiveScrollin----entationConverter();
                }
 
                return _rowDetailsScrollingConverter;
            } 
        } 

        #endregion 

        #region Scrolling

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

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

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

        ///  
        ///     The DependencyProperty for the HorizontalScrollBarVisibility property.
        /// 
        public static readonly DependencyProperty VerticalScrollBarVisibilityProperty = ScrollViewer.VerticalScrollBarVisibilityProperty.AddOwner(typeof(DataGrid), new FrameworkPropertyMetadata(ScrollBarVisibility.Auto));
 
        /// 
        ///     Scrolls a row into view. 
        ///  
        /// The data item of the row to bring into view.
        public void ScrollIntoView(object item) 
        {
            if (item == null)
            {
                throw new ArgumentNullException("item"); 
            }
 
            if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
            {
                ScrollRowIntoView(item); 
            }
            else
            {
                // The items aren't generated, try at a later time 
                Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(OnScrollIntoView), item);
            } 
        } 

        ///  
        ///     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.
        ///  
        /// The data item row that contains the cell.
        /// The cell's column. 
        public void ScrollIntoView(object item, DataGridColumn column) 
        {
            if (column == null) 
            {
                ScrollIntoView(item);
                return;
            } 

            if (!column.IsVisible) 
            { 
                return;
            } 

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

        /// 
        ///     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. 
        ///  
        private object OnScrollIntoView(object arg)
        { 
            object[] arguments = arg as object[];
            if (arguments != null)
            {
                if (arguments[0] != null) 
                {
                    ScrollCellIntoView(arguments[0], (DataGridColumn)arguments[1]); 
                } 
                else
                { 
                    ScrollColumnIntoView((DataGridColumn)arguments[1]);
                }
            }
            else 
            {
                ScrollRowIntoView(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);
                }
            } 
        }
 
        // 
        private void ScrollRowIntoView(object item)
        { 
            FrameworkElement element = ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
            if (element != null)
            {
                element.BringIntoView(); 
            }
            else if (!IsGrouping) 
            { 
                // We might be virtualized, try scrolling by index.
                int index = Items.IndexOf(item); 
                if (index >= 0)
                {
                    // It would be convenient for ItemsHost to be public, but since it is not,
                    // we are relying on some internal communication between DataGridRowsPresenter 
                    // and the DataGrid.
                    DataGridRowsPresenter itemsHost = InternalItemsHost as DataGridRowsPresenter; 
                    if (itemsHost != null) 
                    {
                        // It would have been better to be able to directly call BringIndexIntoView, 
                        // but that method is protected, so we are relying on an internal
                        // method on DataGridRowsPresenter to make the call.
                        itemsHost.InternalBringIndexIntoView(index);
                    } 
                }
            } 
        } 

        // 
        private void ScrollCellIntoView(object item, DataGridColumn column)
        {
            Debug.Assert(item != null, "item is null.");
            Debug.Assert(column != null, "column is null."); 

            if (!column.IsVisible) 
            { 
                return;
            } 

            // Devirtualize the concerned row if it is not already
            DataGridRow row = ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
            if (row == null) 
            {
                ScrollRowIntoView(item); 
                UpdateLayout(); 
                row = ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
            } 
            else
            {
                // Ensures that row is brought into viewport
                row.BringIntoView(); 
            }
 
            // Use the row to scroll cell into view. 
            if (row != null)
            { 
                int columnIndex = _columns.IndexOf(column);
                row.ScrollCellIntoView(columnIndex);
            }
        } 

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

        /// 
        ///     Begins a timer that will periodically scroll and select.
        ///  
        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(); 
            }
        } 

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

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

        /// 
        ///     Based on the mouse position relative to the rows and cells, 
        ///     scrolls and selects rows and/or cells.
        ///  
        /// true if a scroll and select was attempted. false otherwise. 
        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;
                    object dataItem = cell.RowDataItem;
 
                    // Based on the position of the mouse relative to the field
                    // of cells, choose the cell that is torwards the mouse. 
                    // Note: This assumes a grid layout. 
                    if (IsMouseToLeft(position))
                    { 
                        int columnIndex = column.DisplayIndex;
                        if (columnIndex > 0)
                        {
                            column = ColumnFromDisplayIndex(columnIndex - 1); 
                        }
                    } 
                    else if (IsMouseToRight(position)) 
                    {
                        int columnIndex = column.DisplayIndex; 
                        if (columnIndex < (_columns.Count - 1))
                        {
                            column = ColumnFromDisplayIndex(columnIndex + 1);
                        } 
                    }
 
                    if (IsMouseAbove(position)) 
                    {
                        int rowIndex = Items.IndexOf(dataItem); 
                        if (rowIndex > 0)
                        {
                            dataItem = Items[rowIndex - 1];
                        } 
                    }
                    else if (IsMouseBelow(position)) 
                    { 
                        int rowIndex = Items.IndexOf(dataItem);
                        if (rowIndex < (Items.Count - 1)) 
                        {
                            dataItem = Items[rowIndex + 1];
                        }
                    } 

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

        /// 
        ///     Prevents the ScrollViewer from handling keyboard input.
        ///  
        protected internal override bool HandlesScrolling
        { 
            get { return true; } 
        }
 
        /// 
        ///     Workaround for not having access to ItemsControl.ItemsHost.
        /// 
        internal Panel InternalItemsHost 
        {
            get { return _internalItemsHost; } 
            set 
            {
                if (_internalItemsHost != value) 
                {
                    _internalItemsHost = value;
                    if (_internalItemsHost != null)
                    { 
                        EnsureInternalScrollControls();
                    } 
                } 
            }
        } 

        /// 
        ///     Workaround for not having access to ItemsControl.ScrollHost.
        ///  
        internal ScrollViewer InternalScrollHost
        { 
            get 
            {
                EnsureInternalScrollControls(); 
                return _internalScrollHost;
            }
        }
 
        /// 
        ///     Workaround for not having access to ScrollContentPresenter 
        ///  
        internal ScrollContentPresenter InternalScrollContentPresenter
        { 
            get
            {
                EnsureInternalScrollControls();
                return _internalScrollContentPresenter; 
            }
        } 
 
        /// 
        ///     Helper method which ensures the initialization of scroll controls. 
        /// 
        private void EnsureInternalScrollControls()
        {
            if (_internalScrollContentPresenter == null) 
            {
                if (_internalItemsHost != null) 
                { 
                    _internalScrollContentPresenter = DataGridHelper.FindVisualParent(_internalItemsHost);
                } 
                else if (_rowTrackingRoot != null)
                {
                    DataGridRow row = _rowTrackingRoot.Container;
                    _internalScrollContentPresenter = DataGridHelper.FindVisualParent(row); 
                }
                if (_internalScrollContentPresenter != null) 
                { 
                    _internalScrollContentPresenter.SizeChanged += new SizeChangedEventHandler(OnInternalScrollContentPresenterSizeChanged);
                } 
            }

            if (_internalScrollHost == null)
            { 
                if (_internalItemsHost != null)
                { 
                    _internalScrollHost = DataGridHelper.FindVisualParent(_internalItemsHost); 
                }
                else if (_rowTrackingRoot != null) 
                {
                    DataGridRow row = _rowTrackingRoot.Container;
                    _internalScrollHost = DataGridHelper.FindVisualParent(row);
                } 
                if (_internalScrollHost != null)
                { 
                    Binding horizontalOffsetBinding = new Binding("ContentHorizontalOffset"); 
                    horizontalOffsetBinding.Source = _internalScrollHost;
                    SetBinding(HorizontalScrollOffsetProperty, horizontalOffsetBinding); 
                }
            }
        }
 
        /// 
        ///     Helper method which cleans up the internal scroll controls. 
        ///  
        private void CleanUpInternalScrollControls()
        { 
            BindingOperations.ClearBinding(this, HorizontalScrollOffsetProperty);
            _internalScrollHost = null;
            if (_internalScrollContentPresenter != null)
            { 
                _internalScrollContentPresenter.SizeChanged -= new SizeChangedEventHandler(OnInternalScrollContentPresenterSizeChanged);
                _internalScrollContentPresenter = null; 
            } 
        }
 
        /// 
        ///     Size changed handler for InteralScrollContentPresenter.
        /// 
        private void OnInternalScrollContentPresenterSizeChanged(object sender, SizeChangedEventArgs e) 
        {
            if (_internalScrollContentPresenter != null && 
                !_internalScrollContentPresenter.CanContentScroll) 
            {
                OnViewportSizeChanged(e.PreviousSize, e.NewSize); 
            }
        }

        ///  
        ///     Helper method which enqueues a viewport width change
        ///     request to Dispatcher if needed. 
        ///  
        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;
                    }
                } 
            }
        } 
 
        /// 
        ///     Dispatcher callback method for Viewport width change 
        ///     which propagates the notification if needed.
        /// 
        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;
        } 
 
        /// 
        ///     Dependency property which would be bound to ContentHorizontalOffset 
        ///     property of the ScrollViewer.
        /// 
        internal static readonly DependencyProperty HorizontalScrollOffsetProperty =
            DependencyProperty.Register( 
                "HorizontalScrollOffset",
                typeof(double), 
                typeof(DataGrid), 
                new FrameworkPropertyMetadata(0d, OnNotifyHorizontalOffsetPropertyChanged));
 
        /// 
        ///     The HorizontalOffset of the scroll viewer
        /// 
        internal double HorizontalScrollOffset 
        {
            get 
            { 
                return (double)GetValue(HorizontalScrollOffsetProperty);
            } 
        }

        #endregion
 
        #region Editing Commands
 
        ///  
        ///     The command to fire and allow to route to the DataGrid in order to indicate that the
        ///     current cell or row should begin editing. 
        /// 
        public static readonly RoutedCommand BeginEditCommand = new RoutedCommand("BeginEdit", typeof(DataGrid));

        ///  
        ///     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. 
        ///  
        public static readonly RoutedCommand CommitEditCommand = new RoutedCommand("CommitEdit", typeof(DataGrid));
 
        /// 
        ///     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. 
        /// 
        public static readonly RoutedCommand CancelEditCommand = new RoutedCommand("CancelEdit", typeof(DataGrid)); 
 
        /// 
        ///     A command that, when invoked, will delete the current row. 
        /// 
        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);
        } 

        /// 
        ///     Invoked to determine if the BeginEdit command can be executed.
        ///  
        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; 
            } 
        }
 
        /// 
        ///     Invoked when the BeginEdit command is executed.
        /// 
        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 columnIndexRanges = null;
                int newItemIndex = -1; 
                object newItem = null;
                bool placeholderAtBeginning = (EditableItems.NewItemPlaceholderPosition == NewItemPlaceholderPosition.AtBeginning); 
 
                if (IsNewItemPlaceholder(cell.RowDataItem))
                { 
                    // If editing the new item placeholder, then create a new item and edit that instead.
                    if (SelectedItems.Contains(CollectionView.NewItemPlaceholder))
                    {
                        // Unselect the NewItemPlaceholder and select the new row 
                        UnselectItem(CollectionView.NewItemPlaceholder);
                        deselectedPlaceholder = true; 
                    } 
                    else
                    { 
                        // Cells will automatically unselect when the new item placeholder is removed, but we
                        // should reselect them on the new item.
                        newItemIndex = Items.IndexOf(cell.RowDataItem);
                        reselectPlaceholderCells = ((newItemIndex >= 0) && _selectedCells.Intersects(newItemIndex, out columnIndexRanges)); 
                    }
 
                    newItem = AddNewItem(); 
                    SetCurrentValueInternal(CurrentItemProperty, newItem); // Puts focus on the added row
                    cell = CurrentCellContainer; 
                    if (CurrentCellContainer == null)
                    {
                        // CurrentCellContainer becomes null if focus moves out of the datagrid
                        // Calling UpdateLayout instantiates the CurrentCellContainer 
                        UpdateLayout();
                        cell = CurrentCellContainer; 
                        if ((cell != null) && !cell.IsKeyboardFocusWithin) 
                        {
                            cell.Focus(); 
                        }
                    }

                    if (deselectedPlaceholder) 
                    {
                        // Re-select the new item if the placeholder was selected before 
                        SelectItem(newItem); 
                    }
                    else if (reselectPlaceholderCells) 
                    {
                        // Re-select placeholder cells if they were selected before
                        using (UpdateSelectedCells())
                        { 
                            int rowIndex = newItemIndex;
 
                            // When the placeholder is at the beginning, we don't hide it, so those cells need to be unselected. 
                            // The cells to select are also now one row below.
                            if (placeholderAtBeginning) 
                            {
                                _selectedCells.RemoveRegion(newItemIndex, 0, 1, Columns.Count);
                                rowIndex++;
                            } 

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

                    addedPlaceholder = true; 
                }
 
                RoutedEventArgs editingEventArgs = e.Parameter as RoutedEventArgs; 
                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(newItem);
                    } 
                    else if (reselectPlaceholderCells && placeholderAtBeginning)
                    {
                        // When the placeholder is at the beginning, we need to unselect the added item cells.
                        _selectedCells.RemoveRegion(newItemIndex + 1, 0, 1, Columns.Count); 
                    }
 
                    if (addedPlaceholder) 
                    {
                        // The edit was canceled, cancel the new item 
                        CancelRowItem();

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

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

                        var bindingGroup = cell.RowOwner.BindingGroup;
                        if (bindingGroup != null)
                        { 
                            bindingGroup.BeginEdit();
                        } 
 
                        _editingRowItem = cell.RowDataItem;
                        _editingRowIndex = Items.IndexOf(_editingRowItem); 
                    }

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

                    // 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(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));
        }
 
        /// 
        ///     Invoked to determine if the CommitEdit command can be executed. 
        ///  
        protected virtual void OnCanExecuteCommitEdit(CanExecuteRoutedEventArgs e)
        { 
            if (CanEndEdit(e, /* commit = */ true))
            {
                e.CanExecute = true;
                e.Handled = true; 
            }
            else 
            { 
                e.ContinueRouting = true;
            } 
        }

        /// 
        ///     Invoked when the CommitEdit command is executed. 
        /// 
        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;
        } 

        ///  
        /// This is a helper method used to flush the dispatcher down to DataBind priority so that the bindingGroup will be ready for CommitEdit. 
        /// 
        ///  
        /// 
        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; 
        }
 
        ///  
        ///     Raised just before row editing is ended.
        ///     Gives handlers the opportunity to cancel the operation. 
        /// 
        public event EventHandler RowEditEnding;

        ///  
        ///     Called just before row editing is ended.
        ///     Gives subclasses the opportunity to cancel the operation. 
        ///  
        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);
                }
            } 
        }
 
        ///  
        ///     Raised just before cell editing is ended.
        ///     Gives handlers the opportunity to cancel the operation. 
        /// 
        public event EventHandler CellEditEnding;

        ///  
        ///     Called just before cell editing is ended.
        ///     Gives subclasses the opportunity to cancel the operation. 
        ///  
        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);
        } 
 
        /// 
        ///     Invoked to determine if the CancelEdit command can be executed. 
        /// 
        protected virtual void OnCanExecuteCancelEdit(CanExecuteRoutedEventArgs e)
        {
            if (CanEndEdit(e, /* commit = */ false)) 
            {
                e.CanExecute = true; 
                e.Handled = true; 
            }
            else 
            {
                e.ContinueRouting = true;
            }
        } 

        ///  
        ///     Invoked when the CancelEdit command is executed. 
        /// 
        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); 
        }
 
        /// 
        ///     Invoked to determine if the Delete command can be executed.
        /// 
        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; 
        }

        /// 
        ///     Invoked when the Delete command is executed. 
        /// 
        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,
                    // deteremine a new item to select once the items are removed. 
                    int numSelected = SelectedItems.Count;
                    int indexToSelect = -1; 
                    object currentItem = CurrentItem; 

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

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

                    // Save off the selected items. The selected items are going to be cleared 
                    // first as a performance optimization. When items are removed, they are checked
                    // against the selected items to be removed from that collection. This can be slow
                    // since each item could cause a linear search of the selected items collection.
                    // Since it is known that all of the selected items are going to be deleted, they 
                    // can safely be unselected.
                    ArrayList itemsToRemove = new ArrayList(SelectedItems); 
 
                    using (UpdateSelectedCells())
                    { 
                        bool alreadyUpdating = IsUpdatingSelectedItems;
                        if (!alreadyUpdating)
                        {
                            BeginUpdateSelectedItems(); 
                        }
 
                        try 
                        {
                            // Pre-emptively clear the selection lists 
                            _selectedCells.ClearFullRows(SelectedItems);
                            SelectedItems.Clear();
                        }
                        finally 
                        {
                            if (!alreadyUpdating) 
                            { 
                                EndUpdateSelectedItems();
                            } 
                        }
                    }

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

                    // Remove the items that are selected 
                    for (int i = 0; i < numSelected; i++)
                    {
                        object itemToRemove = itemsToRemove[i];
                        if (itemToRemove != CollectionView.NewItemPlaceholder) 
                        {
                            EditableItems.Remove(itemToRemove); 
                        } 
                    }
 
                    // Select a new item
                    if (indexToSelect >= 0)
                    {
                        object itemToSelect = Items[indexToSelect]; 

                        // This should focus the row and bring it into view. 
                        SetCurrentValueInternal(CurrentItemProperty, 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;
        }
 
        #endregion
 
        #region Editing 

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

        ///  
        ///     The DependencyProperty for IsReadOnly.
        /// 
        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);
        } 

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

        ///  
        ///     The DependencyProperty for CurrentItem.
        /// 
        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;

            // the RowHeaders need to know when the current item changes so they can update their Visual State.
            OnNotifyRowHeaderPropertyChanged(d, e); 

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

        ///  
        ///     The column of the CurrentItem (row) that corresponds with the current cell.
        ///  
        ///  
        ///     null indicates that a cell does not have focus. The row may still have focus.
        ///  
        public DataGridColumn CurrentColumn
        {
            get { return (DataGridColumn)GetValue(CurrentColumnProperty); }
            set { SetValue(CurrentColumnProperty, value); } 
        }
 
        ///  
        ///     The DependencyProperty for CurrentColumn.
        ///  
        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)); 
            }
        } 
 
        /// 
        ///     The cell that, if not in edit mode, can be edited. 
        /// 
        /// 
        ///     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.
        ///  
        public DataGridCellInfo CurrentCell 
        {
            get { return (DataGridCellInfo)GetValue(CurrentCellProperty); } 
            set { SetValue(CurrentCellProperty, value); }
        }

        ///  
        ///     The DependencyProperty for CurrentCell.
        ///  
        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.SetCurrentValueInternal(CurrentItemProperty, 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.Item, 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); 
        } 

        ///  
        ///     An event to notify that the value of CurrentCell changed.
        /// 
        public event EventHandler CurrentCellChanged;
 
        /// 
        ///     Called when the value of CurrentCell changes. 
        ///  
        /// Empty event arguments.
        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.
        } 
 
        private 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; 
            }
        } 
 
        /// 
        ///     Called just before a cell will change to edit mode 
        ///     to allow handlers to prevent the cell from entering edit mode.
        /// 
        public event EventHandler BeginningEdit;
 
        /// 
        ///     Called just before a cell will change to edit mode 
        ///     to all subclasses to prevent the cell from entering edit mode. 
        /// 
        ///  
        ///     Default implementation raises the BeginningEdit event.
        /// 
        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); 
                } 
            }
        } 

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

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

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

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

            return true;
        }
 
        /// 
        ///     Raises the CancelEdit command. 
        ///     Reverts any pending editing changes to the desired editing unit and exits edit mode. 
        /// 
        /// Whether to cancel edit mode of the current cell or current row. 
        /// true if the current cell or row exits edit mode, false otherwise.
        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);
            } 
        }

        /// 
        ///     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.
        ///  
        /// true if the current cell or row exits edit mode, false otherwise.
        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 
        }
 
        ///  
        ///     Raises the CommitEdit command.
        ///     Commits any pending changes for the given editing unit and exits edit mode. 
        /// 
        /// Whether to commit changes for the current cell or current row.
        /// Whether to exit edit mode.
        /// true if the current cell or row exits edit mode, false otherwise. 
        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(); 
                }
            } 
        } 

        ///  
        ///     Cell in DataGrid which has logical focus
        /// 
        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

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

        /// 
        ///     DependencyProperty for CanUserAddRows. 
        /// 
        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;
        } 
 
        /// 
        ///     Whether the end-user can delete rows from the ItemsSource. 
        /// 
        public bool CanUserDeleteRows
        {
            get { return (bool)GetValue(CanUserDeleteRowsProperty); } 
            set { SetValue(CanUserDeleteRowsProperty, value); }
        } 
 
        /// 
        ///     DependencyProperty for CanUserDeleteRows. 
        /// 
        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); 
        }
 
        ///  
        ///     An event that is raised when a new item is created so that
        ///     developers can initialize the item with custom default values. 
        /// 
        public event InitializingNewItemEventHandler InitializingNewItem;

        ///  
        ///     A method that is called when a new item is created so that
        ///     overrides can initialize the item with custom default values. 
        ///  
        /// 
        ///     The default implementation raises the InitializingNewItem event. 
        /// 
        /// Event arguments that provide access to the new item.
        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);
 
            object newItem = EditableItems.AddNew();
            if (newItem != null)
            {
                InitializingNewItemEventArgs e = new InitializingNewItemEventArgs(newItem); 
                OnInitializingNewItem(e);
            } 
 
            // CancelEdit and CommitEdit rely on IsAddingNewItem
            CommandManager.InvalidateRequerySuggested(); 

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

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

        private void CommitRowItem() 
        {
            // This case illustrates a minor side-effect of communicating with IEditableObject through two different means 
            // - BindingGroup 
            // - IEditableCollectionView
            // The sequence of operations is as below. 
            // IEditableCollectionView.BeginEdit
            // BindingGroup.BeginEdit
            // BindingGroup.CommitEdit
            // IEditableCollectionView.CommitEdit 
            // After having commited the NewItem row the first time it is possible that the IsAddingNewItem is false
            // during the second call to CommitEdit. Hence we cannot quite make this assertion here. 
 
            // Debug.Assert(IsEditingRowItem || IsAddingNewItem, "CommitRowItem was called when a row was not being edited or added.");
            if (IsEditingRowItem) 
            {
                EditableItems.CommitEdit();
            }
            else 
            {
                EditableItems.CommitNew(); 
 
                // Show the placeholder again
                UpdateNewItemPlaceholder(/* isAddingNewItem = */ false); 
            }
        }

        private void CancelRowItem() 
        {
            // This case illustrates a minor side-effect of communicating with IEditableObject through two different means 
            // - BindingGroup 
            // - IEditableCollectionView
            // The sequence of operations is as below. 
            // IEditableCollectionView.BeginEdit
            // BindingGroup.BeginEdit
            // IEditableCollectionView.CancelEdit
            // BindingGroup.CancelEdit 
            // After having cancelled the NewItem row the first time it is possible that the IsAddingNewItem is false
            // during the second call to CancelEdit. Hence we cannot quite make this assertion here. 
 
            // Debug.Assert(IsEditingRowItem || IsAddingNewItem, "CancelRowItem was called when a row was not being edited or added.");
            if (IsEditingRowItem) 
            {
                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 columnIndexRanges = null; 
                int newItemIndex = -1;

                if (wasSelected)
                { 
                    // Unselect the item that was being added
                    UnselectItem(currentAddItem); 
                } 
                else
                { 
                    // Cells will automatically unselect when the new item is removed, but we
                    // should reselect them on the placeholder.
                    newItemIndex = Items.IndexOf(currentAddItem);
                    reselectPlaceholderCells = ((newItemIndex >= 0) && _selectedCells.Intersects(newItemIndex, out columnIndexRanges)); 
                }
 
                // Cancel the add and remove it from the collection 
                EditableItems.CancelNew();
 
                // Show the placeholder again
                UpdateNewItemPlaceholder(/* isAddingNewItem = */ false);

                if (wasCurrent) 
                {
                    // Focus the placeholder if the new item had focus 
                    SetCurrentValueInternal(CurrentItemProperty, CollectionView.NewItemPlaceholder); 
                }
 
                if (wasSelected)
                {
                    // Re-select the placeholder if it was selected before
                    SelectItem(CollectionView.NewItemPlaceholder); 
                }
                else if (reselectPlaceholderCells) 
                { 
                    // Re-select placeholder cells if they were selected before
                    using (UpdateSelectedCells()) 
                    {
                        int rowIndex = newItemIndex;
                        bool placeholderAtBeginning = (EditableItems.NewItemPlaceholderPosition == NewItemPlaceholderPosition.AtBeginning);
 
                        // When the placeholder is at the beginning, we need to unselect the cells
                        // in the added row and move those back to the previous row. 
                        if (placeholderAtBeginning) 
                        {
                            _selectedCells.RemoveRegion(newItemIndex, 0, 1, Columns.Count); 
                            rowIndex--;
                        }

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

        private void UpdateRowEditing(DataGridCell cell) 
        {
            object rowDataItem = cell.RowDataItem; 
 
            // If the row is not in edit/add mode, then clear its IsEditing flag.
            if (!IsAddingOrEditingRowItem(rowDataItem)) 
            {
                cell.RowOwner.IsEditing = false;
                _editingRowItem = null;
                _editingRowIndex = -1; 
            }
        } 
 
        private IEditableCollectionView EditableItems
        { 
            get { return (IEditableCollectionView)Items; }
        }

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

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

        private bool 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)
                { 
                    SetCurrentValueInternal(CurrentItemProperty, Items[itemCount - 1]); 
                }
            } 
            else if (position == NewItemPlaceholderPosition.AtBeginning)
            {
                if (Items.Count > 0)
                { 
                    SetCurrentValueInternal(CurrentItemProperty, 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
 
        /// 
        ///     Determines which visibility mode the Row's details use. 
        ///  
        public DataGridRowDetailsVisibilityMode RowDetailsVisibilityMode
        { 
            get { return (DataGridRowDetailsVisibilityMode)GetValue(RowDetailsVisibilityModeProperty); }
            set { SetValue(RowDetailsVisibilityModeProperty, value); }
        }
 
        /// 
        ///     DependencyProperty for RowDetailsVisibilityMode. 
        ///  
        public static readonly DependencyProperty RowDetailsVisibilityModeProperty =
            DependencyProperty.Register("RowDetailsVisibilityMode", typeof(DataGridRowDetailsVisibilityMode), typeof(DataGrid), new FrameworkPropertyMetadata(DataGridRowDetailsVisibilityMode.VisibleWhenSelected, OnNotifyRowAndDetailsPropertyChanged)); 

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

        /// 
        ///     DependencyProperty for AreRowDetailsFrozen.
        ///  
        public static readonly DependencyProperty AreRowDetailsFrozenProperty =
            DependencyProperty.Register("AreRowDetailsFrozen", typeof(bool), typeof(DataGrid), new FrameworkPropertyMetadata(false)); 
 
        /// 
        ///     Template used for the Row details. 
        /// 
        public DataTemplate RowDetailsTemplate
        {
            get { return (DataTemplate)GetValue(RowDetailsTemplateProperty); } 
            set { SetValue(RowDetailsTemplateProperty, value); }
        } 
 
        /// 
        ///     DependencyProperty for RowDetailsTemplate. 
        /// 
        public static readonly DependencyProperty RowDetailsTemplateProperty =
            DependencyProperty.Register("RowDetailsTemplate", typeof(DataTemplate), typeof(DataGrid), new FrameworkPropertyMetadata(null, OnNotifyRowAndDetailsPropertyChanged));
 
        /// 
        ///     TemplateSelector used for the Row details 
        ///  
        public DataTemplateSelector RowDetailsTemplateSelector
        { 
            get { return (DataTemplateSelector)GetValue(RowDetailsTemplateSelectorProperty); }
            set { SetValue(RowDetailsTemplateSelectorProperty, value); }
        }
 
        /// 
        ///     DependencyProperty for RowDetailsTemplateSelector. 
        ///  
        public static readonly DependencyProperty RowDetailsTemplateSelectorProperty =
            DependencyProperty.Register("RowDetailsTemplateSelector", typeof(DataTemplateSelector), typeof(DataGrid), new FrameworkPropertyMetadata(null, OnNotifyRowAndDetailsPropertyChanged)); 

        /// 
        ///     Event that is fired just before the details of a Row is shown
        ///  
        public event EventHandler LoadingRowDetails;
 
        ///  
        ///     Event that is fired just before the details of a Row is hidden
        ///  
        public event EventHandler UnloadingRowDetails;

        /// 
        ///     Event that is fired when the visibility of a Rows details changes. 
        /// 
        public event EventHandler 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;
            } 
        }
 
        ///  
        ///     Invokes the LoadingRowDetails event
        ///  
        protected virtual void OnLoadingRowDetails(DataGridRowDetailsEventArgs e)
        {
            if (LoadingRowDetails != null)
            { 
                LoadingRowDetails(this, e);
            } 
        } 

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

        /// 
        ///     Invokes the RowDetailsVisibilityChanged event
        ///  
        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 
 
        /// 
        ///     A property that specifies whether the user can resize rows in the UI by dragging the row headers. 
        /// 
        public bool CanUserResizeRows
        {
            get { return (bool)GetValue(CanUserResizeRowsProperty); } 
            set { SetValue(CanUserResizeRowsProperty, value); }
        } 
 
        /// 
        ///     The DependencyProperty that represents the CanUserResizeColumns property. 
        /// 
        public static readonly DependencyProperty CanUserResizeRowsProperty =
            DependencyProperty.Register("CanUserResizeRows", typeof(bool), typeof(DataGrid), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnNotifyRowHeaderPropertyChanged)));
 
        #endregion
 
        #region Selection 

        ///  
        ///     The currently selected cells.
        /// 
        public IList SelectedCells
        { 
            get { return _selectedCells; }
        } 
 
        internal SelectedCellsCollection SelectedCellsInternal
        { 
            get { return _selectedCells; }
        }

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

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

            // Update the pending list of changes
            if (oldItems != null) 
            {
                // When IsUpdatingSelectedCells is true, there may have been cells 
                // added to _pendingSelectedCells that are now being removed. 
                // These cells should be removed from _pendingSelectedCells and
                // not added to _pendingUnselectedCells. 
                if (_pendingSelectedCells != null)
                {
                    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(); 
                            }
                        }
                    }
                } 
            }
        } 
 
        /// 
        ///     Fires the public change event when there are pending cell changes. 
        /// 
        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(); 
                }
            }
        }
 
        /// 
        ///     Called when there are changes to the SelectedCells collection. 
        ///  
        /// Event arguments that indicate which cells were added or removed.
        ///  
        ///     Base implementation fires the public SelectedCellsChanged event.
        /// 
        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); 
                }
            }
        }
 
        /// 
        ///     A command that, when invoked, will select all items in the DataGrid. 
        ///  
        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;
        } 

        /// 
        ///     Called from the public SelectAll API.
        ///  
        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); 
            }
        } 

        /// 
        ///     Selects all cells.
        ///  
        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); 
                    }
                } 
            }
        }

        ///  
        ///     Unselects all cells.
        ///  
        public void UnselectAllCells() 
        {
            using (UpdateSelectedCells()) 
            {
                // Unselect all of the cells
                _selectedCells.Clear();
 
                if (SelectionUnit != DataGridSelectionUnit.Cell)
                { 
                    // Unselect all the items 
                    UnselectAll();
                } 
            }
        }

        ///  
        ///     Defines the selection behavior.
        ///  
        ///  
        ///     The SelectionMode and the SelectionUnit properties together define
        ///     the selection behavior for the DataGrid. 
        /// 
        public DataGridSelectionMode SelectionMode
        {
            get { return (DataGridSelectionMode)GetValue(SelectionModeProperty); } 
            set { SetValue(SelectionModeProperty, value); }
        } 
 
        /// 
        ///     The DependencyProperty for the SelectionMode property. 
        /// 
        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.Items.IndexOf(dataGrid.SelectedItems[0])); 
                    }
                } 
                else
                {
                    // If there is no selected item, then de-select all cells except for one.
                    using (dataGrid.UpdateSelectedCells()) 
                    {
                        dataGrid._selectedCells.RemoveAllButOne(); 
                    } 
                }
            } 
        }

        /// 
        ///     Defines the selection behavior. 
        /// 
        ///  
        ///     The SelectionMode and the SelectionUnit properties together define 
        ///     the selection behavior for the DataGrid.
        ///  
        public DataGridSelectionUnit SelectionUnit
        {
            get { return (DataGridSelectionUnit)GetValue(SelectionUnitProperty); }
            set { SetValue(SelectionUnitProperty, value); } 
        }
 
        ///  
        ///     The DependencyProperty for the SelectionUnit property.
        ///  
        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);
        } 
 
        /// 
        ///     Called when SelectedItems changes. 
        /// 
        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            if (!IsUpdatingSelectedCells) 
            {
                using (UpdateSelectedCells()) 
                { 
                    // Remove cells of rows that were deselected
                    int count = e.RemovedItems.Count; 
                    for (int i = 0; i < count; i++)
                    {
                        object rowItem = e.RemovedItems[i];
                        UpdateSelectionOfCellsInRow(rowItem, /* isSelected = */ false); 
                    }
 
                    // Add cells of rows that were selected 
                    count = e.AddedItems.Count;
                    for (int i = 0; i < count; i++) 
                    {
                        object rowItem = e.AddedItems[i];
                        UpdateSelectionOfCellsInRow(rowItem, /* isSelected = */ true);
                    } 
                }
            } 
 
            // Delete depends on the selection state
            CommandManager.InvalidateRequerySuggested(); 

            // Raise automation events
            if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected) ||
                AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementAddedToSelection) || 
                AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection))
            { 
                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);
        }
 
        /// 
        ///     Updates the IsSelected property on cells due to a change in SelectedCells. 
        ///  
        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 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 rowTracker = _rowTrackingRoot;
                        while (rowTracker != null)
                        { 
                            DataGridRow row = rowTracker.Container;
                            DataGridCellsPresenter cellsPresenter = row.CellsPresenter; 
                            if (cellsPresenter != null) 
                            {
                                ContainerTracking 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(object rowItem, bool isSelected)
        { 
            int rowIndex = Items.IndexOf(rowItem); 
            if (rowIndex >= 0)
            { 
                int columnCount = _columns.Count;
                if (columnCount > 0)
                {
                    if (isSelected) 
                    {
                        _selectedCells.AddRegion(rowIndex, 0, 1, columnCount); 
                    } 
                    else
                    { 
                        _selectedCells.RemoveRegion(rowIndex, 0, 1, columnCount);
                    }
                }
            } 
        }
 
        ///  
        ///     Notification that a particular cell's IsSelected property changed.
        ///  
        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);
                } 
            }
        } 
 
        /// 
        ///     There was general input that means that selection should occur on 
        ///     the given cell.
        /// 
        /// The target cell.
        /// Whether the input also indicated that dragging should start. 
        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(cell.RowDataItem, allowsExtendSelect, allowsMinimalSelect); 
            }
            else 
            { 
                // In the other modes, cells can be individually selected
                MakeCellSelection(new DataGridCellInfo(cell), allowsExtendSelect, allowsMinimalSelect); 
            }

            if (startDragging)
            { 
                BeginDragging();
            } 
        } 

        ///  
        ///     There was general input on a row header that indicated that
        ///     selection should occur on the given row.
        /// 
        /// The target row. 
        /// Whether the input also indicated that dragging should start.
        internal void HandleSelectionForRowHeaderAndDetailsInput(DataGridRow row, bool startDragging) 
        { 
            object rowItem = row.Item;
 
            // When not dragging, move focus to the first cell
            if (!_isDraggingSelection && (_columns.Count > 0))
            {
                if (!IsKeyboardFocusWithin) 
                {
                    // In order for CurrentCell to move focus, the 
                    // DataGrid needs to be focused. 
                    Focus();
                } 

                DataGridCellInfo currentCell = CurrentCell;
                if (currentCell.Item != rowItem)
                { 
                    // Change the CurrentCell if the row is different
                    SetCurrentValueInternal(CurrentCellProperty, new DataGridCellInfo(rowItem, 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(rowItem, /* allowsExtendSelect = */ true, /* allowsMinimalSelect = */ true);
 
                if (startDragging)
                { 
                    BeginRowDragging(); 
                }
            } 
        }

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

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

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

        /// 
        ///     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 
        /// 
        /// 
        ///     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.
        ///  
        private void MakeFullRowSelection(object dataItem, bool allowsExtendSelect, bool allowsMinimalSelect)
        {
            bool extendSelection = allowsExtendSelect && ShouldExtendSelection;
 
            // minimalModify means that previous selections should not be cleared
            // or that the particular item should be toggled. 
            bool minimalModify = allowsMinimalSelect && ShouldMinimallyModifySelection; 

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

                            if ((startIndex >= 0) && (endIndex >= 0)) 
                            {
                                IList selectedItems = SelectedItems;
                                int numItemsSelected = selectedItems.Count;
 
                                if (!minimalModify)
                                { 
                                    bool clearedCells = false; 

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

                                        if ((itemIndex < startIndex) || (endIndex < itemIndex)) 
                                        { 
                                            // Selector has been signaled to delay updating the
                                            // collection until we have finished the entire update. 
                                            // The item will actually remain in the collection
                                            // until EndUpdateSelectedItems.
                                            selectedItems.RemoveAt(index);
 
                                            if (!clearedCells)
                                            { 
                                                // We only want to clear if something is actually being removed. 
                                                _selectedCells.Clear();
                                                clearedCells = true; 
                                            }
                                        }
                                    }
                                } 
                                else
                                { 
                                    // If we hold Control key - unselect only the previous drag selection (between CurrentCell and endIndex) 
                                    int currentCellIndex = items.IndexOf(CurrentCell.Item);
                                    int removeRangeStartIndex = -1; 
                                    int removeRangeEndIndex = -1;
                                    if (currentCellIndex < startIndex)
                                    {
                                        removeRangeStartIndex = currentCellIndex; 
                                        removeRangeEndIndex = startIndex - 1;
                                    } 
                                    else if (currentCellIndex > endIndex) 
                                    {
                                        removeRangeStartIndex = endIndex + 1; 
                                        removeRangeEndIndex = currentCellIndex;
                                    }

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

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

                                        _selectedCells.RemoveRegion(removeRangeStartIndex, 0, removeRangeEndIndex - removeRangeStartIndex + 1, Columns.Count);
                                    } 
                                }
 
                                // Select the children in the selection range 
                                IEnumerator enumerator = ((IEnumerable)items).GetEnumerator();
                                for (int index = 0; index <= endIndex; index++) 
                                {
                                    if (!enumerator.MoveNext())
                                    {
                                        // In case the enumerator ends unexpectedly 
                                        break;
                                    } 
 
                                    if (index >= startIndex)
                                    { 
                                        selectedItems.Add(enumerator.Current);
                                    }
                                }
 
                                _selectedCells.AddRegion(startIndex, 0, endIndex - startIndex + 1, _columns.Count);
                            } 
                        } 
                    }
                    else 
                    {
                        if (minimalModify && SelectedItems.Contains(dataItem))
                        {
                            // Unselect the one item 
                            UnselectItem(dataItem);
                        } 
                        else 
                        {
                            if (!minimalModify || !CanSelectMultipleItems) 
                            {
                                // Unselect the other items
                                if (_selectedCells.Count > 0)
                                { 
                                    // Pre-emptively clear the SelectedCells collection, which is O(1),
                                    // instead of waiting for the selection change notification to clear 
                                    // SelectedCells row by row, which is O(n). 
                                    _selectedCells.Clear();
                                } 

                                if (SelectedItems.Count > 0)
                                {
                                    SelectedItems.Clear(); 
                                }
                            } 
 
                            if (_editingRowIndex >= 0 && _editingRowItem == dataItem)
                            { 
                                // ADO.Net bug workaround, see remarks.
                                int numColumns = _columns.Count;
                                if (numColumns > 0)
                                { 
                                    _selectedCells.AddRegion(_editingRowIndex, 0, 1, numColumns);
                                } 
 
                                SelectItem(dataItem, false);
                            } 
                            else
                            {
                                // Select the item
                                SelectItem(dataItem); 
                            }
                        } 
 
                        _selectionAnchor = new DataGridCellInfo(dataItem, ColumnFromDisplayIndex(0), this);
                    } 
                }
                finally
                {
                    if (!alreadyUpdating) 
                    {
                        EndUpdateSelectedItems(); 
                    } 
                }
            } 
        }

        /// 
        ///     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 
        /// 
        /// 
        ///     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.
        ///  
        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 = items.IndexOf(_selectionAnchor.Value.Item);
                    int endIndex = items.IndexOf(cellInfo.Item); 

                    if (_editingRowIndex >= 0)
                    {
                        // ADO.Net bug workaround, see remarks. 
                        if (_selectionAnchor.Value.Item == _editingRowItem)
                        { 
                            startIndex = _editingRowIndex; 
                        }
 
                        if (cellInfo.Item == _editingRowItem)
                        {
                            endIndex = _editingRowIndex;
                        } 
                    }
 
                    DataGridColumn anchorColumn = _selectionAnchor.Value.Column; 
                    int startColumnIndex = anchorColumn.DisplayIndex;
                    int endColumnIndex = cellInfoColumnIndex; 

                    if ((startIndex >= 0) && (endIndex >= 0) &&
                        (startColumnIndex >= 0) && (endColumnIndex >= 0))
                    { 
                        int newRowCount = Math.Abs(endIndex - startIndex) + 1;
                        int newColumnCount = Math.Abs(endColumnIndex - startColumnIndex) + 1; 
 
                        if (!minimalModify)
                        { 
                            // When extending cell selection, clear out any selected items
                            if (SelectedItems.Count > 0)
                            {
                                UnselectAll(); 
                            }
 
                            _selectedCells.Clear(); 
                        }
                        else 
                        {
                            // Remove the previously selected region
                            int currentCellIndex = items.IndexOf(CurrentCell.Item);
                            if (_editingRowIndex >= 0 && 
                                _editingRowItem == CurrentCell.Item)
                            { 
                                // ADO.Net bug workaround, see remarks. 
                                currentCellIndex = _editingRowIndex;
                            } 

                            int currentCellColumnIndex = CurrentCell.Column.DisplayIndex;

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

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

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

                                // For cells that were removed, check if their row is selected 
                                for (int i = removeRowStartIndex; i <= removeRowEndIndex; i++) 
                                {
                                    object item = Items[i]; 
                                    if (SelectedItems.Contains(item))
                                    {
                                        // When a cell in a row is unselected, unselect the row too
                                        SelectedItems.Remove(item); 
                                    }
                                } 
                            } 
                        }
 
                        // Select the cells in rows within the selection range
                        _selectedCells.AddRegion(Math.Min(startIndex, endIndex), Math.Min(startColumnIndex, endColumnIndex), newRowCount, newColumnCount);
                    }
                } 
                else
                { 
                    bool selectedCellsContainsCellInfo = _selectedCells.Contains(cellInfo); 
                    bool singleRowOperation = (_editingRowIndex >= 0 && _editingRowItem == cellInfo.Item);
                    if (!selectedCellsContainsCellInfo && 
                        singleRowOperation)
                    {
                        // ADO.Net bug workaround, see remarks.
                        selectedCellsContainsCellInfo = _selectedCells.Contains(_editingRowIndex, cellInfoColumnIndex); 
                    }
 
                    if (minimalModify && selectedCellsContainsCellInfo) 
                    {
                        // Unselect the one cell 
                        if (singleRowOperation)
                        {
                            // ADO.Net bug workaround, see remarks.
                            _selectedCells.RemoveRegion(_editingRowIndex, 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(_editingRowIndex, cellInfoColumnIndex, 1, 1);
                        } 
                        else
                        {
                            // Select the cell
                            _selectedCells.AddValidatedCell(cellInfo); 
                        }
                    } 
 
                    _selectionAnchor = cellInfo;
                } 
            }
        }

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

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

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

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

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

        ///  
        ///     Adds or Removes from SelectedItems when deferred selection is not handled by the caller. 
        /// 
        private void UpdateSelectedItems(object item, bool add) 
        {
            bool updatingSelectedItems = IsUpdatingSelectedItems;
            if (!updatingSelectedItems)
            { 
                BeginUpdateSelectedItems();
            } 
 
            try
            { 
                if (add)
                {
                    SelectedItems.Add(item);
                } 
                else
                { 
                    SelectedItems.Remove(item); 
                }
            } 
            finally
            {
                if (!updatingSelectedItems)
                { 
                    EndUpdateSelectedItems();
                } 
            } 
        }
 
        /// 
        ///     When changing SelectedCells, do:
        ///     using (UpdateSelectedCells())
        ///     { 
        ///         ...
        ///     } 
        ///  
        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; } 
        }
 
        ///  
        ///     Handles tracking defered selection change notifications for selected cells.
        ///  
        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;
        } 
 
        /// 
        ///     SHIFT is down or performing a drag selection. 
        ///     Multiple items can be selected.
        ///     There is a selection anchor.
        /// 
        private bool ShouldExtendSelection 
        {
            get 
            { 
                return CanSelectMultipleItems && (_selectionAnchor != null) &&
                    (_isDraggingSelection || ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)); 
            }
        }

        ///  
        ///     CTRL is down.
        ///     Previous selection should not be cleared, or a selected item should be toggled. 
        ///  
        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;

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

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

        #endregion 

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

            DataGrid dataGrid = (DataGrid)d; 
            if (!(bool)(e.NewValue))
            { 
                dataGrid.UnselectAllCells(); 
            }
 
            // Many commands use IsEnabled to determine if they are enabled or not
            CommandManager.InvalidateRequerySuggested();

            dataGrid.UpdateVisualState(); 
        }
 
        ///  
        /// Cells need to update their visual state when this property changes.
        ///  
        /// 
        /// 
        private static void OnIsKeyboardFocusWithinChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.Rows | DataGridNotificationTarget.RowHeaders | DataGridNotificationTarget.Cells);
        } 
 
        /// 
        ///     Called when a keyboard key is pressed. 
        /// 
        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;
            } 
        }

        /// 
        ///     Helper method which handles the arrow key down 
        /// 
        ///  
        ///     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.
        ///  
        private void OnArrowKeyDown(KeyEventArgs e)
        { 
            DataGridCell currentCellContainer = CurrentCellContainer; 
            if (currentCellContainer != null)
            { 
                e.Handled = true;
                bool wasEditing = currentCellContainer.IsEditing;

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

                            return;
                        } 

                        int currentDisplayIndex = this.CurrentColumn.DisplayIndex; 
                        object currentItem = CurrentItem; 
                        int currentRowIndex = Items.IndexOf(currentItem);
 
                        // ADO.Net bug workaround, see remarks.
                        if (_editingRowIndex >= 0 && currentItem == _editingRowItem)
                        {
                            currentRowIndex = _editingRowIndex; 
                        }
 
                        int nextDisplayIndex = currentDisplayIndex; 
                        int nextRowIndex = currentRowIndex;
                        bool controlModifier = ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control); 

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

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

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

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

                        DataGridColumn nextColumn = ColumnFromDisplayIndex(nextDisplayIndex);
                        object nextItem = Items[nextRowIndex];
                        ScrollCellIntoView(nextItem, nextColumn); 
                        DataGridCell nextCellContainer = TryFindCell(nextItem, nextColumn);
 
                        if (nextCellContainer == null || nextCellContainer == currentCellContainer || !nextCellContainer.Focus()) 
                        {
                            return; 
                        }
                    }

                    // Attempt to move focus 
                    TraversalRequest request = new TraversalRequest(KeyToTraversalDirection(e.Key));
                    if (navigateFromCellContainer || 
                        ((startElement != null) && startElement.MoveFocus(request)) || 
                        ((startContentElement != null) && startContentElement.MoveFocus(request)))
                    { 
                        SelectAndEditOnFocusMove(e, currentCellContainer, wasEditing, /* allowsExtendSelect = */ true, /* ignoreControlKey = */ true);
                    }
                }
            } 
        }
 
        ///  
        ///     Called when the tab key is pressed to perform focus navigation.
        ///  
        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); 
                    request.Wrapped = true; // Navigate only within datagrid 

                    // Move focus to the the next or previous tab stop. 
                    if (((startElement != null) && startElement.MoveFocus(request)) ||
                        ((startContentElement != null) && startContentElement.MoveFocus(request)))
                    {
                        // If focus moved to the cell while in edit mode - keep navigating to the previous cell 
                        if (wasEditing && previous && Keyboard.FocusedElement == currentCellContainer)
                        { 
                            currentCellContainer.MoveFocus(request); 
                        }
 
                        // 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 = Math.Max(0, Math.Min(numItems - 1, Items.IndexOf(currentCellContainer.RowDataItem) + (shiftModifier ? -1 : 1))); 

                    if (index < numItems)
                    {
                        object rowItem = Items[index]; 
                        ScrollIntoView(rowItem, column);
 
                        if (CurrentCell.Item != rowItem) 
                        {
                            // Focus the new cell 
                            SetCurrentValueInternal(CurrentCellProperty, new DataGridCellInfo(rowItem, column, this));

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

        private 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);
 
                // Go to the first or last cell 
                object item = controlModifier ? Items[homeKey ? 0 : Items.Count - 1] : CurrentItem;
                DataGridColumn column = ColumnFromDisplayIndex(homeKey ? InternalColumns.FirstVisibleDisplayIndex : InternalColumns.LastVisibleDisplayIndex); 

                ScrollCellIntoView(item, column);
                DataGridCell cell = TryFindCell(item, 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)
            { 
                object currentRow = CurrentItem;
                DataGridColumn currentColumn = CurrentColumn;
                int rowIndex = Items.IndexOf(currentRow);
                if (rowIndex >= 0) 
                {
                    // Predict the page up/page down item based on the viewport height, which 
                    // should be in logical units. 
                    // This is not going to work well when the rows have different heights, but
                    // it is the best estimate we have at the moment. 
                    int jumpDistance = Math.Max(1, (int)scrollHost.ViewportHeight - 1);
                    int targetIndex = (e.Key == Key.PageUp) ? rowIndex - jumpDistance : rowIndex + jumpDistance;
                    targetIndex = Math.Max(0, Math.Min(targetIndex, Items.Count - 1));
 
                    // Scroll the target row into view, keeping the current column
                    object targetRow = Items[targetIndex]; 
 
                    if (currentColumn == null)
                    { 
                        ScrollRowIntoView(targetRow);
                        SetCurrentValueInternal(CurrentItemProperty, targetRow);
                    }
                    else 
                    {
                        ScrollCellIntoView(targetRow, currentColumn); 
                        DataGridCell cell = TryFindCell(targetRow, currentColumn); 
                        if (cell != null)
                        { 
                            cell.Focus();
                            if (ShouldSelectRowHeader)
                            {
                                HandleSelectionForRowHeaderAndDetailsInput(cell.RowOwner, /* startDragging = */ false); 
                            }
                            else 
                            { 
                                HandleSelectionForCellInput(cell, /* startDragging = */ false, /* allowsExtendSelect = */ true, /* allowsMinimalSelect = */ false);
                            } 
                        }
                    }
                }
            } 
        }
 
        ///  
        ///     Continues a drag selection.
        ///  
        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);
                                    SetCurrentValueInternal(CurrentItemProperty, 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);
                                    SetCurrentValueInternal(CurrentItemProperty, 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);
        }

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

        /// 
        ///     When a ContextMenu opens on a cell that isn't selected, it should 
        ///     become selected.
        ///  
        protected override void OnContextMenuOpening(ContextMenuEventArgs e) 
        {
            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)
                {
                    HandleSelectionForRowHeaderAndDetailsInput(parentRow, /* startDragging = */ false); 
                }
            } 
        } 

        ///  
        ///     Finds the row that contains the mouse's Y coordinate.
        /// 
        /// 
        ///     Relies on InternalItemsHost. 
        ///     Meant to be used when the mouse is outside the DataGrid.
        ///  
        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; 
        }

        /// 
        ///     Finds the cell that is nearest to the mouse. 
        /// 
        ///  
        ///     Relies on InternalItemsHost. 
        /// 
        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 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; 
        } 

        ///  
        ///     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.
        /// 
        ///  
        ///     A value that represents the distance between the mouse and the cell.
        ///     This is not necessarily an accurate pixel number in some cases. 
        ///  
        /// 
        ///     true if the cell can be a drag target. false otherwise. 
        /// 
        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;
        } 
 
        /// 
        ///     The row that the mouse is over. 
        /// 
        private static DataGridRow MouseOverRow
        {
            get 
            {
                return DataGridHelper.FindVisualParent(Mouse.DirectlyOver as UIElement); 
            } 
        }
 
        // The cell that the mouse is over.
        private static DataGridCell MouseOverCell
        {
            get 
            {
                return DataGridHelper.FindVisualParent(Mouse.DirectlyOver as UIElement); 
            } 
        }
 
        /// 
        ///     The mouse position relative to the ItemsHost.
        /// 
        ///  
        ///     Relies on InternalItemsHost.
        ///  
        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 (!Object.Equals(item, _editingRowItem) || 
                !_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(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 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;
                        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

        private DataGridCell TryFindCell(DataGridCellInfo info) 
        {
            // Does not de-virtualize cells 
            return TryFindCell(info.Item, info.Column); 
        }
 
        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 

        ///  
        /// Dependecy property for CanUserSortColumns Property
        /// 
        public static readonly DependencyProperty CanUserSortColumnsProperty =
            DependencyProperty.Register( 
                "CanUserSortColumns",
                typeof(bool), 
                typeof(DataGrid), 
                new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnCanUserSortColumnsPropertyChanged), new CoerceValueCallback(OnCoerceCanUserSortColumns)));
 
        /// 
        /// The property which determines whether the datagrid can be sorted by
        /// cells in the columns or not
        ///  
        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;
 
        /// 
        /// Protected method which raises the sorting event and does default sort 
        ///  
        /// 
        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); 
            }
        } 
 
        /// 
        /// Method to perform sorting on datagrid 
        /// 
        /// 
        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.Get(SRID.DataGrid_ProbableInvalidSortDescription), invalidOperationException);
                    }
                }
            } 
        }
 
        ///  
        /// Clears the sort directions for all the columns except the column to be sorted upon
        ///  
        /// 
        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; 
                    } 
                }
            } 
        }

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

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

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

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

            set
            {
                _groupingSortDescriptionIndices = value; 
            }
        } 
 
        /// 
        /// SortDescription collection changed listener. Ensures that GroupingSortDescriptionIndices 
        /// is in [....] with SortDescriptions.
        /// 
        /// 
        ///  
        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; 
            } 
        }
 
        /// 
        /// Method to remove all the SortDescriptions which were added based on GroupDescriptions
        /// 
        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; 
            } 
        }
 
        /// 
        /// Helper method which determines if one can create a SortDescription out of
        /// a GroupDescription.
        ///  
        /// 
        ///  
        private static bool CanConvertToSortDescription(PropertyGroupDescription propertyGroupDescription) 
        {
            if (propertyGroupDescription != null && 
                propertyGroupDescription.Converter == null &&
                propertyGroupDescription.StringComparison == StringComparison.Ordinal)
            {
                return true; 
            }
 
            return false; 
        }
 
        /// 
        /// Method to add SortDescriptions based on GroupDescriptions.
        /// Only PropertGroupDescriptions with no ValueConverter and with
        /// Oridinal comparison are considered suitable. 
        /// 
        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(); 
                        }
 
                        GroupingSortDescriptionIndices.Add(insertIndex++); 
                    }
                } 
            }
            finally
            {
                _ignoreSortDescriptionsChange = originalIgnoreSortDescriptionChanges; 
            }
        } 
 
        /// 
        /// Method to regenrated the SortDescriptions based on the GroupDescriptions 
        /// 
        private void RegenerateGroupingSortDescriptions()
        {
            RemoveGroupingSortDescriptions(); 
            AddGroupingSortDescriptions();
        } 
 
        /// 
        /// CollectionChanged listener for GroupDescriptions of DataGrid. 
        /// Regenerates Grouping based sort descriptions is required.
        /// 
        /// 
        ///  
        private void OnItemsGroupDescriptionsChanged(object sender, NotifyCollectionChangedEventArgs e)
        { 
            if (!_sortingStarted) 
            {
                return; 
            }

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

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

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

        #endregion
 
        #region Column Auto Generation
 
        ///  
        /// This event will be raised whenever auto generation of columns gets completed
        ///  
        public event EventHandler AutoGeneratedColumns;

        /// 
        /// This event will be raised for each column getting auto generated 
        /// 
        public event EventHandler AutoGeneratingColumn; 
 
        /// 
        ///     The DependencyProperty that represents the AutoGenerateColumns property. 
        /// 
        public static readonly DependencyProperty AutoGenerateColumnsProperty =
            DependencyProperty.Register("AutoGenerateColumns", typeof(bool), typeof(DataGrid), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnAutoGenerateColumnsPropertyChanged)));
 
        /// 
        /// The property which determines whether the columns are to be auto generated or not. 
        /// Setting of the property actually generates or deletes columns. 
        /// 
        public bool AutoGenerateColumns 
        {
            get { return (bool)GetValue(AutoGenerateColumnsProperty); }
            set { SetValue(AutoGenerateColumnsProperty, value); }
        } 

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

        ///  
        /// The polymorphic method which raises the AutoGeneratingColumn event
        /// 
        /// 
        protected virtual void OnAutoGeneratingColumn(DataGridAutoGeneratingColumnEventArgs e) 
        {
            if (AutoGeneratingColumn != null) 
            { 
                AutoGeneratingColumn(this, e);
            } 
        }

        /// 
        ///     Determines the desired size of the control given a constraint. 
        /// 
        ///  
        ///     On the first measure: 
        ///     - Performs auto-generation of columns if needed.
        ///     - Coerces CanUserAddRows and CanUserDeleteRows. 
        ///     - Updates the NewItemPlaceholder.
        /// 
        /// The available space.
        /// The desired size of the control. 
        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);
            } 
        }

        /// 
        ///     Helper method to clear SortDescriptions and all related 
        ///     member when ItemsSource changes
        ///  
        private void ClearSortDescriptionsOnItemsSourceChange() 
        {
            Items.SortDescriptions.Clear(); 
            _sortingStarted = false;
            List groupingSortDescriptionIndices = GroupingSortDescriptionIndices;
            if (groupingSortDescriptionIndices != null)
            { 
                groupingSortDescriptionIndices.Clear();
            } 
            foreach (DataGridColumn column in Columns) 
            {
                column.SortDirection = null; 
            }
        }

        ///  
        ///     Coercion callback for ItemsSource property
        ///  
        ///  
        ///     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.
        ///  
        private static object OnCoerceItemsSourceProperty(DependencyObject d, object baseValue) 
        {
            DataGrid dataGrid = (DataGrid)d; 
            if (baseValue != dataGrid._cachedItemsSource && dataGrid._cachedItemsSource != null)
            {
                dataGrid.ClearSortDescriptionsOnItemsSourceChange();
            } 

            return baseValue; 
        } 

        ///  
        /// The polymorphic method which gets called whenever the ItemsSource gets changed.
        /// We regenerate columns if required when ItemsSource gets changed.
        /// 
        ///  
        /// 
        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 [....]. 
                _selectedCells.RestoreOnlyFullRows(SelectedItems); 
            }
 
            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; 
        }

        /// 
        ///     The flag which determines whether the columns generation is deferred 
        /// 
        private bool DeferAutoGeneration 
        { 
            get;
            set; 
        }

        /// 
        ///     Performs column auto generation and updates validation flags 
        ///     on items change.
        ///  
        ///  
        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;
            }
        } 

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

        /// 
        /// Method which deletes all the auto generated columns.
        ///  
        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;
            }
        }
 
        /// 
        /// Method which regenerates the columns for the datagrid 
        ///  
        private void RegenerateAutoColumns()
        { 
            DeleteAutoColumns();
            AddAutoColumns();
        }
 
        /// 
        /// Helper method which generates columns for a given IItemProperties 
        ///  
        /// 
        ///  
        public static Collection GenerateColumns(IItemProperties itemProperties)
        {
            if (itemProperties == null)
            { 
                throw new ArgumentNullException("itemProperties");
            } 
 
            Collection columnCollection = new Collection();
            DataGrid.GenerateColumns( 
                itemProperties,
                null,
                columnCollection);
            return columnCollection; 
        }
 
        ///  
        /// 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. 
        /// 
        /// 
        /// 
        ///  
        private static void GenerateColumns(
            IItemProperties iItemProperties, 
            DataGrid dataGrid, 
            Collection columnCollection)
        { 
            Debug.Assert(iItemProperties != null, "iItemProperties should not be null");
            Debug.Assert(dataGrid != null || columnCollection != null, "Both dataGrid and columnCollection cannot not be null at the same time");

            ReadOnlyCollection itemProperties = iItemProperties.ItemProperties; 

            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);
                    }
                }
            } 
        }
 
        ///  
        /// The event listener which listens to the change in the AutoGenerateColumns flag
        ///  
        /// 
        /// 
        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
 
        /// 
        /// Dependency Property fro FrozenColumnCount Property 
        ///  
        public static readonly DependencyProperty FrozenColumnCountProperty =
            DependencyProperty.Register( 
                "FrozenColumnCount",
                typeof(int),
                typeof(DataGrid),
                new FrameworkPropertyMetadata(0, new PropertyChangedCallback(OnFrozenColumnCountPropertyChanged), new CoerceValueCallback(OnCoerceFrozenColumnCount)), 
                new ValidateValueCallback(ValidateFrozenColumnCount));
 
        ///  
        /// Property which determines the number of columns which are frozen from the beginning in order of display
        ///  
        public int FrozenColumnCount
        {
            get { return (int)GetValue(FrozenColumnCountProperty); }
            set { SetValue(FrozenColumnCountProperty, value); } 
        }
 
        ///  
        /// Coercion call back for FrozenColumnCount property, which ensures that it is never more that column count
        ///  
        /// 
        /// 
        /// 
        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;
        } 
 
        /// 
        /// Property changed callback fro FrozenColumnCount 
        /// 
        /// 
        /// 
        private static void OnFrozenColumnCountPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.ColumnCollection | DataGridNotificationTarget.ColumnHeadersPresenter | DataGridNotificationTarget.CellsPresenter); 
        } 

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

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

        /// 
        /// Dependency property for NonFrozenColumnsViewportHorizontalOffset Property 
        /// 
        public static readonly DependencyProperty NonFrozenColumnsViewportHorizontalOffsetProperty = NonFrozenColumnsViewportHorizontalOffsetPropertyKey.DependencyProperty; 
 
        /// 
        /// Property which gets/sets the start x coordinate of non frozen columns in view port 
        /// 
        public double NonFrozenColumnsViewportHorizontalOffset
        {
            get 
            {
                return (double)GetValue(NonFrozenColumnsViewportHorizontalOffsetProperty); 
            } 

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

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

        #endregion 
 
        #region Container Virtualization
 
        /// 
        ///     Property which determines if row virtualization is enabled or disabled
        /// 
        public bool EnableRowVirtualization 
        {
            get { return (bool)GetValue(EnableRowVirtualizationProperty); } 
            set { SetValue(EnableRowVirtualizationProperty, value); } 
        }
 
        /// 
        ///     Dependency property for EnableRowVirtualization
        /// 
        public static readonly DependencyProperty EnableRowVirtualizationProperty = DependencyProperty.Register( 
            "EnableRowVirtualization",
            typeof(bool), 
            typeof(DataGrid), 
            new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnEnableRowVirtualizationChanged)));
 
        /// 
        ///     Property changed callback for EnableRowVirtualization.
        ///     Keeps VirtualizingStackPanel.IsVirtualizingProperty in [....].
        ///  
        private static void OnEnableRowVirtualizationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            DataGrid dataGrid = (DataGrid)d; 
            dataGrid.CoerceValue(VirtualizingStackPanel.IsVirtualizingProperty);
 
            Panel itemsHost = dataGrid.InternalItemsHost;
            if (itemsHost != null)
            {
                itemsHost.InvalidateMeasure(); 
                itemsHost.InvalidateArrange();
            } 
        } 

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

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

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

        ///  
        ///     Property changed callback for EnableColumnVirtualization.
        ///     Gets VirtualizingStackPanel.IsVirtualizingProperty for cells presenter and 
        ///     headers presenter in [....]. 
        /// 
        private static void OnEnableColumnVirtualizationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            ((DataGrid)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.CellsPresenter | DataGridNotificationTarget.ColumnHeadersPresenter | DataGridNotificationTarget.ColumnCollection);
        }
 
        #endregion
 
        #region Column Reordering 

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

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

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

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

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

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

        public event EventHandler ColumnReordering; 

        public event EventHandler ColumnHeaderDragStarted;

        public event EventHandler ColumnHeaderDragDelta; 

        public event EventHandler ColumnHeaderDragCompleted; 
 
        public event EventHandler 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 

        ///  
        ///     The DependencyProperty that represents the ClipboardCopyMode property.
        /// 
        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(); 
        }

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

        private static void OnCanExecuteCopy(object target, CanExecuteRoutedEventArgs args)
        { 
            ((DataGrid)target).OnCanExecuteCopy(args);
        } 
 
        /// 
        /// This virtual method is called when ApplicationCommands.Copy command query its state. 
        /// 
        /// 
        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);
        }
 
        /// 
        /// This virtual method is called when ApplicationCommands.Copy command is executed. 
        ///  
        /// 
        /// 
        /// Critical: Calls Secuirty Critical Clipboard operations in full and partial trust.
        /// TreatAsSafe: Only puts text on the clipboard in full trust or a userinitiated command in partial trust through secured critical functions.
        ///
        [SecurityCritical, SecurityTreatAsSafe] 
        protected virtual void OnExecutedCopy(ExecutedRoutedEventArgs args)
        { 
            if (ClipboardCopyMode == DataGridClipboardCopyMode.None) 
            {
                throw new NotSupportedException(SR.Get(SRID.ClipboardCopyMode_Disabled)); 
            }

            args.Handled = true;
 
            // Supported default formats: Html, Text, UnicodeText and CSV
            Collection formats = new Collection(new string[] { DataFormats.Html, DataFormats.Text, DataFormats.UnicodeText, DataFormats.CommaSeparatedValue }); 
            Dictionary dataGridStringBuilders = new Dictionary(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; 
            bool hasPerms = SecurityHelper.CallerHasAllClipboardPermission() && SecurityHelper.CallerHasSerializationPermission();

            // Copy unconditionally in full trust.
            // Only copy in partial trust if user initiated. 
            if (hasPerms ||  args.UserInitiated )
            { 
                (new UIPermission(UIPermissionClipboard.AllClipboard)).Assert(); 
                try
                { 
                    dataObject = new DataObject();
                }
                finally
                { 
                    UIPermission.RevertAssert();
                } 
 
                foreach (string format in formats)
                { 
                        dataObject.CriticalSetData(format, dataGridStringBuilders[format].ToString(), false /*autoConvert*/);
                }

                // This assert is there for an OLE Callback to register CSV type for the clipboard 
                (new SecurityPermission(SecurityPermissionFlag.SerializationFormatter | SecurityPermissionFlag.UnmanagedCode)).Assert();
                try 
                { 
                    Clipboard.CriticalSetDataObject(dataObject, true /* Copy */);
                } 
                finally
                {
                    SecurityPermission.RevertAll();
                } 
            }
        } 
 
        /// 
        /// 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
        /// 
        /// Contains the necessary information for generating the row clipboard content.
        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); 
            }
        }

        ///  
        /// 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 
        ///  
        public event EventHandler CopyingRowClipboardContent;
        #endregion 

        #region Cells Panel Width

        ///  
        /// Dependency Property for CellsPanelActualWidth property
        ///  
        internal static readonly DependencyProperty CellsPanelActualWidthProperty = 
            DependencyProperty.Register(
                        "CellsPanelActualWidth", 
                        typeof(double),
                        typeof(DataGrid),
                        new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(CellsPanelActualWidthChanged)));
 
        /// 
        /// The property which represents the actual width of the cells panel, 
        /// to be used by headers presenter 
        /// 
        internal double CellsPanelActualWidth 
        {
            get
            {
                return (double)GetValue(CellsPanelActualWidthProperty); 
            }
 
            set 
            {
                SetValue(CellsPanelActualWidthProperty, value); 
            }
        }

        ///  
        /// Property changed callback for CellsPanelActualWidth property
        ///  
        ///  
        /// 
        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
 
        /// 
        ///     Dependency Property Key for CellsPanelHorizontalOffset property 
        ///  
        private static readonly DependencyPropertyKey CellsPanelHorizontalOffsetPropertyKey =
            DependencyProperty.RegisterReadOnly( 
                "CellsPanelHorizontalOffset",
                typeof(double),
                typeof(DataGrid),
                new FrameworkPropertyMetadata(0d, OnNotifyHorizontalOffsetPropertyChanged)); 

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

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

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

        /// 
        ///     Helper method which queue a request to dispatcher to 
        ///     invalidate the cellspanel offset if not already queued
        ///  
        internal void QueueInvalidateCellsPanelHorizontalOffset() 
        {
            if (!CellsPanelHorizontalOffsetComputationPending) 
            {
                Dispatcher.BeginInvoke(new DispatcherOperationCallback(InvalidateCellsPanelHorizontalOffset), DispatcherPriority.Loaded, this);
                CellsPanelHorizontalOffsetComputationPending = true;
            } 
        }
 
        ///  
        ///     Dispatcher call back method which recomputes the CellsPanelOffset
        ///  
        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;
        } 
 

        ///  
        /// Helper method which return any one of the cells or column headers
        /// 
        /// 
        internal IProvideDataGridColumn GetAnyCellOrColumnHeader() 
        {
            // Find the best try at a visible cell from a visible row. 
            if (_rowTrackingRoot != null) 
            {
                ContainerTracking rowTracker = _rowTrackingRoot; 
                while (rowTracker != null)
                {
                    if (rowTracker.Container.IsVisible)
                    { 
                        DataGridCellsPresenter cellsPresenter = rowTracker.Container.CellsPresenter;
                        if (cellsPresenter != null) 
                        { 
                            ContainerTracking 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 headerTracker = ColumnHeadersPresenter.HeaderTrackingRoot; 
                while (headerTracker != null)
                {
                    if (headerTracker.Container.IsVisible)
                    { 
                        return headerTracker.Container;
                    } 
                    headerTracker = headerTracker.Next; 
                }
            } 

            return null;
        }
 
        /// 
        /// Helper method which returns the width of the viewport which is available for the columns to render 
        ///  
        /// 
        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
 
        // 

 
        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 _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 Nullable _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 _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 _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 object _editingRowItem = null;                              // Current editing row item
        private int _editingRowIndex = -1;                                  // Current editing row index 
        private bool _hasCellValidationError;                               // An unsuccessful cell commit occurred
        private bool _hasRowValidationError;                                // An unsuccessful row commit occurred 
        private IEnumerable _cachedItemsSource = null;                      // Reference to the ItemsSource instance, used to clear SortDescriptions on ItemsSource change 
        private 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 _editingCellAutomationValueHolders
                    = new Dictionary(); // Holds the content of edited cells. Required for raising Automation events. 
        private DataGridCell _focusedCell = null;                           // Holds the cell which has logical focus.
 
        #endregion 

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

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK