GridViewRowPresenter.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / 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)
        { 
            ((GridViewRowPresenter)d).NeedUpdateVisualTree = true;
        } 
 
        #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 sync
                        // 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));
        } 

        #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