Stack.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / 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  property.
        ///  
        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));

        /// 
        /// 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  property.
        ///  
        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));

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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK