GridViewRowPresenter.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 / GridViewRowPresenter.cs / 1305600 / 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
using MS.Utility; 
 

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) 
        {
            GridViewRowPresenter gvrp = (GridViewRowPresenter)d; 
 
            //
            // 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;
 
            // DisconnectedItem doesn't count as a real type change 
            if (e.NewValue == BindingExpressionBase.DisconnectedItem)
            { 
                gvrp._oldContentType = oldType;
                newType = oldType;
            }
            else if (e.OldValue == BindingExpressionBase.DisconnectedItem) 
            {
                oldType = gvrp._oldContentType; 
            } 

            if (oldType != newType) 
            {
                gvrp.NeedUpdateVisualTree = true;
            }
            else 
            {
                gvrp.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 Type _oldContentType; 
        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
using MS.Utility; 
 

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) 
        {
            GridViewRowPresenter gvrp = (GridViewRowPresenter)d; 
 
            //
            // 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;
 
            // DisconnectedItem doesn't count as a real type change 
            if (e.NewValue == BindingExpressionBase.DisconnectedItem)
            { 
                gvrp._oldContentType = oldType;
                newType = oldType;
            }
            else if (e.OldValue == BindingExpressionBase.DisconnectedItem) 
            {
                oldType = gvrp._oldContentType; 
            } 

            if (oldType != newType) 
            {
                gvrp.NeedUpdateVisualTree = true;
            }
            else 
            {
                gvrp.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 Type _oldContentType; 
        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