DataGridCellsPresenter.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Controls / Primitives / DataGridCellsPresenter.cs / 1305600 / DataGridCellsPresenter.cs

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

using System; 
using System.Collections.ObjectModel; 
using System.Collections.Specialized;
using System.ComponentModel; 
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media; 
using MS.Internal;
 
namespace System.Windows.Controls.Primitives 
{
    ///  
    ///     A control that will be responsible for generating cells.
    ///     This control is meant to be specified within the template of a DataGridRow.
    ///     The APIs from ItemsControl do not match up nicely with the meaning of a
    ///     row, which is why this is being factored out. 
    ///
    ///     The data item for the row is added n times to the Items collection, 
    ///     where n is the number of columns in the DataGrid. This is implemented 
    ///     using a special collection to avoid keeping multiple references to the
    ///     same object. 
    /// 
    public class DataGridCellsPresenter : ItemsControl
    {
        #region Constructors 

        ///  
        ///     Instantiates global information. 
        /// 
        static DataGridCellsPresenter() 
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(DataGridCellsPresenter), new FrameworkPropertyMetadata(typeof(DataGridCellsPresenter)));
            ItemsPanelProperty.OverrideMetadata(typeof(DataGridCellsPresenter), new FrameworkPropertyMetadata(new ItemsPanelTemplate(new FrameworkElementFactory(typeof(DataGridCellsPanel)))));
            FocusableProperty.OverrideMetadata(typeof(DataGridCellsPresenter), new FrameworkPropertyMetadata(false)); 

            HeightProperty.OverrideMetadata(typeof(DataGridCellsPresenter), new FrameworkPropertyMetadata(OnNotifyHeightPropertyChanged, OnCoerceHeight)); 
            MinHeightProperty.OverrideMetadata(typeof(DataGridCellsPresenter), new FrameworkPropertyMetadata(OnNotifyHeightPropertyChanged, OnCoerceMinHeight)); 

            VirtualizingStackPanel.IsVirtualizingProperty.OverrideMetadata( 
                typeof(DataGridCellsPresenter),
                new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsVirtualizingPropertyChanged), new CoerceValueCallback(OnCoerceIsVirtualizingProperty)));
            VirtualizingStackPanel.VirtualizationModeProperty.OverrideMetadata(typeof(DataGridCellsPresenter), new FrameworkPropertyMetadata(VirtualizationMode.Recycling));
        } 

        ///  
        ///     Instantiates a new instance of this class. 
        /// 
        public DataGridCellsPresenter() 
        {
        }

        #endregion 

        #region Row Communication 
 
        /// 
        ///     Tells the row owner about this element. 
        /// 
        public override void OnApplyTemplate()
        {
#if BindingGroups 
            if (ItemBindingGroup == null)
            { 
                ItemBindingGroup = new BindingGroup(); 
            }
#endif 

            base.OnApplyTemplate();

            DataGridRow owningRow = DataGridRowOwner; 
            if (owningRow != null)
            { 
                owningRow.CellsPresenter = this; 
                Item = owningRow.Item;
            } 

            // At the time that a Row is prepared we can't [....] because the CellsPresenter isn't created yet.
            // Doing it here ensures that the CellsPresenter is in the visual tree.
            SyncProperties(false); 
        }
 
        ///  
        ///     Update all properties that get a value from the DataGrid
        ///  
        /// 
        ///     See comment on DataGridRow.SyncProperties
        /// 
        internal void SyncProperties(bool forcePrepareCells) 
        {
            var dataGridOwner = DataGridOwner; 
            if (dataGridOwner == null) 
            {
                return; 
            }

            DataGridHelper.TransferProperty(this, HeightProperty);
            DataGridHelper.TransferProperty(this, MinHeightProperty); 
            DataGridHelper.TransferProperty(this, VirtualizingStackPanel.IsVirtualizingProperty);
 
            // This is a convenient way to walk through all cells and force them to call CoerceValue(StyleProperty) 
            NotifyPropertyChanged(this, new DependencyPropertyChangedEventArgs(DataGrid.CellStyleProperty, null, null), DataGridNotificationTarget.Cells);
 
            // We may have missed an Add / Remove of a column from the grid (DataGridRow.OnColumnsChanged)
            // [....] the MultipleCopiesCollection count and update the Column on changed cells
            MultipleCopiesCollection cellItems = ItemsSource as MultipleCopiesCollection;
            if (cellItems != null) 
            {
                DataGridCell cell; 
                ObservableCollection columns = dataGridOwner.Columns; 
                int newColumnCount = columns.Count;
                int oldColumnCount = cellItems.Count; 
                int dirtyCount = 0;

                if (newColumnCount != oldColumnCount)
                { 
                    cellItems.SyncToCount(newColumnCount);
 
                    // Newly added or removed containers will be updated by the generator via PrepareContainer. 
                    // All others may have a different column
                    dirtyCount = Math.Min(newColumnCount, oldColumnCount); 
                }
                else if (forcePrepareCells)
                {
                    dirtyCount = newColumnCount; 
                }
 
                DataGridRow row = DataGridRowOwner; 
                bool arrangeInvalidated = false;
 
                // Prepare the cells until dirtyCount is reached. Also invalidate the cells panel's measure
                // and arrange if there is a mismatch between cell.ActualWidth and Column.Width.DisplayValue
                for (int i = 0; i < dirtyCount; i++)
                { 
                    cell = (DataGridCell)ItemContainerGenerator.ContainerFromIndex(i);
                    if (cell != null) 
                    { 
                        cell.PrepareCell(row.Item, this, row);
                        if (!arrangeInvalidated && !DoubleUtil.AreClose(cell.ActualWidth, columns[i].Width.DisplayValue)) 
                        {
                            InvalidateDataGridCellsPanelMeasureAndArrange();
                            arrangeInvalidated = true;
                        } 
                    }
                } 
 
                // Keep searching for the mismatch between cell.ActualWidth
                // and Column.Width.DisplayValue 
                if (!arrangeInvalidated)
                {
                    for (int i = dirtyCount; i < newColumnCount; i++)
                    { 
                        cell = (DataGridCell)ItemContainerGenerator.ContainerFromIndex(i);
                        if (cell != null) 
                        { 
                            if (!DoubleUtil.AreClose(cell.ActualWidth, columns[i].Width.DisplayValue))
                            { 
                                InvalidateDataGridCellsPanelMeasureAndArrange();
                                break;
                            }
                        } 
                    }
                } 
            } 
        }
 
        private static object OnCoerceHeight(DependencyObject d, object baseValue)
        {
            var cellsPresenter = d as DataGridCellsPresenter;
            return DataGridHelper.GetCoercedTransferPropertyValue( 
                cellsPresenter,
                baseValue, 
                HeightProperty, 
                cellsPresenter.DataGridOwner,
                DataGrid.RowHeightProperty); 
        }

        private static object OnCoerceMinHeight(DependencyObject d, object baseValue)
        { 
            var cellsPresenter = d as DataGridCellsPresenter;
            return DataGridHelper.GetCoercedTransferPropertyValue( 
                cellsPresenter, 
                baseValue,
                MinHeightProperty, 
                cellsPresenter.DataGridOwner,
                DataGrid.MinRowHeightProperty);
        }
 
        #endregion
 
        #region Data Item 

        ///  
        ///     The item that the row represents. This item is an entry in the list of items from the DataGrid.
        ///     From this item, cells are generated for each column in the DataGrid.
        /// 
        public object Item 
        {
            get 
            { 
                return _item;
            } 

            internal set
            {
                if (_item != value) 
                {
                    object oldItem = _item; 
                    _item = value; 
                    OnItemChanged(oldItem, _item);
                } 
            }
        }

        ///  
        ///     Called when the value of the Item property changes.
        ///  
        /// The old value of Item. 
        /// The new value of Item.
        protected virtual void OnItemChanged(object oldItem, object newItem) 
        {
            ObservableCollection columns = Columns;

            if (columns != null) 
            {
                // Either update or create a collection that will return the row's data item 
                // n number of times, where n is the number of columns. 
                MultipleCopiesCollection cellItems = ItemsSource as MultipleCopiesCollection;
                if (cellItems == null) 
                {
                    cellItems = new MultipleCopiesCollection(newItem, columns.Count);
                    ItemsSource = cellItems;
                } 
                else
                { 
                    cellItems.CopiedItem = newItem; 
                }
            } 
        }

        #endregion
 
        #region Cell Container Generation
 
        ///  
        ///     Determines if an item is its own container.
        ///  
        /// The item to test.
        /// true if the item is a DataGridCell, false otherwise.
        protected override bool IsItemItsOwnContainerOverride(object item)
        { 
            return item is DataGridCell;
        } 
 
        /// 
        ///     Method which returns the result of IsItemItsOwnContainerOverride to be used internally 
        /// 
        internal bool IsItemItsOwnContainerInternal(object item)
        {
            return IsItemItsOwnContainerOverride(item); 
        }
 
        ///  
        ///     Instantiates an instance of a container.
        ///  
        /// A new DataGridCell.
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new DataGridCell(); 
        }
 
        ///  
        ///     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)
        { 
            DataGridCell cell = (DataGridCell)element;
            DataGridRow rowOwner = DataGridRowOwner; 
 
            if (cell.RowOwner != rowOwner)
            { 
                cell.Tracker.StartTracking(ref _cellTrackingRoot);
            }

            cell.PrepareCell(item, this, rowOwner); 
        }
 
        ///  
        ///     Clears a container of references.
        ///  
        /// The container being cleared.
        /// The data item that the container represented.
        protected override void ClearContainerForItemOverride(DependencyObject element, object item)
        { 
            DataGridCell cell = (DataGridCell)element;
            DataGridRow rowOwner = DataGridRowOwner; 
 
            if (cell.RowOwner == rowOwner)
            { 
                cell.Tracker.StopTracking(ref _cellTrackingRoot);
            }

            cell.ClearCell(rowOwner); 
        }
 
        ///  
        ///     Notification from the DataGrid that the columns collection has changed.
        ///  
        /// The columns collection.
        /// The event arguments from the collection's change event.
        protected internal virtual void OnColumnsChanged(ObservableCollection columns, NotifyCollectionChangedEventArgs e)
        { 
            // Update the ItemsSource for the cells
            MultipleCopiesCollection cellItems = ItemsSource as MultipleCopiesCollection; 
            if (cellItems != null) 
            {
                cellItems.MirrorCollectionChange(e); 
            }

            // For a reset event the only thing the MultipleCopiesCollection can do is set its count to 0.
            Debug.Assert( 
                e.Action != NotifyCollectionChangedAction.Reset || columns.Count == 0,
                "A Reset event should only be fired for a Clear event from the columns collection"); 
        } 

        #endregion 

        #region Notification Propagation

        ///  
        /// Notification of Height & MinHeight changes.
        ///  
        private static void OnNotifyHeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            ((DataGridCellsPresenter)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.CellsPresenter); 
        }

        /// 
        ///     General notification for DependencyProperty changes from the grid or from columns. 
        /// 
        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.
        ///  
        internal void NotifyPropertyChanged(DependencyObject d, string propertyName, DependencyPropertyChangedEventArgs e, DataGridNotificationTarget target)
        { 
            if (DataGridHelper.ShouldNotifyCellsPresenter(target)) 
            {
                if (e.Property == DataGridColumn.WidthProperty || 
                    e.Property == DataGridColumn.DisplayIndexProperty)
                {
                    if (((DataGridColumn)d).IsVisible)
                    { 
                        InvalidateDataGridCellsPanelMeasureAndArrange();
                    } 
                } 
                else if (e.Property == DataGrid.FrozenColumnCountProperty ||
                    e.Property == DataGridColumn.VisibilityProperty || 
                    e.Property == DataGrid.CellsPanelHorizontalOffsetProperty ||
                    e.Property == DataGrid.HorizontalScrollOffsetProperty ||
                    string.Compare(propertyName, "ViewportWidth", StringComparison.Ordinal) == 0 ||
                    string.Compare(propertyName, "DelayedColumnWidthComputation", StringComparison.Ordinal) == 0) 
                {
                    InvalidateDataGridCellsPanelMeasureAndArrange(); 
                } 
                else if (string.Compare(propertyName, "RealizedColumnsBlockListForNonVirtualizedRows", StringComparison.Ordinal) == 0)
                { 
                    InvalidateDataGridCellsPanelMeasureAndArrange(/* withColumnVirtualization */ false);
                }
                else if (string.Compare(propertyName, "RealizedColumnsBlockListForVirtualizedRows", StringComparison.Ordinal) == 0)
                { 
                    InvalidateDataGridCellsPanelMeasureAndArrange(/* withColumnVirtualization */ true);
                } 
                else if (e.Property == DataGrid.RowHeightProperty || e.Property == HeightProperty) 
                {
                    DataGridHelper.TransferProperty(this, HeightProperty); 
                }
                else if (e.Property == DataGrid.MinRowHeightProperty || e.Property == MinHeightProperty)
                {
                    DataGridHelper.TransferProperty(this, MinHeightProperty); 
                }
                else if (e.Property == DataGrid.EnableColumnVirtualizationProperty) 
                { 
                    DataGridHelper.TransferProperty(this, VirtualizingStackPanel.IsVirtualizingProperty);
                } 
            }

            if (DataGridHelper.ShouldNotifyCells(target) ||
                DataGridHelper.ShouldRefreshCellContent(target)) 
            {
                ContainerTracking tracker = _cellTrackingRoot; 
                while (tracker != null) 
                {
                    tracker.Container.NotifyPropertyChanged(d, propertyName, e, target); 
                    tracker = tracker.Next;
                }
            }
        } 

        #endregion 
 
        #region GridLines
 
        // Different parts of the DataGrid draw different pieces of the GridLines.
        // Rows draw a single horizontal line on the bottom.  The DataGridDetailsPresenter is the element that handles it.

        ///  
        ///     Measure.  This is overridden so that the row can extend its size to account for a grid line on the bottom.
        ///  
        ///  
        /// 
        protected override Size MeasureOverride(Size availableSize) 
        {
            // Make space for the GridLine on the bottom.
            // Remove space from the constraint (since it implicitly includes the GridLine's thickness),
            // call the base implementation, and add the thickness back for the returned size. 
            var row = DataGridRowOwner;
            if (row == null) 
            { 
                return base.MeasureOverride(availableSize);
            } 

            var dataGrid = row.DataGridOwner;
            if (dataGrid == null)
            { 
                return base.MeasureOverride(availableSize);
            } 
 
            if (DataGridHelper.IsGridLineVisible(dataGrid, /*isHorizontal = */ true))
            { 
                double thickness = dataGrid.HorizontalGridLineThickness;
                Size desiredSize = base.MeasureOverride(DataGridHelper.SubtractFromSize(availableSize, thickness, /*height = */ true));
                desiredSize.Height += thickness;
                return desiredSize; 
            }
            else 
            { 
                return base.MeasureOverride(availableSize);
            } 
        }

        /// 
        ///     Arrange.  This is overriden so that the row can position its content to account for a grid line on the bottom. 
        /// 
        /// Arrange size 
        protected override Size ArrangeOverride(Size finalSize) 
        {
            // We don't need to adjust the Arrange position of the content.  By default it is arranged at 0,0 and we're 
            // adding a line to the bottom.  All we have to do is compress and extend the size, just like Measure.
            var row = DataGridRowOwner;
            if (row == null)
            { 
                return base.ArrangeOverride(finalSize);
            } 
 
            var dataGrid = row.DataGridOwner;
            if (dataGrid == null) 
            {
                return base.ArrangeOverride(finalSize);
            }
 
            if (DataGridHelper.IsGridLineVisible(dataGrid, /*isHorizontal = */ true))
            { 
                double thickness = dataGrid.HorizontalGridLineThickness; 
                Size returnSize = base.ArrangeOverride(DataGridHelper.SubtractFromSize(finalSize, thickness, /*height = */ true));
                returnSize.Height += thickness; 
                return returnSize;
            }
            else
            { 
                return base.ArrangeOverride(finalSize);
            } 
        } 

        ///  
        ///     OnRender.  Overriden to draw a horizontal line underneath the content.
        /// 
        /// 
        protected override void OnRender(DrawingContext drawingContext) 
        {
            base.OnRender(drawingContext); 
 
            var row = DataGridRowOwner;
            if (row == null) 
            {
                return;
            }
 
            var dataGrid = row.DataGridOwner;
            if (dataGrid == null) 
            { 
                return;
            } 

            if (DataGridHelper.IsGridLineVisible(dataGrid, /*isHorizontal = */ true))
            {
                double thickness = dataGrid.HorizontalGridLineThickness; 
                Rect rect = new Rect(new Size(RenderSize.Width, thickness));
                rect.Y = RenderSize.Height - thickness; 
 
                drawingContext.DrawRectangle(dataGrid.HorizontalGridLinesBrush, null, rect);
            } 
        }

        #endregion
 
        #region Column Virtualization
 
        ///  
        ///     Property changed callback for VirtualizingStackPanel.IsVirtualizing property
        ///  
        private static void OnIsVirtualizingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DataGridCellsPresenter cellsPresenter = (DataGridCellsPresenter)d;
            DataGridHelper.TransferProperty(cellsPresenter, VirtualizingStackPanel.IsVirtualizingProperty); 
            if (e.OldValue != cellsPresenter.GetValue(VirtualizingStackPanel.IsVirtualizingProperty))
            { 
                cellsPresenter.InvalidateDataGridCellsPanelMeasureAndArrange(); 
            }
        } 

        /// 
        ///     Coercion callback for VirtualizingStackPanel.IsVirtualizing property
        ///  
        private static object OnCoerceIsVirtualizingProperty(DependencyObject d, object baseValue)
        { 
            var cellsPresenter = d as DataGridCellsPresenter; 
            return DataGridHelper.GetCoercedTransferPropertyValue(
                cellsPresenter, 
                baseValue,
                VirtualizingStackPanel.IsVirtualizingProperty,
                cellsPresenter.DataGridOwner,
                DataGrid.EnableColumnVirtualizationProperty); 
        }
 
        ///  
        ///     Helper method which invalidate the underlying itemshost's measure and arrange
        ///  
        internal void InvalidateDataGridCellsPanelMeasureAndArrange()
        {
            if (_internalItemsHost != null)
            { 
                _internalItemsHost.InvalidateMeasure();
                _internalItemsHost.InvalidateArrange(); 
            } 
        }
 
        /// 
        ///     Helper method which invalidate the underlying itemshost's measure and arrange
        /// 
        ///  
        ///     True to invalidate only when virtualization is on.
        ///     False to invalidate only when virtualization is off. 
        ///  
        private void InvalidateDataGridCellsPanelMeasureAndArrange(bool withColumnVirtualization)
        { 
            // Invalidates measure and arrange if the flag and the virtualization
            // are either both true or both false.
            if (withColumnVirtualization == VirtualizingStackPanel.GetIsVirtualizing(this))
            { 
                InvalidateDataGridCellsPanelMeasureAndArrange();
            } 
        } 

        ///  
        ///     Workaround for not being able to access the panel instance of
        ///     itemscontrol directly
        /// 
        internal Panel InternalItemsHost 
        {
            get { return _internalItemsHost; } 
            set { _internalItemsHost = value; } 
        }
 
        /// 
        ///     Method which tries to scroll a cell for given index into the scroll view
        /// 
        ///  
        internal void ScrollCellIntoView(int index)
        { 
            DataGridCellsPanel itemsHost = InternalItemsHost as DataGridCellsPanel; 
            if (itemsHost != null)
            { 
                itemsHost.InternalBringIndexIntoView(index);
                return;
            }
        } 

        #endregion 
 
        #region Helpers
 
        /// 
        ///     The DataGrid that owns this control
        /// 
        private DataGrid DataGridOwner 
        {
            get 
            { 
                DataGridRow parent = DataGridRowOwner;
                if (parent != null) 
                {
                    return parent.DataGridOwner;
                }
 
                return null;
            } 
        } 

        ///  
        ///     The DataGridRow that owns this control.
        /// 
        internal DataGridRow DataGridRowOwner
        { 
            get { return DataGridHelper.FindParent(this); }
        } 
 
        private ObservableCollection Columns
        { 
            get
            {
                DataGridRow owningRow = DataGridRowOwner;
                DataGrid owningDataGrid = (owningRow != null) ? owningRow.DataGridOwner : null; 
                return (owningDataGrid != null) ? owningDataGrid.Columns : null;
            } 
        } 

        internal ContainerTracking CellTrackingRoot 
        {
            get { return _cellTrackingRoot; }
        }
 
        #endregion
 
        #region Data 

        private object _item; 
        private ContainerTracking _cellTrackingRoot;    // Root of a linked list of active cell containers
        private Panel _internalItemsHost;

        #endregion 
    }
} 

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

using System; 
using System.Collections.ObjectModel; 
using System.Collections.Specialized;
using System.ComponentModel; 
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media; 
using MS.Internal;
 
namespace System.Windows.Controls.Primitives 
{
    ///  
    ///     A control that will be responsible for generating cells.
    ///     This control is meant to be specified within the template of a DataGridRow.
    ///     The APIs from ItemsControl do not match up nicely with the meaning of a
    ///     row, which is why this is being factored out. 
    ///
    ///     The data item for the row is added n times to the Items collection, 
    ///     where n is the number of columns in the DataGrid. This is implemented 
    ///     using a special collection to avoid keeping multiple references to the
    ///     same object. 
    /// 
    public class DataGridCellsPresenter : ItemsControl
    {
        #region Constructors 

        ///  
        ///     Instantiates global information. 
        /// 
        static DataGridCellsPresenter() 
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(DataGridCellsPresenter), new FrameworkPropertyMetadata(typeof(DataGridCellsPresenter)));
            ItemsPanelProperty.OverrideMetadata(typeof(DataGridCellsPresenter), new FrameworkPropertyMetadata(new ItemsPanelTemplate(new FrameworkElementFactory(typeof(DataGridCellsPanel)))));
            FocusableProperty.OverrideMetadata(typeof(DataGridCellsPresenter), new FrameworkPropertyMetadata(false)); 

            HeightProperty.OverrideMetadata(typeof(DataGridCellsPresenter), new FrameworkPropertyMetadata(OnNotifyHeightPropertyChanged, OnCoerceHeight)); 
            MinHeightProperty.OverrideMetadata(typeof(DataGridCellsPresenter), new FrameworkPropertyMetadata(OnNotifyHeightPropertyChanged, OnCoerceMinHeight)); 

            VirtualizingStackPanel.IsVirtualizingProperty.OverrideMetadata( 
                typeof(DataGridCellsPresenter),
                new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsVirtualizingPropertyChanged), new CoerceValueCallback(OnCoerceIsVirtualizingProperty)));
            VirtualizingStackPanel.VirtualizationModeProperty.OverrideMetadata(typeof(DataGridCellsPresenter), new FrameworkPropertyMetadata(VirtualizationMode.Recycling));
        } 

        ///  
        ///     Instantiates a new instance of this class. 
        /// 
        public DataGridCellsPresenter() 
        {
        }

        #endregion 

        #region Row Communication 
 
        /// 
        ///     Tells the row owner about this element. 
        /// 
        public override void OnApplyTemplate()
        {
#if BindingGroups 
            if (ItemBindingGroup == null)
            { 
                ItemBindingGroup = new BindingGroup(); 
            }
#endif 

            base.OnApplyTemplate();

            DataGridRow owningRow = DataGridRowOwner; 
            if (owningRow != null)
            { 
                owningRow.CellsPresenter = this; 
                Item = owningRow.Item;
            } 

            // At the time that a Row is prepared we can't [....] because the CellsPresenter isn't created yet.
            // Doing it here ensures that the CellsPresenter is in the visual tree.
            SyncProperties(false); 
        }
 
        ///  
        ///     Update all properties that get a value from the DataGrid
        ///  
        /// 
        ///     See comment on DataGridRow.SyncProperties
        /// 
        internal void SyncProperties(bool forcePrepareCells) 
        {
            var dataGridOwner = DataGridOwner; 
            if (dataGridOwner == null) 
            {
                return; 
            }

            DataGridHelper.TransferProperty(this, HeightProperty);
            DataGridHelper.TransferProperty(this, MinHeightProperty); 
            DataGridHelper.TransferProperty(this, VirtualizingStackPanel.IsVirtualizingProperty);
 
            // This is a convenient way to walk through all cells and force them to call CoerceValue(StyleProperty) 
            NotifyPropertyChanged(this, new DependencyPropertyChangedEventArgs(DataGrid.CellStyleProperty, null, null), DataGridNotificationTarget.Cells);
 
            // We may have missed an Add / Remove of a column from the grid (DataGridRow.OnColumnsChanged)
            // [....] the MultipleCopiesCollection count and update the Column on changed cells
            MultipleCopiesCollection cellItems = ItemsSource as MultipleCopiesCollection;
            if (cellItems != null) 
            {
                DataGridCell cell; 
                ObservableCollection columns = dataGridOwner.Columns; 
                int newColumnCount = columns.Count;
                int oldColumnCount = cellItems.Count; 
                int dirtyCount = 0;

                if (newColumnCount != oldColumnCount)
                { 
                    cellItems.SyncToCount(newColumnCount);
 
                    // Newly added or removed containers will be updated by the generator via PrepareContainer. 
                    // All others may have a different column
                    dirtyCount = Math.Min(newColumnCount, oldColumnCount); 
                }
                else if (forcePrepareCells)
                {
                    dirtyCount = newColumnCount; 
                }
 
                DataGridRow row = DataGridRowOwner; 
                bool arrangeInvalidated = false;
 
                // Prepare the cells until dirtyCount is reached. Also invalidate the cells panel's measure
                // and arrange if there is a mismatch between cell.ActualWidth and Column.Width.DisplayValue
                for (int i = 0; i < dirtyCount; i++)
                { 
                    cell = (DataGridCell)ItemContainerGenerator.ContainerFromIndex(i);
                    if (cell != null) 
                    { 
                        cell.PrepareCell(row.Item, this, row);
                        if (!arrangeInvalidated && !DoubleUtil.AreClose(cell.ActualWidth, columns[i].Width.DisplayValue)) 
                        {
                            InvalidateDataGridCellsPanelMeasureAndArrange();
                            arrangeInvalidated = true;
                        } 
                    }
                } 
 
                // Keep searching for the mismatch between cell.ActualWidth
                // and Column.Width.DisplayValue 
                if (!arrangeInvalidated)
                {
                    for (int i = dirtyCount; i < newColumnCount; i++)
                    { 
                        cell = (DataGridCell)ItemContainerGenerator.ContainerFromIndex(i);
                        if (cell != null) 
                        { 
                            if (!DoubleUtil.AreClose(cell.ActualWidth, columns[i].Width.DisplayValue))
                            { 
                                InvalidateDataGridCellsPanelMeasureAndArrange();
                                break;
                            }
                        } 
                    }
                } 
            } 
        }
 
        private static object OnCoerceHeight(DependencyObject d, object baseValue)
        {
            var cellsPresenter = d as DataGridCellsPresenter;
            return DataGridHelper.GetCoercedTransferPropertyValue( 
                cellsPresenter,
                baseValue, 
                HeightProperty, 
                cellsPresenter.DataGridOwner,
                DataGrid.RowHeightProperty); 
        }

        private static object OnCoerceMinHeight(DependencyObject d, object baseValue)
        { 
            var cellsPresenter = d as DataGridCellsPresenter;
            return DataGridHelper.GetCoercedTransferPropertyValue( 
                cellsPresenter, 
                baseValue,
                MinHeightProperty, 
                cellsPresenter.DataGridOwner,
                DataGrid.MinRowHeightProperty);
        }
 
        #endregion
 
        #region Data Item 

        ///  
        ///     The item that the row represents. This item is an entry in the list of items from the DataGrid.
        ///     From this item, cells are generated for each column in the DataGrid.
        /// 
        public object Item 
        {
            get 
            { 
                return _item;
            } 

            internal set
            {
                if (_item != value) 
                {
                    object oldItem = _item; 
                    _item = value; 
                    OnItemChanged(oldItem, _item);
                } 
            }
        }

        ///  
        ///     Called when the value of the Item property changes.
        ///  
        /// The old value of Item. 
        /// The new value of Item.
        protected virtual void OnItemChanged(object oldItem, object newItem) 
        {
            ObservableCollection columns = Columns;

            if (columns != null) 
            {
                // Either update or create a collection that will return the row's data item 
                // n number of times, where n is the number of columns. 
                MultipleCopiesCollection cellItems = ItemsSource as MultipleCopiesCollection;
                if (cellItems == null) 
                {
                    cellItems = new MultipleCopiesCollection(newItem, columns.Count);
                    ItemsSource = cellItems;
                } 
                else
                { 
                    cellItems.CopiedItem = newItem; 
                }
            } 
        }

        #endregion
 
        #region Cell Container Generation
 
        ///  
        ///     Determines if an item is its own container.
        ///  
        /// The item to test.
        /// true if the item is a DataGridCell, false otherwise.
        protected override bool IsItemItsOwnContainerOverride(object item)
        { 
            return item is DataGridCell;
        } 
 
        /// 
        ///     Method which returns the result of IsItemItsOwnContainerOverride to be used internally 
        /// 
        internal bool IsItemItsOwnContainerInternal(object item)
        {
            return IsItemItsOwnContainerOverride(item); 
        }
 
        ///  
        ///     Instantiates an instance of a container.
        ///  
        /// A new DataGridCell.
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new DataGridCell(); 
        }
 
        ///  
        ///     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)
        { 
            DataGridCell cell = (DataGridCell)element;
            DataGridRow rowOwner = DataGridRowOwner; 
 
            if (cell.RowOwner != rowOwner)
            { 
                cell.Tracker.StartTracking(ref _cellTrackingRoot);
            }

            cell.PrepareCell(item, this, rowOwner); 
        }
 
        ///  
        ///     Clears a container of references.
        ///  
        /// The container being cleared.
        /// The data item that the container represented.
        protected override void ClearContainerForItemOverride(DependencyObject element, object item)
        { 
            DataGridCell cell = (DataGridCell)element;
            DataGridRow rowOwner = DataGridRowOwner; 
 
            if (cell.RowOwner == rowOwner)
            { 
                cell.Tracker.StopTracking(ref _cellTrackingRoot);
            }

            cell.ClearCell(rowOwner); 
        }
 
        ///  
        ///     Notification from the DataGrid that the columns collection has changed.
        ///  
        /// The columns collection.
        /// The event arguments from the collection's change event.
        protected internal virtual void OnColumnsChanged(ObservableCollection columns, NotifyCollectionChangedEventArgs e)
        { 
            // Update the ItemsSource for the cells
            MultipleCopiesCollection cellItems = ItemsSource as MultipleCopiesCollection; 
            if (cellItems != null) 
            {
                cellItems.MirrorCollectionChange(e); 
            }

            // For a reset event the only thing the MultipleCopiesCollection can do is set its count to 0.
            Debug.Assert( 
                e.Action != NotifyCollectionChangedAction.Reset || columns.Count == 0,
                "A Reset event should only be fired for a Clear event from the columns collection"); 
        } 

        #endregion 

        #region Notification Propagation

        ///  
        /// Notification of Height & MinHeight changes.
        ///  
        private static void OnNotifyHeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            ((DataGridCellsPresenter)d).NotifyPropertyChanged(d, e, DataGridNotificationTarget.CellsPresenter); 
        }

        /// 
        ///     General notification for DependencyProperty changes from the grid or from columns. 
        /// 
        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.
        ///  
        internal void NotifyPropertyChanged(DependencyObject d, string propertyName, DependencyPropertyChangedEventArgs e, DataGridNotificationTarget target)
        { 
            if (DataGridHelper.ShouldNotifyCellsPresenter(target)) 
            {
                if (e.Property == DataGridColumn.WidthProperty || 
                    e.Property == DataGridColumn.DisplayIndexProperty)
                {
                    if (((DataGridColumn)d).IsVisible)
                    { 
                        InvalidateDataGridCellsPanelMeasureAndArrange();
                    } 
                } 
                else if (e.Property == DataGrid.FrozenColumnCountProperty ||
                    e.Property == DataGridColumn.VisibilityProperty || 
                    e.Property == DataGrid.CellsPanelHorizontalOffsetProperty ||
                    e.Property == DataGrid.HorizontalScrollOffsetProperty ||
                    string.Compare(propertyName, "ViewportWidth", StringComparison.Ordinal) == 0 ||
                    string.Compare(propertyName, "DelayedColumnWidthComputation", StringComparison.Ordinal) == 0) 
                {
                    InvalidateDataGridCellsPanelMeasureAndArrange(); 
                } 
                else if (string.Compare(propertyName, "RealizedColumnsBlockListForNonVirtualizedRows", StringComparison.Ordinal) == 0)
                { 
                    InvalidateDataGridCellsPanelMeasureAndArrange(/* withColumnVirtualization */ false);
                }
                else if (string.Compare(propertyName, "RealizedColumnsBlockListForVirtualizedRows", StringComparison.Ordinal) == 0)
                { 
                    InvalidateDataGridCellsPanelMeasureAndArrange(/* withColumnVirtualization */ true);
                } 
                else if (e.Property == DataGrid.RowHeightProperty || e.Property == HeightProperty) 
                {
                    DataGridHelper.TransferProperty(this, HeightProperty); 
                }
                else if (e.Property == DataGrid.MinRowHeightProperty || e.Property == MinHeightProperty)
                {
                    DataGridHelper.TransferProperty(this, MinHeightProperty); 
                }
                else if (e.Property == DataGrid.EnableColumnVirtualizationProperty) 
                { 
                    DataGridHelper.TransferProperty(this, VirtualizingStackPanel.IsVirtualizingProperty);
                } 
            }

            if (DataGridHelper.ShouldNotifyCells(target) ||
                DataGridHelper.ShouldRefreshCellContent(target)) 
            {
                ContainerTracking tracker = _cellTrackingRoot; 
                while (tracker != null) 
                {
                    tracker.Container.NotifyPropertyChanged(d, propertyName, e, target); 
                    tracker = tracker.Next;
                }
            }
        } 

        #endregion 
 
        #region GridLines
 
        // Different parts of the DataGrid draw different pieces of the GridLines.
        // Rows draw a single horizontal line on the bottom.  The DataGridDetailsPresenter is the element that handles it.

        ///  
        ///     Measure.  This is overridden so that the row can extend its size to account for a grid line on the bottom.
        ///  
        ///  
        /// 
        protected override Size MeasureOverride(Size availableSize) 
        {
            // Make space for the GridLine on the bottom.
            // Remove space from the constraint (since it implicitly includes the GridLine's thickness),
            // call the base implementation, and add the thickness back for the returned size. 
            var row = DataGridRowOwner;
            if (row == null) 
            { 
                return base.MeasureOverride(availableSize);
            } 

            var dataGrid = row.DataGridOwner;
            if (dataGrid == null)
            { 
                return base.MeasureOverride(availableSize);
            } 
 
            if (DataGridHelper.IsGridLineVisible(dataGrid, /*isHorizontal = */ true))
            { 
                double thickness = dataGrid.HorizontalGridLineThickness;
                Size desiredSize = base.MeasureOverride(DataGridHelper.SubtractFromSize(availableSize, thickness, /*height = */ true));
                desiredSize.Height += thickness;
                return desiredSize; 
            }
            else 
            { 
                return base.MeasureOverride(availableSize);
            } 
        }

        /// 
        ///     Arrange.  This is overriden so that the row can position its content to account for a grid line on the bottom. 
        /// 
        /// Arrange size 
        protected override Size ArrangeOverride(Size finalSize) 
        {
            // We don't need to adjust the Arrange position of the content.  By default it is arranged at 0,0 and we're 
            // adding a line to the bottom.  All we have to do is compress and extend the size, just like Measure.
            var row = DataGridRowOwner;
            if (row == null)
            { 
                return base.ArrangeOverride(finalSize);
            } 
 
            var dataGrid = row.DataGridOwner;
            if (dataGrid == null) 
            {
                return base.ArrangeOverride(finalSize);
            }
 
            if (DataGridHelper.IsGridLineVisible(dataGrid, /*isHorizontal = */ true))
            { 
                double thickness = dataGrid.HorizontalGridLineThickness; 
                Size returnSize = base.ArrangeOverride(DataGridHelper.SubtractFromSize(finalSize, thickness, /*height = */ true));
                returnSize.Height += thickness; 
                return returnSize;
            }
            else
            { 
                return base.ArrangeOverride(finalSize);
            } 
        } 

        ///  
        ///     OnRender.  Overriden to draw a horizontal line underneath the content.
        /// 
        /// 
        protected override void OnRender(DrawingContext drawingContext) 
        {
            base.OnRender(drawingContext); 
 
            var row = DataGridRowOwner;
            if (row == null) 
            {
                return;
            }
 
            var dataGrid = row.DataGridOwner;
            if (dataGrid == null) 
            { 
                return;
            } 

            if (DataGridHelper.IsGridLineVisible(dataGrid, /*isHorizontal = */ true))
            {
                double thickness = dataGrid.HorizontalGridLineThickness; 
                Rect rect = new Rect(new Size(RenderSize.Width, thickness));
                rect.Y = RenderSize.Height - thickness; 
 
                drawingContext.DrawRectangle(dataGrid.HorizontalGridLinesBrush, null, rect);
            } 
        }

        #endregion
 
        #region Column Virtualization
 
        ///  
        ///     Property changed callback for VirtualizingStackPanel.IsVirtualizing property
        ///  
        private static void OnIsVirtualizingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DataGridCellsPresenter cellsPresenter = (DataGridCellsPresenter)d;
            DataGridHelper.TransferProperty(cellsPresenter, VirtualizingStackPanel.IsVirtualizingProperty); 
            if (e.OldValue != cellsPresenter.GetValue(VirtualizingStackPanel.IsVirtualizingProperty))
            { 
                cellsPresenter.InvalidateDataGridCellsPanelMeasureAndArrange(); 
            }
        } 

        /// 
        ///     Coercion callback for VirtualizingStackPanel.IsVirtualizing property
        ///  
        private static object OnCoerceIsVirtualizingProperty(DependencyObject d, object baseValue)
        { 
            var cellsPresenter = d as DataGridCellsPresenter; 
            return DataGridHelper.GetCoercedTransferPropertyValue(
                cellsPresenter, 
                baseValue,
                VirtualizingStackPanel.IsVirtualizingProperty,
                cellsPresenter.DataGridOwner,
                DataGrid.EnableColumnVirtualizationProperty); 
        }
 
        ///  
        ///     Helper method which invalidate the underlying itemshost's measure and arrange
        ///  
        internal void InvalidateDataGridCellsPanelMeasureAndArrange()
        {
            if (_internalItemsHost != null)
            { 
                _internalItemsHost.InvalidateMeasure();
                _internalItemsHost.InvalidateArrange(); 
            } 
        }
 
        /// 
        ///     Helper method which invalidate the underlying itemshost's measure and arrange
        /// 
        ///  
        ///     True to invalidate only when virtualization is on.
        ///     False to invalidate only when virtualization is off. 
        ///  
        private void InvalidateDataGridCellsPanelMeasureAndArrange(bool withColumnVirtualization)
        { 
            // Invalidates measure and arrange if the flag and the virtualization
            // are either both true or both false.
            if (withColumnVirtualization == VirtualizingStackPanel.GetIsVirtualizing(this))
            { 
                InvalidateDataGridCellsPanelMeasureAndArrange();
            } 
        } 

        ///  
        ///     Workaround for not being able to access the panel instance of
        ///     itemscontrol directly
        /// 
        internal Panel InternalItemsHost 
        {
            get { return _internalItemsHost; } 
            set { _internalItemsHost = value; } 
        }
 
        /// 
        ///     Method which tries to scroll a cell for given index into the scroll view
        /// 
        ///  
        internal void ScrollCellIntoView(int index)
        { 
            DataGridCellsPanel itemsHost = InternalItemsHost as DataGridCellsPanel; 
            if (itemsHost != null)
            { 
                itemsHost.InternalBringIndexIntoView(index);
                return;
            }
        } 

        #endregion 
 
        #region Helpers
 
        /// 
        ///     The DataGrid that owns this control
        /// 
        private DataGrid DataGridOwner 
        {
            get 
            { 
                DataGridRow parent = DataGridRowOwner;
                if (parent != null) 
                {
                    return parent.DataGridOwner;
                }
 
                return null;
            } 
        } 

        ///  
        ///     The DataGridRow that owns this control.
        /// 
        internal DataGridRow DataGridRowOwner
        { 
            get { return DataGridHelper.FindParent(this); }
        } 
 
        private ObservableCollection Columns
        { 
            get
            {
                DataGridRow owningRow = DataGridRowOwner;
                DataGrid owningDataGrid = (owningRow != null) ? owningRow.DataGridOwner : null; 
                return (owningDataGrid != null) ? owningDataGrid.Columns : null;
            } 
        } 

        internal ContainerTracking CellTrackingRoot 
        {
            get { return _cellTrackingRoot; }
        }
 
        #endregion
 
        #region Data 

        private object _item; 
        private ContainerTracking _cellTrackingRoot;    // Root of a linked list of active cell containers
        private Panel _internalItemsHost;

        #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