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; // Listusing 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 ListActualCells { 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; // Listusing 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 ListActualCells { 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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DispatcherOperation.cs
- WindowVisualStateTracker.cs
- GridViewDeleteEventArgs.cs
- DiffuseMaterial.cs
- SimpleWorkerRequest.cs
- LayoutTable.cs
- MexBindingElement.cs
- MemberCollection.cs
- ConstraintStruct.cs
- ToolBarPanel.cs
- SerialPort.cs
- OleDbFactory.cs
- CodeTypeOfExpression.cs
- CodeDomSerializationProvider.cs
- ExpandCollapsePattern.cs
- WebDescriptionAttribute.cs
- ApplicationException.cs
- GPPOINTF.cs
- SystemGatewayIPAddressInformation.cs
- IsolatedStorageFilePermission.cs
- PerformanceCounterPermissionAttribute.cs
- FatalException.cs
- ProjectedSlot.cs
- BrushConverter.cs
- PointAnimation.cs
- messageonlyhwndwrapper.cs
- SystemGatewayIPAddressInformation.cs
- Model3DGroup.cs
- ThemeDirectoryCompiler.cs
- SqlBulkCopyColumnMappingCollection.cs
- AnimatedTypeHelpers.cs
- CompositeKey.cs
- MembershipSection.cs
- CompiledRegexRunnerFactory.cs
- TabItemAutomationPeer.cs
- RequestDescription.cs
- HttpCapabilitiesEvaluator.cs
- BaseParaClient.cs
- CollectionType.cs
- MetadataPropertyCollection.cs
- XmlSchemaValidator.cs
- VisualTransition.cs
- AspNetHostingPermission.cs
- TraceContextEventArgs.cs
- RunWorkerCompletedEventArgs.cs
- GridErrorDlg.cs
- ForceCopyBuildProvider.cs
- PersonalizationStateQuery.cs
- Vector3DKeyFrameCollection.cs
- DurableInstancingOptions.cs
- SaveFileDialogDesigner.cs
- WebPartVerbsEventArgs.cs
- SendReply.cs
- CodeExpressionCollection.cs
- EnumType.cs
- GridLengthConverter.cs
- TraceSection.cs
- XmlSchemaComplexType.cs
- StoragePropertyMapping.cs
- StopRoutingHandler.cs
- DataPagerCommandEventArgs.cs
- Point3DCollectionConverter.cs
- DBSchemaTable.cs
- MexBindingBindingCollectionElement.cs
- ObjectQueryState.cs
- TypefaceMetricsCache.cs
- ErrorTolerantObjectWriter.cs
- ToolStripSeparator.cs
- smtppermission.cs
- ExtractedStateEntry.cs
- BindingElementCollection.cs
- XsltCompileContext.cs
- COM2PictureConverter.cs
- SerializationSectionGroup.cs
- KeyInstance.cs
- Rule.cs
- RowUpdatingEventArgs.cs
- DirectoryInfo.cs
- ParenthesizePropertyNameAttribute.cs
- TypeGeneratedEventArgs.cs
- FileSystemInfo.cs
- RegexMatchCollection.cs
- EntityCollection.cs
- VectorConverter.cs
- SingleTagSectionHandler.cs
- TimerTable.cs
- DesignerOptions.cs
- DeferredTextReference.cs
- UnsafeNativeMethods.cs
- Content.cs
- DataBinder.cs
- TypeUsage.cs
- shaper.cs
- FileDialogPermission.cs
- InfiniteIntConverter.cs
- HtmlHistory.cs
- XmlArrayItemAttribute.cs
- TextReader.cs
- WindowsImpersonationContext.cs
- Query.cs