GridViewRowPresenter.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / Controls / GridViewRowPresenter.cs / 1 / GridViewRowPresenter.cs

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

 
using System.Collections.Generic;       // List 
using System.Collections.Specialized;   // NotifyCollectionChangedAction
using System.ComponentModel;            // PropertyChangedEventArgs 
using System.Diagnostics;
using System.Windows.Controls.Primitives;   // GridViewRowPresenterBase
using System.Windows.Data;              // BindingBase
 
using MS.Internal;                      // DoubleUtil
 
 
namespace System.Windows.Controls
{ 
    /// 
    ///     An GridViewRowPresenter marks the site (in a style) of the panel that controls
    ///     layout of groups or items.
    ///  
    public class GridViewRowPresenter : GridViewRowPresenterBase
    { 
        //------------------------------------------------------------------- 
        //
        //  Public Methods 
        //
        //-------------------------------------------------------------------

        #region Public Methods 

        ///  
        ///     Returns a string representation of this object. 
        /// 
        ///  
        public override string ToString()
        {
            return SR.Get(SRID.ToStringFormatString_GridViewRowPresenter,
                this.GetType(), 
                (Content != null) ? Content.ToString() : String.Empty,
                (Columns != null) ? Columns.Count : 0); 
        } 

        #endregion 

        //--------------------------------------------------------------------
        //
        //  Public Properties 
        //
        //------------------------------------------------------------------- 
 
        #region Public Properties
 
        /// 
        ///     The DependencyProperty for the Content property.
        ///     Flags:              None
        ///     Default Value:      null 
        /// 
        // Any change in Content properties affectes layout measurement since 
        // a new template may be used. On measurement, 
        // ApplyTemplate will be invoked leading to possible application
        // of a new template. 
        public static readonly DependencyProperty ContentProperty =
                ContentControl.ContentProperty.AddOwner(
                        typeof(GridViewRowPresenter),
                        new FrameworkPropertyMetadata( 
                            (object)null,
                            FrameworkPropertyMetadataOptions.AffectsMeasure, 
                            new PropertyChangedCallback(OnContentChanged))); 

        ///  
        ///     Content is the data used to generate the child elements of this control.
        /// 
        public object Content
        { 
            get { return GetValue(GridViewRowPresenter.ContentProperty); }
            set { SetValue(GridViewRowPresenter.ContentProperty, value); } 
        } 

        ///  
        ///     Called when ContentProperty is invalidated on "d."
        /// 
        private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            //
            // If the old and new value have the same type then we can save a lot of perf by 
            // keeping the existing ContentPresenters 
            //
 
            Type oldType = (e.OldValue != null) ? e.OldValue.GetType() : null;
            Type newType = (e.NewValue != null) ? e.NewValue.GetType() : null;

            if (oldType != newType) 
            {
                ((GridViewRowPresenter)d).NeedUpdateVisualTree = true; 
            } 
            else
            { 
                ((GridViewRowPresenter)d).UpdateCells();
            }
        }
 
        #endregion
 
        //-------------------------------------------------------------------- 
        //
        // Protected Methods 
        //
        //--------------------------------------------------------------------

        #region Protected Methods 

        ///  
        /// Override of . 
        /// 
        /// Constraint size is an "upper limit" that the return value should not exceed. 
        /// The GridViewRowPresenter's desired size.
        protected override Size MeasureOverride(Size constraint)
        {
            if (Columns == null) { return new Size(); } 

            UIElementCollection children = InternalChildren; 
            double maxHeight = 0.0;           // Max height of children. 
            double accumulatedWidth = 0.0;    // Total width consumed by children.
            double constraintHeight = constraint.Height; 
            bool desiredWidthListEnsured = false;

            foreach (GridViewColumn column in Columns)
            { 
                UIElement child = children[column.ActualIndex];
                if (child == null) { continue; } 
 
                double childConstraintWidth = Math.Max(0.0, constraint.Width - accumulatedWidth);
 
                if (column.State == ColumnMeasureState.Init
                    || column.State == ColumnMeasureState.Headered)
                {
                    if (!desiredWidthListEnsured) 
                    {
                        EnsureDesiredWidthList(); 
                        LayoutUpdated += new EventHandler(OnLayoutUpdated); 
                        desiredWidthListEnsured = true;
                    } 

                    // Measure child.
                    child.Measure(new Size(childConstraintWidth, constraintHeight));
 
                    // As long as this is the first round of measure that has data participate
                    // the width should be ensured 
                    // only element on current page paticipates in calculating the shared width 
                    if (IsOnCurrentPage)
                    { 
                        column.EnsureWidth(child.DesiredSize.Width);
                    }

                    DesiredWidthList[column.ActualIndex] = column.DesiredWidth; 

                    accumulatedWidth += column.DesiredWidth; 
                } 
                else if (column.State == ColumnMeasureState.Data)
                { 
                    childConstraintWidth = Math.Min(childConstraintWidth, column.DesiredWidth);

                    child.Measure(new Size(childConstraintWidth, constraintHeight));
 
                    accumulatedWidth += column.DesiredWidth;
                } 
                else // ColumnMeasureState.SpecificWidth 
                {
                    childConstraintWidth = Math.Min(childConstraintWidth, column.Width); 

                    child.Measure(new Size(childConstraintWidth, constraintHeight));

                    accumulatedWidth += column.Width; 
                }
 
                maxHeight = Math.Max(maxHeight, child.DesiredSize.Height); 
            }
 
            // Reset this flag so that we will re-caculate it on every measure.
            _isOnCurrentPageValid = false;

            // reserve space for dummy header next to the last column 
            accumulatedWidth += c_PaddingHeaderMinWidth;
 
            return (new Size(accumulatedWidth, maxHeight)); 
        }
 
        /// 
        /// GridViewRowPresenter computes the position of its children inside each child's Margin and calls Arrange
        /// on each child.
        ///  
        /// Size the GridViewRowPresenter will assume.
        protected override Size ArrangeOverride(Size arrangeSize) 
        { 
            GridViewColumnCollection columns = Columns;
            if (columns == null) { return arrangeSize; } 

            UIElementCollection children = InternalChildren;

            double accumulatedWidth = 0.0; 
            double remainingWidth = arrangeSize.Width;
 
            foreach (GridViewColumn column in columns) 
            {
                UIElement child = children[column.ActualIndex]; 
                if (child == null) { continue; }

                // has a given value or 'auto'
                double childArrangeWidth = Math.Min(remainingWidth, ((column.State == ColumnMeasureState.SpecificWidth) ? column.Width : column.DesiredWidth)); 

                child.Arrange(new Rect(accumulatedWidth, 0, childArrangeWidth, arrangeSize.Height)); 
 
                remainingWidth -= childArrangeWidth;
                accumulatedWidth += childArrangeWidth; 
            }

            return arrangeSize;
        } 

        #endregion Protected Methods 
 
        //-------------------------------------------------------------------
        // 
        // Internal Methods / Properties
        //
        //--------------------------------------------------------------------
 
        #region Internal Methods / Properties
 
        ///  
        /// Called when the Template's tree has been generated
        ///  
        internal override void OnPreApplyTemplate()
        {
            // +-- GridViewRowPresenter ------------------------------------+
            // |                                                            | 
            // |  +- CtPstr1 ---+   +- CtPstr2 ---+   +- CtPstr3 ---+       |
            // |  |             |   |             |   |             |  ...  | 
            // |  +-------------+   +-------------+   +-------------+       | 
            // +-----------------------------------------------------------+
 
            base.OnPreApplyTemplate();

            if (NeedUpdateVisualTree)
            { 
                InternalChildren.Clear();
 
                // build the whole collection from draft. 
                GridViewColumnCollection columns = Columns;
                if (columns != null) 
                {
                    foreach (GridViewColumn column in columns.ColumnCollection)
                    {
                        InternalChildren.AddInternal(CreateCell(column)); 
                    }
                } 
 
                NeedUpdateVisualTree = false;
            } 

            // invalidate viewPort cache
            _viewPortValid = false;
        } 

        ///  
        /// Handler of column's PropertyChanged event. Update correspondent property 
        /// if change is of Width / CellTemplate / CellTemplateSelector.
        ///  
        internal override void OnColumnPropertyChanged(GridViewColumn column, string propertyName)
        {
            Debug.Assert(column != null);
            int index; 

            // ActualWidth change is a noise to RowPresenter, so filter it out. 
            // Note-on-perf: ActualWidth property change of will fire N x M times 
            // on every start up. (N: number of column with Width set to 'auto',
            // M: number of visible items) 
            if (GridViewColumn.c_ActualWidthName.Equals(propertyName))
            {
                return;
            } 

            // Width is the #1 property that will be changed frequently. The others 
            // (DisplayMemberBinding/CellTemplate/Selector) are not. 

            if (((index = column.ActualIndex) >= 0) && (index < InternalChildren.Count)) 
            {
                if (GridViewColumn.WidthProperty.Name.Equals(propertyName))
                {
                    InvalidateMeasure(); 
                }
 
                // Priority: DisplayMemberBinding > CellTemplate > CellTemplateSelector 
                else if (GridViewColumn.c_DisplayMemberBindingName.Equals(propertyName))
                { 
                    FrameworkElement cell = InternalChildren[index] as FrameworkElement;
                    if (cell != null)
                    {
                        BindingBase binding = column.DisplayMemberBinding; 
                        if (binding != null && cell is TextBlock)
                        { 
                            cell.SetBinding(TextBlock.TextProperty, binding); 
                        }
                        else 
                        {
                            RenewCell(index, column);
                        }
                    } 
                }
                else 
                { 
                    ContentPresenter cp = InternalChildren[index] as ContentPresenter;
                    if (cp != null) 
                    {
                        if (GridViewColumn.CellTemplateProperty.Name.Equals(propertyName))
                        {
                            DataTemplate dt; 
                            if ((dt = column.CellTemplate) == null)
                            { 
                                cp.ClearValue(ContentControl.ContentTemplateProperty); 
                            }
                            else 
                            {
                                cp.ContentTemplate = dt;
                            }
                        } 

                        else if (GridViewColumn.CellTemplateSelectorProperty.Name.Equals(propertyName)) 
                        { 
                            DataTemplateSelector dts;
                            if ((dts = column.CellTemplateSelector) == null) 
                            {
                                cp.ClearValue(ContentControl.ContentTemplateSelectorProperty);
                            }
                            else 
                            {
                                cp.ContentTemplateSelector = dts; 
                            } 
                        }
                    } 

                }
            }
        } 

        ///  
        /// process GridViewColumnCollection.CollectionChanged event. 
        /// 
        internal override void OnColumnCollectionChanged(GridViewColumnCollectionChangedEventArgs e) 
        {
            base.OnColumnCollectionChanged(e);

            if (e.Action == NotifyCollectionChangedAction.Move) 
            {
                InvalidateArrange(); 
            } 
            else
            { 
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        // New child will always be appended to the very last, no matter it 
                        // is actually add via 'Insert' or just 'Add'.
                        InternalChildren.AddInternal(CreateCell((GridViewColumn)(e.NewItems[0]))); 
                        break; 

                    case NotifyCollectionChangedAction.Remove: 
                        InternalChildren.RemoveAt(e.ActualIndex);
                        break;

                    case NotifyCollectionChangedAction.Replace: 
                        InternalChildren.RemoveAt(e.ActualIndex);
                        InternalChildren.AddInternal(CreateCell((GridViewColumn)(e.NewItems[0]))); 
                        break; 

                    case NotifyCollectionChangedAction.Reset: 
                        InternalChildren.Clear();
                        break;

                    default: 
                        break;
                } 
 
                InvalidateMeasure();
            } 
        }

        // Used in UIAutomation
        // Return the actual cells array (If user reorder column, the cell in InternalChildren isn't in the correct order) 
        internal List ActualCells
        { 
            get 
            {
                List list = new List(); 
                if (Columns != null)
                {
                    GridViewColumnCollection columns = Columns;
                    UIElementCollection children = InternalChildren; 
                    List indexList = columns.IndexList;
 
                    for (int i = 0, count = columns.Count; i < count; ++i) 
                    {
                        UIElement cell = children[indexList[i]]; 
                        if (cell != null)
                        {
                            list.Add(cell);
                        } 
                    }
                } 
 
                return list;
            } 
        }

        #endregion Internal Methods / Properties
 
        //-------------------------------------------------------------------
        // 
        // Private Methods 
        //
        //------------------------------------------------------------------- 

        #region Private Methods

        private void FindViewPort() 
        {
            // assume GridViewRowPresenter is in Item's template 
            _viewItem = this.TemplatedParent as FrameworkElement; 

            if (_viewItem != null) 
            {
                ItemsControl itemsControl = ItemsControl.ItemsControlFromItemContainer(_viewItem) as ItemsControl;

                if (itemsControl != null) 
                {
                    ScrollViewer scrollViewer = itemsControl.ScrollHost as ScrollViewer; 
                    if (scrollViewer != null) 
                    {
                        // check if Virtualizing Panel do works 
                        if (itemsControl.ItemsHost is VirtualizingPanel &&
                            scrollViewer.CanContentScroll)
                        {
                            // find the 'PART_ScrollContentPresenter' in GridViewScrollViewer 
                            _viewPort = scrollViewer.GetTemplateChild(ScrollViewer.ScrollContentPresenterTemplateName) as FrameworkElement;
 
                            // in case GridViewScrollViewer is re-styled, say, cannot find PART_ScrollContentPresenter 
                            if (_viewPort == null)
                            { 
                                _viewPort = scrollViewer;
                            }
                        }
                    } 
                }
            } 
        } 

        private bool CheckVisibleOnCurrentPage() 
        {
            if (!_viewPortValid)
            {
                FindViewPort(); 
            }
 
            bool result = true; 

            if (_viewItem != null && _viewPort != null) 
            {
                Rect viewPortBounds = new Rect(new Point(), _viewPort.RenderSize);
                Rect itemBounds = new Rect(new Point(), _viewItem.RenderSize);
                itemBounds = _viewItem.TransformToAncestor(_viewPort).TransformBounds(itemBounds); 

                // check if item bounds falls in view port bounds (in height) 
                result = CheckContains(viewPortBounds, itemBounds); 
            }
 
            return result;
        }

        private bool CheckContains(Rect container, Rect element) 
        {
            // Check if ANY part of the element reside in container 
            // return true if and only if (either case) 
            //
            // +-------------------------------------------+ 
            // +  #================================#       +
            // +--#--------------------------------#-------+
            //    #                                #
            //    #                                # 
            // +--#--------------------------------#-------+
            // +  #                                #       + 
            // +--#--------------------------------#-------+ 
            //    #                                #
            //    #                                # 
            // +--#--------------------------------#-------+
            // +  #================================#       +
            // +-------------------------------------------+
 
            // The tolerance here is to make sure at least 2 pixels are inside container
            const double tolerance = 2.0; 
 
            return ((CheckIsPointBetween(container, element.Top) && CheckIsPointBetween(container, element.Bottom)) ||
                    CheckIsPointBetween(element, container.Top + tolerance) || 
                    CheckIsPointBetween(element, container.Bottom - tolerance));
        }

        private bool CheckIsPointBetween(Rect rect, double pointY) 
        {
            // return rect.Top <= pointY <= rect.Bottom 
            return (DoubleUtil.LessThanOrClose(rect.Top, pointY) && 
                    DoubleUtil.LessThanOrClose(pointY, rect.Bottom));
        } 

        private void OnLayoutUpdated(object sender, EventArgs e)
        {
            bool desiredWidthChanged = false; // whether the shared minimum width has been changed since last layout 

            foreach (GridViewColumn column in Columns) 
            { 
                if ((column.State != ColumnMeasureState.SpecificWidth))
                { 
                    column.State = ColumnMeasureState.Data;

                    if (DesiredWidthList == null || column.ActualIndex >= DesiredWidthList.Count)
                    { 
                        // How can this happen?
                        // Between the last measure was called and this update is called, there can be a 
                        // change done to the ColumnCollection and result in DesiredWidthList out of [....] 
                        // with the columnn collection. What can we do is end this call asap and the next
                        // measure will fix it. 
                        desiredWidthChanged = true;
                        break;
                    }
 
                    if (!DoubleUtil.AreClose(column.DesiredWidth, DesiredWidthList[column.ActualIndex]))
                    { 
                        // Update the record because collection operation latter on might 
                        // need to verified this list again, e.g. insert an 'auto'
                        // column, so that we won't trigger unnecessary update due to 
                        // inconsistency of this column.
                        DesiredWidthList[column.ActualIndex] = column.DesiredWidth;

                        desiredWidthChanged = true; 
                    }
                } 
            } 

            if (desiredWidthChanged) 
            {
                InvalidateMeasure();
            }
 
            LayoutUpdated -= new EventHandler(OnLayoutUpdated);
        } 
 
        private FrameworkElement CreateCell(GridViewColumn column)
        { 
            Debug.Assert(column != null, "column shouldn't be null");

            FrameworkElement cell;
            BindingBase binding; 

            // Priority: DisplayMemberBinding > CellTemplate > CellTemplateSelector 
 
            if ((binding = column.DisplayMemberBinding) != null)
            { 
                cell = new TextBlock();

                // Needed this. Otherwise can't size to content at startup time.
                // The reason is cell.Text is empty after the first round of measure. 
                cell.DataContext = Content;
 
                cell.SetBinding(TextBlock.TextProperty, binding); 
            }
            else 
            {
                ContentPresenter cp = new ContentPresenter();
                cp.Content = Content;
 
                DataTemplate dt;
                DataTemplateSelector dts; 
                if ((dt = column.CellTemplate) != null) 
                {
                    cp.ContentTemplate = dt; 
                }
                if ((dts = column.CellTemplateSelector) != null)
                {
                    cp.ContentTemplateSelector = dts; 
                }
 
                cell = cp; 
            }
 
            // copy alignment properties from ListViewItem
            // for perf reason, not use binding here
            ContentControl parent;
            if ((parent = TemplatedParent as ContentControl) != null) 
            {
                cell.VerticalAlignment = parent.VerticalContentAlignment; 
                cell.HorizontalAlignment = parent.HorizontalContentAlignment; 
            }
 
            cell.Margin = _defalutCellMargin;

            return cell;
        } 

        private void RenewCell(int index, GridViewColumn column) 
        { 
            InternalChildren.RemoveAt(index);
            InternalChildren.Insert(index, CreateCell(column)); 
        }


        ///  
        /// Updates all cells to the latest Content.
        ///  
        private void UpdateCells() 
        {
            ContentPresenter cellAsCP; 
            FrameworkElement cell;
            UIElementCollection children = InternalChildren;
            ContentControl parent = TemplatedParent as ContentControl;
 
            for (int i = 0; i < children.Count; i++)
            { 
                cell = (FrameworkElement)children[i]; 

                if ((cellAsCP = cell as ContentPresenter) != null) 
                {
                    cellAsCP.Content = Content;
                }
                else 
                {
                    Debug.Assert(cell is TextBlock, "cells are either TextBlocks or ContentPresenters"); 
                    cell.DataContext = Content; 
                }
 
                if (parent != null)
                {
                    cell.VerticalAlignment = parent.VerticalContentAlignment;
                    cell.HorizontalAlignment = parent.HorizontalContentAlignment; 
                }
            } 
        } 

 
        #endregion

        //--------------------------------------------------------------------
        // 
        // Private Properties / Fields
        // 
        //------------------------------------------------------------------- 

        #region Private Properties / Fields 

        // if RowPresenter is not 'real' visible, it should not participating in measuring column width
        // NOTE: IsVisible is force-inheriting parent's value, that's why we pick IsVisible instead of Visibility
        //       e.g. if RowPresenter's parent is hidden/collapsed (e.g. in ListTreeView), 
        //            then RowPresenter.Visiblity = Visible, but RowPresenter.IsVisible = false
        private bool IsOnCurrentPage 
        { 
            get
            { 
                if (!_isOnCurrentPageValid)
                {
                    _isOnCurrentPage = IsVisible && CheckVisibleOnCurrentPage();
                    _isOnCurrentPageValid = true; 
                }
 
                return _isOnCurrentPage; 
            }
        } 

        private FrameworkElement _viewPort;
        private FrameworkElement _viewItem;
        private bool _viewPortValid = false; 
        private bool _isOnCurrentPage = false;
        private bool _isOnCurrentPageValid = false; 
 
        private static readonly Thickness _defalutCellMargin = new Thickness(6, 0, 6, 0);
 
        #endregion Private Properties / Fields
    }
}

// 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.Collections.Generic;       // List 
using System.Collections.Specialized;   // NotifyCollectionChangedAction
using System.ComponentModel;            // PropertyChangedEventArgs 
using System.Diagnostics;
using System.Windows.Controls.Primitives;   // GridViewRowPresenterBase
using System.Windows.Data;              // BindingBase
 
using MS.Internal;                      // DoubleUtil
 
 
namespace System.Windows.Controls
{ 
    /// 
    ///     An GridViewRowPresenter marks the site (in a style) of the panel that controls
    ///     layout of groups or items.
    ///  
    public class GridViewRowPresenter : GridViewRowPresenterBase
    { 
        //------------------------------------------------------------------- 
        //
        //  Public Methods 
        //
        //-------------------------------------------------------------------

        #region Public Methods 

        ///  
        ///     Returns a string representation of this object. 
        /// 
        ///  
        public override string ToString()
        {
            return SR.Get(SRID.ToStringFormatString_GridViewRowPresenter,
                this.GetType(), 
                (Content != null) ? Content.ToString() : String.Empty,
                (Columns != null) ? Columns.Count : 0); 
        } 

        #endregion 

        //--------------------------------------------------------------------
        //
        //  Public Properties 
        //
        //------------------------------------------------------------------- 
 
        #region Public Properties
 
        /// 
        ///     The DependencyProperty for the Content property.
        ///     Flags:              None
        ///     Default Value:      null 
        /// 
        // Any change in Content properties affectes layout measurement since 
        // a new template may be used. On measurement, 
        // ApplyTemplate will be invoked leading to possible application
        // of a new template. 
        public static readonly DependencyProperty ContentProperty =
                ContentControl.ContentProperty.AddOwner(
                        typeof(GridViewRowPresenter),
                        new FrameworkPropertyMetadata( 
                            (object)null,
                            FrameworkPropertyMetadataOptions.AffectsMeasure, 
                            new PropertyChangedCallback(OnContentChanged))); 

        ///  
        ///     Content is the data used to generate the child elements of this control.
        /// 
        public object Content
        { 
            get { return GetValue(GridViewRowPresenter.ContentProperty); }
            set { SetValue(GridViewRowPresenter.ContentProperty, value); } 
        } 

        ///  
        ///     Called when ContentProperty is invalidated on "d."
        /// 
        private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            //
            // If the old and new value have the same type then we can save a lot of perf by 
            // keeping the existing ContentPresenters 
            //
 
            Type oldType = (e.OldValue != null) ? e.OldValue.GetType() : null;
            Type newType = (e.NewValue != null) ? e.NewValue.GetType() : null;

            if (oldType != newType) 
            {
                ((GridViewRowPresenter)d).NeedUpdateVisualTree = true; 
            } 
            else
            { 
                ((GridViewRowPresenter)d).UpdateCells();
            }
        }
 
        #endregion
 
        //-------------------------------------------------------------------- 
        //
        // Protected Methods 
        //
        //--------------------------------------------------------------------

        #region Protected Methods 

        ///  
        /// Override of . 
        /// 
        /// Constraint size is an "upper limit" that the return value should not exceed. 
        /// The GridViewRowPresenter's desired size.
        protected override Size MeasureOverride(Size constraint)
        {
            if (Columns == null) { return new Size(); } 

            UIElementCollection children = InternalChildren; 
            double maxHeight = 0.0;           // Max height of children. 
            double accumulatedWidth = 0.0;    // Total width consumed by children.
            double constraintHeight = constraint.Height; 
            bool desiredWidthListEnsured = false;

            foreach (GridViewColumn column in Columns)
            { 
                UIElement child = children[column.ActualIndex];
                if (child == null) { continue; } 
 
                double childConstraintWidth = Math.Max(0.0, constraint.Width - accumulatedWidth);
 
                if (column.State == ColumnMeasureState.Init
                    || column.State == ColumnMeasureState.Headered)
                {
                    if (!desiredWidthListEnsured) 
                    {
                        EnsureDesiredWidthList(); 
                        LayoutUpdated += new EventHandler(OnLayoutUpdated); 
                        desiredWidthListEnsured = true;
                    } 

                    // Measure child.
                    child.Measure(new Size(childConstraintWidth, constraintHeight));
 
                    // As long as this is the first round of measure that has data participate
                    // the width should be ensured 
                    // only element on current page paticipates in calculating the shared width 
                    if (IsOnCurrentPage)
                    { 
                        column.EnsureWidth(child.DesiredSize.Width);
                    }

                    DesiredWidthList[column.ActualIndex] = column.DesiredWidth; 

                    accumulatedWidth += column.DesiredWidth; 
                } 
                else if (column.State == ColumnMeasureState.Data)
                { 
                    childConstraintWidth = Math.Min(childConstraintWidth, column.DesiredWidth);

                    child.Measure(new Size(childConstraintWidth, constraintHeight));
 
                    accumulatedWidth += column.DesiredWidth;
                } 
                else // ColumnMeasureState.SpecificWidth 
                {
                    childConstraintWidth = Math.Min(childConstraintWidth, column.Width); 

                    child.Measure(new Size(childConstraintWidth, constraintHeight));

                    accumulatedWidth += column.Width; 
                }
 
                maxHeight = Math.Max(maxHeight, child.DesiredSize.Height); 
            }
 
            // Reset this flag so that we will re-caculate it on every measure.
            _isOnCurrentPageValid = false;

            // reserve space for dummy header next to the last column 
            accumulatedWidth += c_PaddingHeaderMinWidth;
 
            return (new Size(accumulatedWidth, maxHeight)); 
        }
 
        /// 
        /// GridViewRowPresenter computes the position of its children inside each child's Margin and calls Arrange
        /// on each child.
        ///  
        /// Size the GridViewRowPresenter will assume.
        protected override Size ArrangeOverride(Size arrangeSize) 
        { 
            GridViewColumnCollection columns = Columns;
            if (columns == null) { return arrangeSize; } 

            UIElementCollection children = InternalChildren;

            double accumulatedWidth = 0.0; 
            double remainingWidth = arrangeSize.Width;
 
            foreach (GridViewColumn column in columns) 
            {
                UIElement child = children[column.ActualIndex]; 
                if (child == null) { continue; }

                // has a given value or 'auto'
                double childArrangeWidth = Math.Min(remainingWidth, ((column.State == ColumnMeasureState.SpecificWidth) ? column.Width : column.DesiredWidth)); 

                child.Arrange(new Rect(accumulatedWidth, 0, childArrangeWidth, arrangeSize.Height)); 
 
                remainingWidth -= childArrangeWidth;
                accumulatedWidth += childArrangeWidth; 
            }

            return arrangeSize;
        } 

        #endregion Protected Methods 
 
        //-------------------------------------------------------------------
        // 
        // Internal Methods / Properties
        //
        //--------------------------------------------------------------------
 
        #region Internal Methods / Properties
 
        ///  
        /// Called when the Template's tree has been generated
        ///  
        internal override void OnPreApplyTemplate()
        {
            // +-- GridViewRowPresenter ------------------------------------+
            // |                                                            | 
            // |  +- CtPstr1 ---+   +- CtPstr2 ---+   +- CtPstr3 ---+       |
            // |  |             |   |             |   |             |  ...  | 
            // |  +-------------+   +-------------+   +-------------+       | 
            // +-----------------------------------------------------------+
 
            base.OnPreApplyTemplate();

            if (NeedUpdateVisualTree)
            { 
                InternalChildren.Clear();
 
                // build the whole collection from draft. 
                GridViewColumnCollection columns = Columns;
                if (columns != null) 
                {
                    foreach (GridViewColumn column in columns.ColumnCollection)
                    {
                        InternalChildren.AddInternal(CreateCell(column)); 
                    }
                } 
 
                NeedUpdateVisualTree = false;
            } 

            // invalidate viewPort cache
            _viewPortValid = false;
        } 

        ///  
        /// Handler of column's PropertyChanged event. Update correspondent property 
        /// if change is of Width / CellTemplate / CellTemplateSelector.
        ///  
        internal override void OnColumnPropertyChanged(GridViewColumn column, string propertyName)
        {
            Debug.Assert(column != null);
            int index; 

            // ActualWidth change is a noise to RowPresenter, so filter it out. 
            // Note-on-perf: ActualWidth property change of will fire N x M times 
            // on every start up. (N: number of column with Width set to 'auto',
            // M: number of visible items) 
            if (GridViewColumn.c_ActualWidthName.Equals(propertyName))
            {
                return;
            } 

            // Width is the #1 property that will be changed frequently. The others 
            // (DisplayMemberBinding/CellTemplate/Selector) are not. 

            if (((index = column.ActualIndex) >= 0) && (index < InternalChildren.Count)) 
            {
                if (GridViewColumn.WidthProperty.Name.Equals(propertyName))
                {
                    InvalidateMeasure(); 
                }
 
                // Priority: DisplayMemberBinding > CellTemplate > CellTemplateSelector 
                else if (GridViewColumn.c_DisplayMemberBindingName.Equals(propertyName))
                { 
                    FrameworkElement cell = InternalChildren[index] as FrameworkElement;
                    if (cell != null)
                    {
                        BindingBase binding = column.DisplayMemberBinding; 
                        if (binding != null && cell is TextBlock)
                        { 
                            cell.SetBinding(TextBlock.TextProperty, binding); 
                        }
                        else 
                        {
                            RenewCell(index, column);
                        }
                    } 
                }
                else 
                { 
                    ContentPresenter cp = InternalChildren[index] as ContentPresenter;
                    if (cp != null) 
                    {
                        if (GridViewColumn.CellTemplateProperty.Name.Equals(propertyName))
                        {
                            DataTemplate dt; 
                            if ((dt = column.CellTemplate) == null)
                            { 
                                cp.ClearValue(ContentControl.ContentTemplateProperty); 
                            }
                            else 
                            {
                                cp.ContentTemplate = dt;
                            }
                        } 

                        else if (GridViewColumn.CellTemplateSelectorProperty.Name.Equals(propertyName)) 
                        { 
                            DataTemplateSelector dts;
                            if ((dts = column.CellTemplateSelector) == null) 
                            {
                                cp.ClearValue(ContentControl.ContentTemplateSelectorProperty);
                            }
                            else 
                            {
                                cp.ContentTemplateSelector = dts; 
                            } 
                        }
                    } 

                }
            }
        } 

        ///  
        /// process GridViewColumnCollection.CollectionChanged event. 
        /// 
        internal override void OnColumnCollectionChanged(GridViewColumnCollectionChangedEventArgs e) 
        {
            base.OnColumnCollectionChanged(e);

            if (e.Action == NotifyCollectionChangedAction.Move) 
            {
                InvalidateArrange(); 
            } 
            else
            { 
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        // New child will always be appended to the very last, no matter it 
                        // is actually add via 'Insert' or just 'Add'.
                        InternalChildren.AddInternal(CreateCell((GridViewColumn)(e.NewItems[0]))); 
                        break; 

                    case NotifyCollectionChangedAction.Remove: 
                        InternalChildren.RemoveAt(e.ActualIndex);
                        break;

                    case NotifyCollectionChangedAction.Replace: 
                        InternalChildren.RemoveAt(e.ActualIndex);
                        InternalChildren.AddInternal(CreateCell((GridViewColumn)(e.NewItems[0]))); 
                        break; 

                    case NotifyCollectionChangedAction.Reset: 
                        InternalChildren.Clear();
                        break;

                    default: 
                        break;
                } 
 
                InvalidateMeasure();
            } 
        }

        // Used in UIAutomation
        // Return the actual cells array (If user reorder column, the cell in InternalChildren isn't in the correct order) 
        internal List ActualCells
        { 
            get 
            {
                List list = new List(); 
                if (Columns != null)
                {
                    GridViewColumnCollection columns = Columns;
                    UIElementCollection children = InternalChildren; 
                    List indexList = columns.IndexList;
 
                    for (int i = 0, count = columns.Count; i < count; ++i) 
                    {
                        UIElement cell = children[indexList[i]]; 
                        if (cell != null)
                        {
                            list.Add(cell);
                        } 
                    }
                } 
 
                return list;
            } 
        }

        #endregion Internal Methods / Properties
 
        //-------------------------------------------------------------------
        // 
        // Private Methods 
        //
        //------------------------------------------------------------------- 

        #region Private Methods

        private void FindViewPort() 
        {
            // assume GridViewRowPresenter is in Item's template 
            _viewItem = this.TemplatedParent as FrameworkElement; 

            if (_viewItem != null) 
            {
                ItemsControl itemsControl = ItemsControl.ItemsControlFromItemContainer(_viewItem) as ItemsControl;

                if (itemsControl != null) 
                {
                    ScrollViewer scrollViewer = itemsControl.ScrollHost as ScrollViewer; 
                    if (scrollViewer != null) 
                    {
                        // check if Virtualizing Panel do works 
                        if (itemsControl.ItemsHost is VirtualizingPanel &&
                            scrollViewer.CanContentScroll)
                        {
                            // find the 'PART_ScrollContentPresenter' in GridViewScrollViewer 
                            _viewPort = scrollViewer.GetTemplateChild(ScrollViewer.ScrollContentPresenterTemplateName) as FrameworkElement;
 
                            // in case GridViewScrollViewer is re-styled, say, cannot find PART_ScrollContentPresenter 
                            if (_viewPort == null)
                            { 
                                _viewPort = scrollViewer;
                            }
                        }
                    } 
                }
            } 
        } 

        private bool CheckVisibleOnCurrentPage() 
        {
            if (!_viewPortValid)
            {
                FindViewPort(); 
            }
 
            bool result = true; 

            if (_viewItem != null && _viewPort != null) 
            {
                Rect viewPortBounds = new Rect(new Point(), _viewPort.RenderSize);
                Rect itemBounds = new Rect(new Point(), _viewItem.RenderSize);
                itemBounds = _viewItem.TransformToAncestor(_viewPort).TransformBounds(itemBounds); 

                // check if item bounds falls in view port bounds (in height) 
                result = CheckContains(viewPortBounds, itemBounds); 
            }
 
            return result;
        }

        private bool CheckContains(Rect container, Rect element) 
        {
            // Check if ANY part of the element reside in container 
            // return true if and only if (either case) 
            //
            // +-------------------------------------------+ 
            // +  #================================#       +
            // +--#--------------------------------#-------+
            //    #                                #
            //    #                                # 
            // +--#--------------------------------#-------+
            // +  #                                #       + 
            // +--#--------------------------------#-------+ 
            //    #                                #
            //    #                                # 
            // +--#--------------------------------#-------+
            // +  #================================#       +
            // +-------------------------------------------+
 
            // The tolerance here is to make sure at least 2 pixels are inside container
            const double tolerance = 2.0; 
 
            return ((CheckIsPointBetween(container, element.Top) && CheckIsPointBetween(container, element.Bottom)) ||
                    CheckIsPointBetween(element, container.Top + tolerance) || 
                    CheckIsPointBetween(element, container.Bottom - tolerance));
        }

        private bool CheckIsPointBetween(Rect rect, double pointY) 
        {
            // return rect.Top <= pointY <= rect.Bottom 
            return (DoubleUtil.LessThanOrClose(rect.Top, pointY) && 
                    DoubleUtil.LessThanOrClose(pointY, rect.Bottom));
        } 

        private void OnLayoutUpdated(object sender, EventArgs e)
        {
            bool desiredWidthChanged = false; // whether the shared minimum width has been changed since last layout 

            foreach (GridViewColumn column in Columns) 
            { 
                if ((column.State != ColumnMeasureState.SpecificWidth))
                { 
                    column.State = ColumnMeasureState.Data;

                    if (DesiredWidthList == null || column.ActualIndex >= DesiredWidthList.Count)
                    { 
                        // How can this happen?
                        // Between the last measure was called and this update is called, there can be a 
                        // change done to the ColumnCollection and result in DesiredWidthList out of [....] 
                        // with the columnn collection. What can we do is end this call asap and the next
                        // measure will fix it. 
                        desiredWidthChanged = true;
                        break;
                    }
 
                    if (!DoubleUtil.AreClose(column.DesiredWidth, DesiredWidthList[column.ActualIndex]))
                    { 
                        // Update the record because collection operation latter on might 
                        // need to verified this list again, e.g. insert an 'auto'
                        // column, so that we won't trigger unnecessary update due to 
                        // inconsistency of this column.
                        DesiredWidthList[column.ActualIndex] = column.DesiredWidth;

                        desiredWidthChanged = true; 
                    }
                } 
            } 

            if (desiredWidthChanged) 
            {
                InvalidateMeasure();
            }
 
            LayoutUpdated -= new EventHandler(OnLayoutUpdated);
        } 
 
        private FrameworkElement CreateCell(GridViewColumn column)
        { 
            Debug.Assert(column != null, "column shouldn't be null");

            FrameworkElement cell;
            BindingBase binding; 

            // Priority: DisplayMemberBinding > CellTemplate > CellTemplateSelector 
 
            if ((binding = column.DisplayMemberBinding) != null)
            { 
                cell = new TextBlock();

                // Needed this. Otherwise can't size to content at startup time.
                // The reason is cell.Text is empty after the first round of measure. 
                cell.DataContext = Content;
 
                cell.SetBinding(TextBlock.TextProperty, binding); 
            }
            else 
            {
                ContentPresenter cp = new ContentPresenter();
                cp.Content = Content;
 
                DataTemplate dt;
                DataTemplateSelector dts; 
                if ((dt = column.CellTemplate) != null) 
                {
                    cp.ContentTemplate = dt; 
                }
                if ((dts = column.CellTemplateSelector) != null)
                {
                    cp.ContentTemplateSelector = dts; 
                }
 
                cell = cp; 
            }
 
            // copy alignment properties from ListViewItem
            // for perf reason, not use binding here
            ContentControl parent;
            if ((parent = TemplatedParent as ContentControl) != null) 
            {
                cell.VerticalAlignment = parent.VerticalContentAlignment; 
                cell.HorizontalAlignment = parent.HorizontalContentAlignment; 
            }
 
            cell.Margin = _defalutCellMargin;

            return cell;
        } 

        private void RenewCell(int index, GridViewColumn column) 
        { 
            InternalChildren.RemoveAt(index);
            InternalChildren.Insert(index, CreateCell(column)); 
        }


        ///  
        /// Updates all cells to the latest Content.
        ///  
        private void UpdateCells() 
        {
            ContentPresenter cellAsCP; 
            FrameworkElement cell;
            UIElementCollection children = InternalChildren;
            ContentControl parent = TemplatedParent as ContentControl;
 
            for (int i = 0; i < children.Count; i++)
            { 
                cell = (FrameworkElement)children[i]; 

                if ((cellAsCP = cell as ContentPresenter) != null) 
                {
                    cellAsCP.Content = Content;
                }
                else 
                {
                    Debug.Assert(cell is TextBlock, "cells are either TextBlocks or ContentPresenters"); 
                    cell.DataContext = Content; 
                }
 
                if (parent != null)
                {
                    cell.VerticalAlignment = parent.VerticalContentAlignment;
                    cell.HorizontalAlignment = parent.HorizontalContentAlignment; 
                }
            } 
        } 

 
        #endregion

        //--------------------------------------------------------------------
        // 
        // Private Properties / Fields
        // 
        //------------------------------------------------------------------- 

        #region Private Properties / Fields 

        // if RowPresenter is not 'real' visible, it should not participating in measuring column width
        // NOTE: IsVisible is force-inheriting parent's value, that's why we pick IsVisible instead of Visibility
        //       e.g. if RowPresenter's parent is hidden/collapsed (e.g. in ListTreeView), 
        //            then RowPresenter.Visiblity = Visible, but RowPresenter.IsVisible = false
        private bool IsOnCurrentPage 
        { 
            get
            { 
                if (!_isOnCurrentPageValid)
                {
                    _isOnCurrentPage = IsVisible && CheckVisibleOnCurrentPage();
                    _isOnCurrentPageValid = true; 
                }
 
                return _isOnCurrentPage; 
            }
        } 

        private FrameworkElement _viewPort;
        private FrameworkElement _viewItem;
        private bool _viewPortValid = false; 
        private bool _isOnCurrentPage = false;
        private bool _isOnCurrentPageValid = false; 
 
        private static readonly Thickness _defalutCellMargin = new Thickness(6, 0, 6, 0);
 
        #endregion Private Properties / Fields
    }
}

// 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