Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / Controls / Stack.cs / 1 / Stack.cs
//---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // // File: Stack.cs // // Description: Implementation of StackPanel class. // Spec at http://avalon/layout/Specs/StackPanel.doc // // History: // 06/25/2004 : olego - Created // 09/03/2004 : greglett - Converted to new layout system, IScrollInfo implementation // //--------------------------------------------------------------------------- //#define Profiling using MS.Internal; using MS.Utility; using System; using System.Collections; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Windows.Controls.Primitives; using System.Windows.Media; using System.Windows.Threading; namespace System.Windows.Controls { ////// StackPanel is used to arrange children into single line. /// public class StackPanel : Panel, IScrollInfo { //------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------- #region Constructors ////// Default constructor. /// public StackPanel() : base() { } #endregion Constructors //-------------------------------------------------------------------- // // Public Methods // //------------------------------------------------------------------- #region Public Methods //------------------------------------------------------------ // IScrollInfo Methods //------------------------------------------------------------ #region IScrollInfo Methods ////// Scroll content by one line to the top. /// public void LineUp() { SetVerticalOffset(VerticalOffset - ((Orientation == Orientation.Vertical) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one line to the bottom. /// public void LineDown() { SetVerticalOffset(VerticalOffset + ((Orientation == Orientation.Vertical) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one line to the left. /// public void LineLeft() { SetHorizontalOffset(HorizontalOffset - ((Orientation == Orientation.Horizontal) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one line to the right. /// public void LineRight() { SetHorizontalOffset(HorizontalOffset + ((Orientation == Orientation.Horizontal) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one page to the top. /// public void PageUp() { SetVerticalOffset(VerticalOffset - ViewportHeight); } ////// Scroll content by one page to the bottom. /// public void PageDown() { SetVerticalOffset(VerticalOffset + ViewportHeight); } ////// Scroll content by one page to the left. /// public void PageLeft() { SetHorizontalOffset(HorizontalOffset - ViewportWidth); } ////// Scroll content by one page to the right. /// public void PageRight() { SetHorizontalOffset(HorizontalOffset + ViewportWidth); } ////// Scroll content by one page to the top. /// public void MouseWheelUp() { SetVerticalOffset(VerticalOffset - SystemParameters.WheelScrollLines * ((Orientation == Orientation.Vertical) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one page to the bottom. /// public void MouseWheelDown() { SetVerticalOffset(VerticalOffset + SystemParameters.WheelScrollLines * ((Orientation == Orientation.Vertical) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one page to the left. /// public void MouseWheelLeft() { SetHorizontalOffset(HorizontalOffset - 3.0 * ((Orientation == Orientation.Horizontal) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one page to the right. /// public void MouseWheelRight() { SetHorizontalOffset(HorizontalOffset + 3.0 * ((Orientation == Orientation.Horizontal) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Set the HorizontalOffset to the passed value. /// public void SetHorizontalOffset(double offset) { EnsureScrollData(); double scrollX = ScrollContentPresenter.ValidateInputOffset(offset, "HorizontalOffset"); if (!DoubleUtil.AreClose(scrollX, _scrollData._offset.X)) { _scrollData._offset.X = scrollX; InvalidateMeasure(); } } ////// Set the VerticalOffset to the passed value. /// public void SetVerticalOffset(double offset) { EnsureScrollData(); double scrollY = ScrollContentPresenter.ValidateInputOffset(offset, "VerticalOffset"); if (!DoubleUtil.AreClose(scrollY, _scrollData._offset.Y)) { _scrollData._offset.Y = scrollY; InvalidateMeasure(); } } ////// StackPanel implementation of // The goal is to change offsets to bring the child into view, and return a rectangle in our space to make visible. // The rectangle we return is in the physical dimension the input target rect transformed into our pace. // In the logical dimension, it is our immediate child's rect. // Note: This code presently assumes we/children are layout clean. See work item 22269 for more detail. public Rect MakeVisible(Visual visual, Rect rectangle) { Vector newOffset = new Vector(); Rect newRect = new Rect(); // We can only work on visuals that are us or children. // An empty rect has no size or position. We can't meaningfully use it. if ( rectangle.IsEmpty || visual == null || visual == (Visual)this || !this.IsAncestorOf(visual)) { return Rect.Empty; } #pragma warning disable 1634, 1691 #pragma warning disable 56506 // Compute the child's rect relative to (0,0) in our coordinate space. // This is a false positive by PreSharp. visual cannot be null because of the 'if' check above GeneralTransform childTransform = visual.TransformToAncestor(this); #pragma warning restore 56506 #pragma warning restore 1634, 1691 rectangle = childTransform.TransformBounds(rectangle); // We can't do any work unless we're scrolling. if (!IsScrolling) { return rectangle; } // Bring the target rect into view in the physical dimension. MakeVisiblePhysicalHelper(rectangle, ref newOffset, ref newRect); // Bring our child containing the visual into view. int childIndex = FindChildIndexThatParentsVisual(visual); MakeVisibleLogicalHelper(childIndex, ref newOffset, ref newRect); // We have computed the scrolling offsets; validate and scroll to them. newOffset.X = ScrollContentPresenter.CoerceOffset(newOffset.X, _scrollData._extent.Width, _scrollData._viewport.Width); newOffset.Y = ScrollContentPresenter.CoerceOffset(newOffset.Y, _scrollData._extent.Height, _scrollData._viewport.Height); if (!DoubleUtil.AreClose(newOffset, _scrollData._offset)) { _scrollData._offset = newOffset; InvalidateMeasure(); OnScrollChange(); } // Return the rectangle return newRect; } #endregion #endregion //------------------------------------------------------------------- // // Public Properties // //-------------------------------------------------------------------- #region Public Properties ///. /// /// Specifies dimension of children stacking. /// public Orientation Orientation { get { return (Orientation) GetValue(OrientationProperty); } set { SetValue(OrientationProperty, value); } } ////// DependencyProperty for public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register( "Orientation", typeof(Orientation), typeof(StackPanel), new FrameworkPropertyMetadata( Orientation.Vertical, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnOrientationChanged)), new ValidateValueCallback(ScrollBar.IsValidOrientation)); ///property. /// /// This property is always true because this panel has vertical or horizontal orientation /// protected internal override bool HasLogicalOrientation { get { return true; } } ////// Orientation of the panel if its layout is in one dimension. /// Otherwise HasLogicalOrientation is false and LogicalOrientation should be ignored /// protected internal override Orientation LogicalOrientation { get { return this.Orientation; } } //----------------------------------------------------------- // IScrollInfo Properties //----------------------------------------------------------- #region IScrollInfo Properties ////// StackPanel reacts to this property by changing it's child measurement algorithm. /// If scrolling in a dimension, infinite space is allowed the child; otherwise, available size is preserved. /// [DefaultValue(false)] public bool CanHorizontallyScroll { get { if (_scrollData == null) { return false; } return _scrollData._allowHorizontal; } set { EnsureScrollData(); if (_scrollData._allowHorizontal != value) { _scrollData._allowHorizontal = value; InvalidateMeasure(); } } } ////// StackPanel reacts to this property by changing it's child measurement algorithm. /// If scrolling in a dimension, infinite space is allowed the child; otherwise, available size is preserved. /// [DefaultValue(false)] public bool CanVerticallyScroll { get { if (_scrollData == null) { return false; } return _scrollData._allowVertical; } set { EnsureScrollData(); if (_scrollData._allowVertical != value) { _scrollData._allowVertical = value; InvalidateMeasure(); } } } ////// ExtentWidth contains the horizontal size of the scrolled content element in 1/96" /// public double ExtentWidth { get { if (_scrollData == null) { return 0.0; } return _scrollData._extent.Width; } } ////// ExtentHeight contains the vertical size of the scrolled content element in 1/96" /// public double ExtentHeight { get { if (_scrollData == null) { return 0.0; } return _scrollData._extent.Height; } } ////// ViewportWidth contains the horizontal size of content's visible range in 1/96" /// public double ViewportWidth { get { if (_scrollData == null) { return 0.0; } return _scrollData._viewport.Width; } } ////// ViewportHeight contains the vertical size of content's visible range in 1/96" /// public double ViewportHeight { get { if (_scrollData == null) { return 0.0; } return _scrollData._viewport.Height; } } ////// HorizontalOffset is the horizontal offset of the scrolled content in 1/96". /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public double HorizontalOffset { get { if (_scrollData == null) { return 0.0; } return _scrollData._computedOffset.X; } } ////// VerticalOffset is the vertical offset of the scrolled content in 1/96". /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public double VerticalOffset { get { if (_scrollData == null) { return 0.0; } return _scrollData._computedOffset.Y; } } ////// ScrollOwner is the container that controls any scrollbars, headers, etc... that are dependant /// on this IScrollInfo's properties. /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ScrollViewer ScrollOwner { get { EnsureScrollData(); return _scrollData._scrollOwner; } set { EnsureScrollData(); if (value != _scrollData._scrollOwner) { ResetScrolling(this); _scrollData._scrollOwner = value; } } } #endregion IScrollInfo Properties #endregion Public Properties //------------------------------------------------------------------- // // Protected Methods // //-------------------------------------------------------------------- #region Protected Methods ////// General StackPanel layout behavior is to grow unbounded in the "stacking" direction (Size To Content). /// Children in this dimension are encouraged to be as large as they like. In the other dimension, /// StackPanel will assume the maximum size of its children. /// ////// When scrolling, StackPanel will not grow in layout size but effectively add the children on a z-plane which /// will probably be clipped by some parent (typically a ScrollContentPresenter) to Stack's size. /// /// Constraint ///Desired size protected override Size MeasureOverride(Size constraint) { #if Profiling if (Panel.IsAboutToGenerateContent(this)) return MeasureOverrideProfileStub(constraint); else return RealMeasureOverride(constraint); } // this is a handy place to start/stop profiling private Size MeasureOverrideProfileStub(Size constraint) { return RealMeasureOverride(constraint); } private Size RealMeasureOverride(Size constraint) { #endif UIElementCollection children = InternalChildren; Size stackDesiredSize = new Size(); Size layoutSlotSize = constraint; bool fHorizontal = (Orientation == Orientation.Horizontal); int firstViewport; // First child index in the viewport. int lastViewport = -1; // Last child index in the viewport. -1 indicates we have not yet iterated through the last child. double logicalVisibleSpace, childLogicalSize; bool etwTracingEnabled = IsScrolling && EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal); if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.StartEvent, "STACK:MeasureOverride"); } // // Initialize child sizing and iterator data // Allow children as much size as they want along the stack. // if (fHorizontal) { layoutSlotSize.Width = Double.PositiveInfinity; if (IsScrolling && CanVerticallyScroll) { layoutSlotSize.Height = Double.PositiveInfinity; } firstViewport = (IsScrolling) ? CoerceOffsetToInteger(_scrollData._offset.X, children.Count) : 0; logicalVisibleSpace = constraint.Width; } else { layoutSlotSize.Height = Double.PositiveInfinity; if (IsScrolling && CanHorizontallyScroll) { layoutSlotSize.Width = Double.PositiveInfinity; } firstViewport = (IsScrolling) ? CoerceOffsetToInteger(_scrollData._offset.Y, children.Count) : 0; logicalVisibleSpace = constraint.Height; } // // Iterate through children. // While we still supported virtualization, this was hidden in a child iterator (see source history). // for (int i = 0, count = children.Count; i < count; ++i) { // Get next child. UIElement child = children[i]; if (child == null) { continue; } // Measure the child. child.Measure(layoutSlotSize); Size childDesiredSize = child.DesiredSize; // Accumulate child size. if (fHorizontal) { stackDesiredSize.Width += childDesiredSize.Width; stackDesiredSize.Height = Math.Max(stackDesiredSize.Height, childDesiredSize.Height); childLogicalSize = childDesiredSize.Width; } else { stackDesiredSize.Width = Math.Max(stackDesiredSize.Width, childDesiredSize.Width); stackDesiredSize.Height += childDesiredSize.Height; childLogicalSize = childDesiredSize.Height; } // Adjust remaining viewport space if we are scrolling and within the viewport region. // While scrolling (not virtualizing), we always measure children before and after the viewport. if (IsScrolling && lastViewport == -1 && i >= firstViewport) { logicalVisibleSpace -= childLogicalSize; if (DoubleUtil.LessThanOrClose(logicalVisibleSpace, 0.0)) { lastViewport = i; } } } // // Compute Scrolling stuff. // if (IsScrolling) { // Compute viewport and extent. Size viewport = constraint; Size extent = stackDesiredSize; Vector offset = _scrollData._offset; // If we have not yet set the last child in the viewport, set it to the last child. if (lastViewport == -1) { lastViewport = children.Count - 1; } // If we or children have resized, it's possible that we can now display more content. // This is true if we started at a nonzero offeset and still have space remaining. // In this case, we loop back through previous children until we run out of space. while (firstViewport > 0) { double projectedLogicalVisibleSpace = logicalVisibleSpace; if (fHorizontal) { projectedLogicalVisibleSpace -= children[firstViewport - 1].DesiredSize.Width; } else { projectedLogicalVisibleSpace -= children[firstViewport - 1].DesiredSize.Height; } // If we have run out of room, break. if (DoubleUtil.LessThan(projectedLogicalVisibleSpace, 0.0)) { break; } // Adjust viewport firstViewport--; logicalVisibleSpace = projectedLogicalVisibleSpace; } int logicalExtent = children.Count; int logicalViewport = lastViewport - firstViewport; // We are conservative when estimating a viewport, not including the last element in case it is only partially visible. // We want to count it if it is fully visible (>= 0 space remaining) or the only element in the viewport. if (logicalViewport == 0 || DoubleUtil.GreaterThanOrClose(logicalVisibleSpace, 0.0)) { logicalViewport++; } if (fHorizontal) { _scrollData._physicalViewport = viewport.Width; viewport.Width = logicalViewport; extent.Width = logicalExtent; offset.X = firstViewport; offset.Y = Math.Max(0, Math.Min(offset.Y, extent.Height - viewport.Height)); } else { _scrollData._physicalViewport = viewport.Height; viewport.Height = logicalViewport; extent.Height = logicalExtent; offset.Y = firstViewport; offset.X = Math.Max(0, Math.Min(offset.X, extent.Width - viewport.Width)); } // Since we can offset and clip our content, we never need to be larger than the parent suggestion. // If we returned the full size of the content, we would always be so big we didn't need to scroll. :) stackDesiredSize.Width = Math.Min(stackDesiredSize.Width, constraint.Width); stackDesiredSize.Height = Math.Min(stackDesiredSize.Height, constraint.Height); // Verify Scroll Info, invalidate ScrollOwner if necessary. VerifyScrollingData(viewport, extent, offset); } if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.EndEvent, "STACK:MeasureOverride"); } return stackDesiredSize; } ////// Content arrangement. /// /// Arrange size protected override Size ArrangeOverride(Size arrangeSize) { UIElementCollection children = this.Children; bool fHorizontal = (Orientation == Orientation.Horizontal); Rect rcChild = new Rect(arrangeSize); double previousChildSize = 0.0; bool etwTracingEnabled = IsScrolling && EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal); if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.StartEvent, "STACK:ArrangeOverride"); } // // Compute scroll offset and seed it into rcChild. // if (IsScrolling) { if (fHorizontal) { rcChild.X = ComputePhysicalFromLogicalOffset(_scrollData._computedOffset.X, true); rcChild.Y = -1.0 * _scrollData._computedOffset.Y; } else { rcChild.X = -1.0 * _scrollData._computedOffset.X; rcChild.Y = ComputePhysicalFromLogicalOffset(_scrollData._computedOffset.Y, false); } } // // Arrange and Position Children. // for (int i = 0, count = children.Count; i < count; ++i) { UIElement child = (UIElement)children[i]; if (child == null) { continue; } if (fHorizontal) { rcChild.X += previousChildSize; previousChildSize = child.DesiredSize.Width; rcChild.Width = previousChildSize; rcChild.Height = Math.Max(arrangeSize.Height, child.DesiredSize.Height); } else { rcChild.Y += previousChildSize; previousChildSize = child.DesiredSize.Height; rcChild.Height = previousChildSize; rcChild.Width = Math.Max(arrangeSize.Width, child.DesiredSize.Width); } child.Arrange(rcChild); } if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.EndEvent, "STACK:ArrangeOverride"); } return arrangeSize; } #endregion Protected Methods //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods private void EnsureScrollData() { if (_scrollData == null) { _scrollData = new ScrollData(); } } private static void ResetScrolling(StackPanel element) { element.InvalidateMeasure(); // Clear scrolling data. Because of thrash (being disconnected & reconnected, &c...), we may if (element.IsScrolling) { element._scrollData.ClearLayout(); } } // OnScrollChange is an override called whenever the IScrollInfo exposed scrolling state changes on this element. // At the time this method is called, scrolling state is in its new, valid state. private void OnScrollChange() { if (ScrollOwner != null) { ScrollOwner.InvalidateScrollInfo(); } } private void VerifyScrollingData(Size viewport, Size extent, Vector offset) { bool fValid = true; Debug.Assert(IsScrolling); fValid &= DoubleUtil.AreClose(viewport, _scrollData._viewport); fValid &= DoubleUtil.AreClose(extent, _scrollData._extent); fValid &= DoubleUtil.AreClose(offset, _scrollData._computedOffset); _scrollData._offset = offset; if (!fValid) { _scrollData._viewport = viewport; _scrollData._extent = extent; _scrollData._computedOffset = offset; OnScrollChange(); } } // Translates a logical (child index) offset to a physical (1/96") when scrolling. // If virtualizing, it makes the assumption that the logicalOffset is always the first in the visual collection // and thus returns 0. // If not virtualizing, it assumes that children are Measure clean; should only be called after running Measure. private double ComputePhysicalFromLogicalOffset(double logicalOffset, bool fHorizontal) { double physicalOffset = 0.0; UIElementCollection children = this.Children; Debug.Assert(logicalOffset == 0 || (logicalOffset > 0 && logicalOffset < children.Count)); for (int i = 0; i < logicalOffset; i++) { physicalOffset -= (fHorizontal) ? ((UIElement)children[i]).DesiredSize.Width : ((UIElement)children[i]).DesiredSize.Height; } return physicalOffset; } private int FindChildIndexThatParentsVisual(Visual child) { DependencyObject dependencyObjectChild = child; DependencyObject parent = VisualTreeHelper.GetParent(child); while (parent != this) { dependencyObjectChild = parent; parent = VisualTreeHelper.GetParent(dependencyObjectChild); if (parent == null) { throw new ArgumentException(SR.Get(SRID.Stack_VisualInDifferentSubTree),"child"); } } UIElementCollection children = this.Children; //The Downcast is ok because StackPanel's //child has to be a UIElement to be in this.Children collection return (children.IndexOf((UIElement)dependencyObjectChild)); } private void MakeVisiblePhysicalHelper(Rect r, ref Vector newOffset, ref Rect newRect) { double viewportOffset; double viewportSize; double targetRectOffset; double targetRectSize; double minPhysicalOffset; bool fHorizontal = (Orientation == Orientation.Horizontal); if (fHorizontal) { viewportOffset = _scrollData._computedOffset.Y; viewportSize = ViewportHeight; targetRectOffset = r.Y; targetRectSize = r.Height; } else { viewportOffset = _scrollData._computedOffset.X; viewportSize = ViewportWidth; targetRectOffset = r.X; targetRectSize = r.Width; } targetRectOffset += viewportOffset; minPhysicalOffset = ScrollContentPresenter.ComputeScrollOffsetWithMinimalScroll( viewportOffset, viewportOffset + viewportSize, targetRectOffset, targetRectOffset + targetRectSize); // Compute the visible rectangle of the child relative to the viewport. double left = Math.Max(targetRectOffset, minPhysicalOffset); targetRectSize = Math.Max(Math.Min(targetRectSize + targetRectOffset, minPhysicalOffset + viewportSize) - left, 0); targetRectOffset = left; targetRectOffset -= viewportOffset; if (fHorizontal) { newOffset.Y = minPhysicalOffset; newRect.Y = targetRectOffset; newRect.Height = targetRectSize; } else { newOffset.X = minPhysicalOffset; newRect.X = targetRectOffset; newRect.Width = targetRectSize; } } private void MakeVisibleLogicalHelper(int childIndex, ref Vector newOffset, ref Rect newRect) { bool fHorizontal = (Orientation == Orientation.Horizontal); int firstChildInView; int newFirstChild; int viewportSize; double childOffsetWithinViewport = 0; if (fHorizontal) { firstChildInView = (int)_scrollData._computedOffset.X; viewportSize = (int)_scrollData._viewport.Width; } else { firstChildInView = (int)_scrollData._computedOffset.Y; viewportSize = (int)_scrollData._viewport.Height; } newFirstChild = firstChildInView; // If the target child is before the current viewport, move the viewport to put the child at the top. if (childIndex < firstChildInView) { newFirstChild = childIndex; } // If the target child is after the current viewport, move the viewport to put the child at the bottom. else if (childIndex > firstChildInView + viewportSize - 1) { Size childDesiredSize = InternalChildren[childIndex].DesiredSize; double nextChildSize = ((fHorizontal) ? childDesiredSize.Width : childDesiredSize.Height); double viewportSpace = _scrollData._physicalViewport - nextChildSize; int i = childIndex; while (i > 0 && DoubleUtil.GreaterThanOrClose(viewportSpace, 0.0)) { i--; childDesiredSize = InternalChildren[i].DesiredSize; nextChildSize = ((fHorizontal) ? childDesiredSize.Width : childDesiredSize.Height); childOffsetWithinViewport += nextChildSize; viewportSpace -= nextChildSize; } if (i != childIndex && DoubleUtil.LessThan(viewportSpace, 0.0)) { childOffsetWithinViewport -= nextChildSize; i++; } newFirstChild = i; } if (fHorizontal) { newOffset.X = newFirstChild; newRect.X = childOffsetWithinViewport; newRect.Width = InternalChildren[childIndex].DesiredSize.Width; } else { newOffset.Y = newFirstChild; newRect.Y = childOffsetWithinViewport; newRect.Height = InternalChildren[childIndex].DesiredSize.Height; } } static private int CoerceOffsetToInteger(double offset, int numberOfItems) { int iNewOffset; if (Double.IsNegativeInfinity(offset)) { iNewOffset = 0; } else if (Double.IsPositiveInfinity(offset)) { iNewOffset = numberOfItems - 1; } else { iNewOffset = (int)offset; iNewOffset = Math.Max(Math.Min(numberOfItems - 1, iNewOffset), 0); } return iNewOffset; } //------------------------------------------------------------ // Avalon Property Callbacks/Overrides //----------------------------------------------------------- #region Avalon Property Callbacks/Overrides ////// private static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // Since Orientation is so essential to logical scrolling/virtualization, we synchronously check if // the new value is different and clear all scrolling data if so. ResetScrolling(d as StackPanel); } #endregion #endregion Private Methods //------------------------------------------------------ // // Private Properties // //----------------------------------------------------- #region Private Properties private bool IsScrolling { get { return (_scrollData != null) && (_scrollData._scrollOwner != null); } } // // This property // 1. Finds the correct initial size for the _effectiveValues store on the current DependencyObject // 2. This is a performance optimization // internal override int EffectiveValuesInitialSize { get { return 9; } } #endregion Private Properties //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- #region Private Fields // Logical scrolling and virtualization data. private ScrollData _scrollData; #endregion Private Fields //------------------------------------------------------ // // Private Structures / Classes // //----------------------------------------------------- #region Private Structures Classes //------------------------------------------------------------ // ScrollData class //------------------------------------------------------------ #region ScrollData // Helper class to hold scrolling data. // This class exists to reduce working set when StackPanel is used outside a scrolling situation. // Standard "extra pointer always for less data sometimes" cache savings model: // !Scroll [1xReference] // Scroll [1xReference] + [6xDouble + 1xReference] private class ScrollData { // Clears layout generated data. // Does not clear scrollOwner, because unless resetting due to a scrollOwner change, we won't get reattached. internal void ClearLayout() { _offset = new Vector(); _viewport = _extent = new Size(); _physicalViewport = 0; } // For Stack/Flow, the two dimensions of properties are in different units: // 1. The "logically scrolling" dimension uses items as units. // 2. The other dimension physically scrolls. Units are in Avalon pixels (1/96"). internal bool _allowHorizontal; internal bool _allowVertical; internal Vector _offset; // Scroll offset of content. Positive corresponds to a visually upward offset. internal Vector _computedOffset = new Vector(0,0); internal Size _viewport; // ViewportSize is in {pixels x items} (or vice-versa). internal Size _extent; // Extent is the total number of children (logical dimension) or physical size internal double _physicalViewport; // The physical size of the viewport for the items dimension above. internal ScrollViewer _scrollOwner; // ScrollViewer to which we're attached. } #endregion ScrollData #endregion Private Structures Classes } } // 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. // // File: Stack.cs // // Description: Implementation of StackPanel class. // Spec at http://avalon/layout/Specs/StackPanel.doc // // History: // 06/25/2004 : olego - Created // 09/03/2004 : greglett - Converted to new layout system, IScrollInfo implementation // //--------------------------------------------------------------------------- //#define Profiling using MS.Internal; using MS.Utility; using System; using System.Collections; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Windows.Controls.Primitives; using System.Windows.Media; using System.Windows.Threading; namespace System.Windows.Controls { ////// /// StackPanel is used to arrange children into single line. /// public class StackPanel : Panel, IScrollInfo { //------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------- #region Constructors ////// Default constructor. /// public StackPanel() : base() { } #endregion Constructors //-------------------------------------------------------------------- // // Public Methods // //------------------------------------------------------------------- #region Public Methods //------------------------------------------------------------ // IScrollInfo Methods //------------------------------------------------------------ #region IScrollInfo Methods ////// Scroll content by one line to the top. /// public void LineUp() { SetVerticalOffset(VerticalOffset - ((Orientation == Orientation.Vertical) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one line to the bottom. /// public void LineDown() { SetVerticalOffset(VerticalOffset + ((Orientation == Orientation.Vertical) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one line to the left. /// public void LineLeft() { SetHorizontalOffset(HorizontalOffset - ((Orientation == Orientation.Horizontal) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one line to the right. /// public void LineRight() { SetHorizontalOffset(HorizontalOffset + ((Orientation == Orientation.Horizontal) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one page to the top. /// public void PageUp() { SetVerticalOffset(VerticalOffset - ViewportHeight); } ////// Scroll content by one page to the bottom. /// public void PageDown() { SetVerticalOffset(VerticalOffset + ViewportHeight); } ////// Scroll content by one page to the left. /// public void PageLeft() { SetHorizontalOffset(HorizontalOffset - ViewportWidth); } ////// Scroll content by one page to the right. /// public void PageRight() { SetHorizontalOffset(HorizontalOffset + ViewportWidth); } ////// Scroll content by one page to the top. /// public void MouseWheelUp() { SetVerticalOffset(VerticalOffset - SystemParameters.WheelScrollLines * ((Orientation == Orientation.Vertical) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one page to the bottom. /// public void MouseWheelDown() { SetVerticalOffset(VerticalOffset + SystemParameters.WheelScrollLines * ((Orientation == Orientation.Vertical) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one page to the left. /// public void MouseWheelLeft() { SetHorizontalOffset(HorizontalOffset - 3.0 * ((Orientation == Orientation.Horizontal) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Scroll content by one page to the right. /// public void MouseWheelRight() { SetHorizontalOffset(HorizontalOffset + 3.0 * ((Orientation == Orientation.Horizontal) ? 1.0 : ScrollViewer._scrollLineDelta)); } ////// Set the HorizontalOffset to the passed value. /// public void SetHorizontalOffset(double offset) { EnsureScrollData(); double scrollX = ScrollContentPresenter.ValidateInputOffset(offset, "HorizontalOffset"); if (!DoubleUtil.AreClose(scrollX, _scrollData._offset.X)) { _scrollData._offset.X = scrollX; InvalidateMeasure(); } } ////// Set the VerticalOffset to the passed value. /// public void SetVerticalOffset(double offset) { EnsureScrollData(); double scrollY = ScrollContentPresenter.ValidateInputOffset(offset, "VerticalOffset"); if (!DoubleUtil.AreClose(scrollY, _scrollData._offset.Y)) { _scrollData._offset.Y = scrollY; InvalidateMeasure(); } } ////// StackPanel implementation of // The goal is to change offsets to bring the child into view, and return a rectangle in our space to make visible. // The rectangle we return is in the physical dimension the input target rect transformed into our pace. // In the logical dimension, it is our immediate child's rect. // Note: This code presently assumes we/children are layout clean. See work item 22269 for more detail. public Rect MakeVisible(Visual visual, Rect rectangle) { Vector newOffset = new Vector(); Rect newRect = new Rect(); // We can only work on visuals that are us or children. // An empty rect has no size or position. We can't meaningfully use it. if ( rectangle.IsEmpty || visual == null || visual == (Visual)this || !this.IsAncestorOf(visual)) { return Rect.Empty; } #pragma warning disable 1634, 1691 #pragma warning disable 56506 // Compute the child's rect relative to (0,0) in our coordinate space. // This is a false positive by PreSharp. visual cannot be null because of the 'if' check above GeneralTransform childTransform = visual.TransformToAncestor(this); #pragma warning restore 56506 #pragma warning restore 1634, 1691 rectangle = childTransform.TransformBounds(rectangle); // We can't do any work unless we're scrolling. if (!IsScrolling) { return rectangle; } // Bring the target rect into view in the physical dimension. MakeVisiblePhysicalHelper(rectangle, ref newOffset, ref newRect); // Bring our child containing the visual into view. int childIndex = FindChildIndexThatParentsVisual(visual); MakeVisibleLogicalHelper(childIndex, ref newOffset, ref newRect); // We have computed the scrolling offsets; validate and scroll to them. newOffset.X = ScrollContentPresenter.CoerceOffset(newOffset.X, _scrollData._extent.Width, _scrollData._viewport.Width); newOffset.Y = ScrollContentPresenter.CoerceOffset(newOffset.Y, _scrollData._extent.Height, _scrollData._viewport.Height); if (!DoubleUtil.AreClose(newOffset, _scrollData._offset)) { _scrollData._offset = newOffset; InvalidateMeasure(); OnScrollChange(); } // Return the rectangle return newRect; } #endregion #endregion //------------------------------------------------------------------- // // Public Properties // //-------------------------------------------------------------------- #region Public Properties ///. /// /// Specifies dimension of children stacking. /// public Orientation Orientation { get { return (Orientation) GetValue(OrientationProperty); } set { SetValue(OrientationProperty, value); } } ////// DependencyProperty for public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register( "Orientation", typeof(Orientation), typeof(StackPanel), new FrameworkPropertyMetadata( Orientation.Vertical, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnOrientationChanged)), new ValidateValueCallback(ScrollBar.IsValidOrientation)); ///property. /// /// This property is always true because this panel has vertical or horizontal orientation /// protected internal override bool HasLogicalOrientation { get { return true; } } ////// Orientation of the panel if its layout is in one dimension. /// Otherwise HasLogicalOrientation is false and LogicalOrientation should be ignored /// protected internal override Orientation LogicalOrientation { get { return this.Orientation; } } //----------------------------------------------------------- // IScrollInfo Properties //----------------------------------------------------------- #region IScrollInfo Properties ////// StackPanel reacts to this property by changing it's child measurement algorithm. /// If scrolling in a dimension, infinite space is allowed the child; otherwise, available size is preserved. /// [DefaultValue(false)] public bool CanHorizontallyScroll { get { if (_scrollData == null) { return false; } return _scrollData._allowHorizontal; } set { EnsureScrollData(); if (_scrollData._allowHorizontal != value) { _scrollData._allowHorizontal = value; InvalidateMeasure(); } } } ////// StackPanel reacts to this property by changing it's child measurement algorithm. /// If scrolling in a dimension, infinite space is allowed the child; otherwise, available size is preserved. /// [DefaultValue(false)] public bool CanVerticallyScroll { get { if (_scrollData == null) { return false; } return _scrollData._allowVertical; } set { EnsureScrollData(); if (_scrollData._allowVertical != value) { _scrollData._allowVertical = value; InvalidateMeasure(); } } } ////// ExtentWidth contains the horizontal size of the scrolled content element in 1/96" /// public double ExtentWidth { get { if (_scrollData == null) { return 0.0; } return _scrollData._extent.Width; } } ////// ExtentHeight contains the vertical size of the scrolled content element in 1/96" /// public double ExtentHeight { get { if (_scrollData == null) { return 0.0; } return _scrollData._extent.Height; } } ////// ViewportWidth contains the horizontal size of content's visible range in 1/96" /// public double ViewportWidth { get { if (_scrollData == null) { return 0.0; } return _scrollData._viewport.Width; } } ////// ViewportHeight contains the vertical size of content's visible range in 1/96" /// public double ViewportHeight { get { if (_scrollData == null) { return 0.0; } return _scrollData._viewport.Height; } } ////// HorizontalOffset is the horizontal offset of the scrolled content in 1/96". /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public double HorizontalOffset { get { if (_scrollData == null) { return 0.0; } return _scrollData._computedOffset.X; } } ////// VerticalOffset is the vertical offset of the scrolled content in 1/96". /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public double VerticalOffset { get { if (_scrollData == null) { return 0.0; } return _scrollData._computedOffset.Y; } } ////// ScrollOwner is the container that controls any scrollbars, headers, etc... that are dependant /// on this IScrollInfo's properties. /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ScrollViewer ScrollOwner { get { EnsureScrollData(); return _scrollData._scrollOwner; } set { EnsureScrollData(); if (value != _scrollData._scrollOwner) { ResetScrolling(this); _scrollData._scrollOwner = value; } } } #endregion IScrollInfo Properties #endregion Public Properties //------------------------------------------------------------------- // // Protected Methods // //-------------------------------------------------------------------- #region Protected Methods ////// General StackPanel layout behavior is to grow unbounded in the "stacking" direction (Size To Content). /// Children in this dimension are encouraged to be as large as they like. In the other dimension, /// StackPanel will assume the maximum size of its children. /// ////// When scrolling, StackPanel will not grow in layout size but effectively add the children on a z-plane which /// will probably be clipped by some parent (typically a ScrollContentPresenter) to Stack's size. /// /// Constraint ///Desired size protected override Size MeasureOverride(Size constraint) { #if Profiling if (Panel.IsAboutToGenerateContent(this)) return MeasureOverrideProfileStub(constraint); else return RealMeasureOverride(constraint); } // this is a handy place to start/stop profiling private Size MeasureOverrideProfileStub(Size constraint) { return RealMeasureOverride(constraint); } private Size RealMeasureOverride(Size constraint) { #endif UIElementCollection children = InternalChildren; Size stackDesiredSize = new Size(); Size layoutSlotSize = constraint; bool fHorizontal = (Orientation == Orientation.Horizontal); int firstViewport; // First child index in the viewport. int lastViewport = -1; // Last child index in the viewport. -1 indicates we have not yet iterated through the last child. double logicalVisibleSpace, childLogicalSize; bool etwTracingEnabled = IsScrolling && EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal); if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.StartEvent, "STACK:MeasureOverride"); } // // Initialize child sizing and iterator data // Allow children as much size as they want along the stack. // if (fHorizontal) { layoutSlotSize.Width = Double.PositiveInfinity; if (IsScrolling && CanVerticallyScroll) { layoutSlotSize.Height = Double.PositiveInfinity; } firstViewport = (IsScrolling) ? CoerceOffsetToInteger(_scrollData._offset.X, children.Count) : 0; logicalVisibleSpace = constraint.Width; } else { layoutSlotSize.Height = Double.PositiveInfinity; if (IsScrolling && CanHorizontallyScroll) { layoutSlotSize.Width = Double.PositiveInfinity; } firstViewport = (IsScrolling) ? CoerceOffsetToInteger(_scrollData._offset.Y, children.Count) : 0; logicalVisibleSpace = constraint.Height; } // // Iterate through children. // While we still supported virtualization, this was hidden in a child iterator (see source history). // for (int i = 0, count = children.Count; i < count; ++i) { // Get next child. UIElement child = children[i]; if (child == null) { continue; } // Measure the child. child.Measure(layoutSlotSize); Size childDesiredSize = child.DesiredSize; // Accumulate child size. if (fHorizontal) { stackDesiredSize.Width += childDesiredSize.Width; stackDesiredSize.Height = Math.Max(stackDesiredSize.Height, childDesiredSize.Height); childLogicalSize = childDesiredSize.Width; } else { stackDesiredSize.Width = Math.Max(stackDesiredSize.Width, childDesiredSize.Width); stackDesiredSize.Height += childDesiredSize.Height; childLogicalSize = childDesiredSize.Height; } // Adjust remaining viewport space if we are scrolling and within the viewport region. // While scrolling (not virtualizing), we always measure children before and after the viewport. if (IsScrolling && lastViewport == -1 && i >= firstViewport) { logicalVisibleSpace -= childLogicalSize; if (DoubleUtil.LessThanOrClose(logicalVisibleSpace, 0.0)) { lastViewport = i; } } } // // Compute Scrolling stuff. // if (IsScrolling) { // Compute viewport and extent. Size viewport = constraint; Size extent = stackDesiredSize; Vector offset = _scrollData._offset; // If we have not yet set the last child in the viewport, set it to the last child. if (lastViewport == -1) { lastViewport = children.Count - 1; } // If we or children have resized, it's possible that we can now display more content. // This is true if we started at a nonzero offeset and still have space remaining. // In this case, we loop back through previous children until we run out of space. while (firstViewport > 0) { double projectedLogicalVisibleSpace = logicalVisibleSpace; if (fHorizontal) { projectedLogicalVisibleSpace -= children[firstViewport - 1].DesiredSize.Width; } else { projectedLogicalVisibleSpace -= children[firstViewport - 1].DesiredSize.Height; } // If we have run out of room, break. if (DoubleUtil.LessThan(projectedLogicalVisibleSpace, 0.0)) { break; } // Adjust viewport firstViewport--; logicalVisibleSpace = projectedLogicalVisibleSpace; } int logicalExtent = children.Count; int logicalViewport = lastViewport - firstViewport; // We are conservative when estimating a viewport, not including the last element in case it is only partially visible. // We want to count it if it is fully visible (>= 0 space remaining) or the only element in the viewport. if (logicalViewport == 0 || DoubleUtil.GreaterThanOrClose(logicalVisibleSpace, 0.0)) { logicalViewport++; } if (fHorizontal) { _scrollData._physicalViewport = viewport.Width; viewport.Width = logicalViewport; extent.Width = logicalExtent; offset.X = firstViewport; offset.Y = Math.Max(0, Math.Min(offset.Y, extent.Height - viewport.Height)); } else { _scrollData._physicalViewport = viewport.Height; viewport.Height = logicalViewport; extent.Height = logicalExtent; offset.Y = firstViewport; offset.X = Math.Max(0, Math.Min(offset.X, extent.Width - viewport.Width)); } // Since we can offset and clip our content, we never need to be larger than the parent suggestion. // If we returned the full size of the content, we would always be so big we didn't need to scroll. :) stackDesiredSize.Width = Math.Min(stackDesiredSize.Width, constraint.Width); stackDesiredSize.Height = Math.Min(stackDesiredSize.Height, constraint.Height); // Verify Scroll Info, invalidate ScrollOwner if necessary. VerifyScrollingData(viewport, extent, offset); } if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.EndEvent, "STACK:MeasureOverride"); } return stackDesiredSize; } ////// Content arrangement. /// /// Arrange size protected override Size ArrangeOverride(Size arrangeSize) { UIElementCollection children = this.Children; bool fHorizontal = (Orientation == Orientation.Horizontal); Rect rcChild = new Rect(arrangeSize); double previousChildSize = 0.0; bool etwTracingEnabled = IsScrolling && EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal); if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.StartEvent, "STACK:ArrangeOverride"); } // // Compute scroll offset and seed it into rcChild. // if (IsScrolling) { if (fHorizontal) { rcChild.X = ComputePhysicalFromLogicalOffset(_scrollData._computedOffset.X, true); rcChild.Y = -1.0 * _scrollData._computedOffset.Y; } else { rcChild.X = -1.0 * _scrollData._computedOffset.X; rcChild.Y = ComputePhysicalFromLogicalOffset(_scrollData._computedOffset.Y, false); } } // // Arrange and Position Children. // for (int i = 0, count = children.Count; i < count; ++i) { UIElement child = (UIElement)children[i]; if (child == null) { continue; } if (fHorizontal) { rcChild.X += previousChildSize; previousChildSize = child.DesiredSize.Width; rcChild.Width = previousChildSize; rcChild.Height = Math.Max(arrangeSize.Height, child.DesiredSize.Height); } else { rcChild.Y += previousChildSize; previousChildSize = child.DesiredSize.Height; rcChild.Height = previousChildSize; rcChild.Width = Math.Max(arrangeSize.Width, child.DesiredSize.Width); } child.Arrange(rcChild); } if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.EndEvent, "STACK:ArrangeOverride"); } return arrangeSize; } #endregion Protected Methods //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods private void EnsureScrollData() { if (_scrollData == null) { _scrollData = new ScrollData(); } } private static void ResetScrolling(StackPanel element) { element.InvalidateMeasure(); // Clear scrolling data. Because of thrash (being disconnected & reconnected, &c...), we may if (element.IsScrolling) { element._scrollData.ClearLayout(); } } // OnScrollChange is an override called whenever the IScrollInfo exposed scrolling state changes on this element. // At the time this method is called, scrolling state is in its new, valid state. private void OnScrollChange() { if (ScrollOwner != null) { ScrollOwner.InvalidateScrollInfo(); } } private void VerifyScrollingData(Size viewport, Size extent, Vector offset) { bool fValid = true; Debug.Assert(IsScrolling); fValid &= DoubleUtil.AreClose(viewport, _scrollData._viewport); fValid &= DoubleUtil.AreClose(extent, _scrollData._extent); fValid &= DoubleUtil.AreClose(offset, _scrollData._computedOffset); _scrollData._offset = offset; if (!fValid) { _scrollData._viewport = viewport; _scrollData._extent = extent; _scrollData._computedOffset = offset; OnScrollChange(); } } // Translates a logical (child index) offset to a physical (1/96") when scrolling. // If virtualizing, it makes the assumption that the logicalOffset is always the first in the visual collection // and thus returns 0. // If not virtualizing, it assumes that children are Measure clean; should only be called after running Measure. private double ComputePhysicalFromLogicalOffset(double logicalOffset, bool fHorizontal) { double physicalOffset = 0.0; UIElementCollection children = this.Children; Debug.Assert(logicalOffset == 0 || (logicalOffset > 0 && logicalOffset < children.Count)); for (int i = 0; i < logicalOffset; i++) { physicalOffset -= (fHorizontal) ? ((UIElement)children[i]).DesiredSize.Width : ((UIElement)children[i]).DesiredSize.Height; } return physicalOffset; } private int FindChildIndexThatParentsVisual(Visual child) { DependencyObject dependencyObjectChild = child; DependencyObject parent = VisualTreeHelper.GetParent(child); while (parent != this) { dependencyObjectChild = parent; parent = VisualTreeHelper.GetParent(dependencyObjectChild); if (parent == null) { throw new ArgumentException(SR.Get(SRID.Stack_VisualInDifferentSubTree),"child"); } } UIElementCollection children = this.Children; //The Downcast is ok because StackPanel's //child has to be a UIElement to be in this.Children collection return (children.IndexOf((UIElement)dependencyObjectChild)); } private void MakeVisiblePhysicalHelper(Rect r, ref Vector newOffset, ref Rect newRect) { double viewportOffset; double viewportSize; double targetRectOffset; double targetRectSize; double minPhysicalOffset; bool fHorizontal = (Orientation == Orientation.Horizontal); if (fHorizontal) { viewportOffset = _scrollData._computedOffset.Y; viewportSize = ViewportHeight; targetRectOffset = r.Y; targetRectSize = r.Height; } else { viewportOffset = _scrollData._computedOffset.X; viewportSize = ViewportWidth; targetRectOffset = r.X; targetRectSize = r.Width; } targetRectOffset += viewportOffset; minPhysicalOffset = ScrollContentPresenter.ComputeScrollOffsetWithMinimalScroll( viewportOffset, viewportOffset + viewportSize, targetRectOffset, targetRectOffset + targetRectSize); // Compute the visible rectangle of the child relative to the viewport. double left = Math.Max(targetRectOffset, minPhysicalOffset); targetRectSize = Math.Max(Math.Min(targetRectSize + targetRectOffset, minPhysicalOffset + viewportSize) - left, 0); targetRectOffset = left; targetRectOffset -= viewportOffset; if (fHorizontal) { newOffset.Y = minPhysicalOffset; newRect.Y = targetRectOffset; newRect.Height = targetRectSize; } else { newOffset.X = minPhysicalOffset; newRect.X = targetRectOffset; newRect.Width = targetRectSize; } } private void MakeVisibleLogicalHelper(int childIndex, ref Vector newOffset, ref Rect newRect) { bool fHorizontal = (Orientation == Orientation.Horizontal); int firstChildInView; int newFirstChild; int viewportSize; double childOffsetWithinViewport = 0; if (fHorizontal) { firstChildInView = (int)_scrollData._computedOffset.X; viewportSize = (int)_scrollData._viewport.Width; } else { firstChildInView = (int)_scrollData._computedOffset.Y; viewportSize = (int)_scrollData._viewport.Height; } newFirstChild = firstChildInView; // If the target child is before the current viewport, move the viewport to put the child at the top. if (childIndex < firstChildInView) { newFirstChild = childIndex; } // If the target child is after the current viewport, move the viewport to put the child at the bottom. else if (childIndex > firstChildInView + viewportSize - 1) { Size childDesiredSize = InternalChildren[childIndex].DesiredSize; double nextChildSize = ((fHorizontal) ? childDesiredSize.Width : childDesiredSize.Height); double viewportSpace = _scrollData._physicalViewport - nextChildSize; int i = childIndex; while (i > 0 && DoubleUtil.GreaterThanOrClose(viewportSpace, 0.0)) { i--; childDesiredSize = InternalChildren[i].DesiredSize; nextChildSize = ((fHorizontal) ? childDesiredSize.Width : childDesiredSize.Height); childOffsetWithinViewport += nextChildSize; viewportSpace -= nextChildSize; } if (i != childIndex && DoubleUtil.LessThan(viewportSpace, 0.0)) { childOffsetWithinViewport -= nextChildSize; i++; } newFirstChild = i; } if (fHorizontal) { newOffset.X = newFirstChild; newRect.X = childOffsetWithinViewport; newRect.Width = InternalChildren[childIndex].DesiredSize.Width; } else { newOffset.Y = newFirstChild; newRect.Y = childOffsetWithinViewport; newRect.Height = InternalChildren[childIndex].DesiredSize.Height; } } static private int CoerceOffsetToInteger(double offset, int numberOfItems) { int iNewOffset; if (Double.IsNegativeInfinity(offset)) { iNewOffset = 0; } else if (Double.IsPositiveInfinity(offset)) { iNewOffset = numberOfItems - 1; } else { iNewOffset = (int)offset; iNewOffset = Math.Max(Math.Min(numberOfItems - 1, iNewOffset), 0); } return iNewOffset; } //------------------------------------------------------------ // Avalon Property Callbacks/Overrides //----------------------------------------------------------- #region Avalon Property Callbacks/Overrides ////// private static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // Since Orientation is so essential to logical scrolling/virtualization, we synchronously check if // the new value is different and clear all scrolling data if so. ResetScrolling(d as StackPanel); } #endregion #endregion Private Methods //------------------------------------------------------ // // Private Properties // //----------------------------------------------------- #region Private Properties private bool IsScrolling { get { return (_scrollData != null) && (_scrollData._scrollOwner != null); } } // // This property // 1. Finds the correct initial size for the _effectiveValues store on the current DependencyObject // 2. This is a performance optimization // internal override int EffectiveValuesInitialSize { get { return 9; } } #endregion Private Properties //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- #region Private Fields // Logical scrolling and virtualization data. private ScrollData _scrollData; #endregion Private Fields //------------------------------------------------------ // // Private Structures / Classes // //----------------------------------------------------- #region Private Structures Classes //------------------------------------------------------------ // ScrollData class //------------------------------------------------------------ #region ScrollData // Helper class to hold scrolling data. // This class exists to reduce working set when StackPanel is used outside a scrolling situation. // Standard "extra pointer always for less data sometimes" cache savings model: // !Scroll [1xReference] // Scroll [1xReference] + [6xDouble + 1xReference] private class ScrollData { // Clears layout generated data. // Does not clear scrollOwner, because unless resetting due to a scrollOwner change, we won't get reattached. internal void ClearLayout() { _offset = new Vector(); _viewport = _extent = new Size(); _physicalViewport = 0; } // For Stack/Flow, the two dimensions of properties are in different units: // 1. The "logically scrolling" dimension uses items as units. // 2. The other dimension physically scrolls. Units are in Avalon pixels (1/96"). internal bool _allowHorizontal; internal bool _allowVertical; internal Vector _offset; // Scroll offset of content. Positive corresponds to a visually upward offset. internal Vector _computedOffset = new Vector(0,0); internal Size _viewport; // ViewportSize is in {pixels x items} (or vice-versa). internal Size _extent; // Extent is the total number of children (logical dimension) or physical size internal double _physicalViewport; // The physical size of the viewport for the items dimension above. internal ScrollViewer _scrollOwner; // ScrollViewer to which we're attached. } #endregion ScrollData #endregion Private Structures Classes } } // 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
- DrawingAttributes.cs
- InvalidFilterCriteriaException.cs
- TemplatedWizardStep.cs
- StubHelpers.cs
- FontWeight.cs
- SR.Designer.cs
- DataControlButton.cs
- GridViewRowEventArgs.cs
- PageBreakRecord.cs
- SelectionItemPattern.cs
- WizardStepBase.cs
- ArrayElementGridEntry.cs
- ProfilePropertyNameValidator.cs
- CatalogPartCollection.cs
- SatelliteContractVersionAttribute.cs
- DesignerDataConnection.cs
- ObjectRef.cs
- ComNativeDescriptor.cs
- DataGridViewCellParsingEventArgs.cs
- SuppressMergeCheckAttribute.cs
- BooleanSwitch.cs
- ModulesEntry.cs
- SendingRequestEventArgs.cs
- WindowsGraphicsCacheManager.cs
- PackageRelationship.cs
- translator.cs
- GeneralTransform3DTo2DTo3D.cs
- CallbackWrapper.cs
- EmptyImpersonationContext.cs
- ControlUtil.cs
- GlobalizationSection.cs
- TraversalRequest.cs
- UntypedNullExpression.cs
- IisTraceWebEventProvider.cs
- AppSettingsExpressionBuilder.cs
- PackageController.cs
- ClusterRegistryConfigurationProvider.cs
- FunctionQuery.cs
- EditingCommands.cs
- ScriptingWebServicesSectionGroup.cs
- SuppressIldasmAttribute.cs
- UrlMapping.cs
- SqlMetaData.cs
- KeyboardEventArgs.cs
- SystemInformation.cs
- MapPathBasedVirtualPathProvider.cs
- SmiXetterAccessMap.cs
- SqlPersonalizationProvider.cs
- HandlerFactoryCache.cs
- QueryableFilterRepeater.cs
- DatagridviewDisplayedBandsData.cs
- ToolStripItemTextRenderEventArgs.cs
- DocumentApplication.cs
- ExpressionBindingCollection.cs
- EntitySqlQueryBuilder.cs
- Converter.cs
- Form.cs
- AffineTransform3D.cs
- SelectionUIHandler.cs
- Validator.cs
- ActivityInterfaces.cs
- SrgsRule.cs
- PenCursorManager.cs
- DataBinding.cs
- CanonicalXml.cs
- DCSafeHandle.cs
- TableLayout.cs
- RegexInterpreter.cs
- ElementProxy.cs
- AdapterDictionary.cs
- unsafeIndexingFilterStream.cs
- RawUIStateInputReport.cs
- PointAnimationClockResource.cs
- CqlBlock.cs
- Visitor.cs
- XmlSortKeyAccumulator.cs
- Parser.cs
- WindowsListBox.cs
- DataSourceHelper.cs
- FamilyMapCollection.cs
- JsonEnumDataContract.cs
- DbMetaDataColumnNames.cs
- RoutedUICommand.cs
- CqlBlock.cs
- Figure.cs
- SpellerStatusTable.cs
- XmlILIndex.cs
- MdImport.cs
- TrackingConditionCollection.cs
- SafePointer.cs
- BinaryUtilClasses.cs
- IteratorFilter.cs
- DisableDpiAwarenessAttribute.cs
- columnmapfactory.cs
- Propagator.JoinPropagator.JoinPredicateVisitor.cs
- EventLogger.cs
- COAUTHIDENTITY.cs
- activationcontext.cs
- ApplicationGesture.cs
- BamlLocalizableResource.cs