VirtualizingStackPanel.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Controls / VirtualizingStackPanel.cs / 4 / VirtualizingStackPanel.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//--------------------------------------------------------------------------- 

//#define Profiling 
 
using MS.Internal;
using MS.Utility; 

using System;
using System.Collections;
using System.Collections.Generic; 
using System.Collections.Specialized;
using System.ComponentModel; 
using System.Diagnostics; 
using System.Windows.Controls.Primitives;
using System.Windows.Media; 
using System.Windows.Threading;
using System.Windows.Input;

namespace System.Windows.Controls 
{
    ///  
    /// VirtualizingStackPanel is used to arrange children into single line. 
    /// 
    public class VirtualizingStackPanel : VirtualizingPanel, IScrollInfo 
    {
        //-------------------------------------------------------------------
        //
        //  Constructors 
        //
        //------------------------------------------------------------------- 
 
        #region Constructors
 
        /// 
        /// Default constructor.
        /// 
        public VirtualizingStackPanel() 
        {
        } 
 
        #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();
            }
        }
 
        /// 
        /// VirtualizingStackPanel 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(); 
            Rect originalRect = rectangle; 

            // 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, rectangle, 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();
                if (ScrollOwner != null) 
                {
                    // When layout gets updated it may happen that visual is obscured by a ScrollBar 
                    // We call MakeVisible again to make sure element is visible in this case 
                    ScrollOwner.MakeVisible(visual, originalRect);
                } 
            }

            // Return the rectangle
            return newRect; 
        }
 
        ///  
        /// Generates the item at the specified index and calls BringIntoView on it.
        ///  
        /// Specify the item index that should become visible
        /// 
        /// Thrown if index is out of range
        ///  
        protected internal override void BringIndexIntoView(int index)
        { 
            UIElementCollection children = InternalChildren; 
            if (index < 0 || index >= ItemCount)
                throw new ArgumentOutOfRangeException("index"); 

            IItemContainerGenerator generator = Generator;
            int childIndex;
            GeneratorPosition position = IndexToGeneratorPositionForStart(index, out childIndex); 
            using (generator.StartAt(position, GeneratorDirection.Forward, true))
            { 
                bool newlyRealized; 
                UIElement child = generator.GenerateNext(out newlyRealized) as UIElement;
                if (child != null) 
                {
                    if (newlyRealized)
                    {
                        // A new container was generated, update the generated state 
                        if (childIndex >= children.Count)
                        { 
                            VirtualizingPanel.AddInternalChild(children, child); 
                        }
                        else 
                        {
                            VirtualizingPanel.InsertInternalChild(children, childIndex, child);
                            if (childIndex <= _firstVisibleChildIndex)
                            { 
                                _firstVisibleChildIndex++;
                            } 
                        } 
                        generator.PrepareItemContainer(child);
                    } 

                    FrameworkElement element = child as FrameworkElement;
                    if (element != null)
                    { 
                        element.BringIntoView();
                    } 
                } 
            }
        } 

        #endregion

        #endregion 

        //------------------------------------------------------------------- 
        // 
        //  Public Properties
        // 
        //--------------------------------------------------------------------

        #region Public Properties
 
        /// 
        /// Specifies dimension of children stacking. 
        ///  
        public Orientation Orientation
        { 
            get { return (Orientation) GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }
 
        /// 
        /// 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; }
        }

        ///  
        /// DependencyProperty for  property.
        ///  
        public static readonly DependencyProperty OrientationProperty = 
            DependencyProperty.Register("Orientation", typeof(Orientation), typeof(VirtualizingStackPanel),
                new FrameworkPropertyMetadata(Orientation.Vertical, 
                        FrameworkPropertyMetadataOptions.AffectsMeasure,
                        new PropertyChangedCallback(OnOrientationChanged)),
                new ValidateValueCallback(ScrollBar.IsValidOrientation));
 
        /// 
        ///     Attached property for use on the ItemsControl that is the host for the items being 
        ///     presented by this panel. Use this property to turn virtualization on/off. 
        /// 
        public static readonly DependencyProperty IsVirtualizingProperty = 
            DependencyProperty.RegisterAttached("IsVirtualizing", typeof(bool), typeof(VirtualizingStackPanel),
                new FrameworkPropertyMetadata(true));

        ///  
        ///     Retrieves the value for .
        ///  
        /// The object on which to query the value. 
        /// True if virtualizing, false otherwise.
        public static bool GetIsVirtualizing(DependencyObject o) 
        {
            if (o == null)
            {
                throw new ArgumentNullException("o"); 
            }
 
            return (bool)o.GetValue(IsVirtualizingProperty); 
        }
 
        /// 
        ///     Sets the value for .
        /// 
        /// The element on which to set the value. 
        /// True if virtualizing, false otherwise.
        public static void SetIsVirtualizing(DependencyObject element, bool value) 
        { 
            if (element == null)
            { 
                throw new ArgumentNullException("element");
            }

            element.SetValue(IsVirtualizingProperty, value); 
        }
 
 
        //-----------------------------------------------------------
        //  IScrollInfo Properties 
        //-----------------------------------------------------------
        #region IScrollInfo Properties

        ///  
        /// VirtualizingStackPanel 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(); 
                }
            } 
        } 

        ///  
        /// VirtualizingStackPanel 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
 
        //------------------------------------------------------------------- 
        //
        //  Public Events 
        //
        //--------------------------------------------------------------------

 
        #region Public Events
 
        ///  
        ///     Called on the ItemsControl that owns this panel when an item is being re-virtualized.
        ///  
        public static readonly RoutedEvent CleanUpVirtualizedItemEvent = EventManager.RegisterRoutedEvent("CleanUpVirtualizedItemEvent", RoutingStrategy.Direct, typeof(CleanUpVirtualizedItemEventHandler), typeof(VirtualizingStackPanel));


        ///  
        ///     Adds a handler for the CleanUpVirtualizedItem attached event
        ///  
        /// DependencyObject that listens to this event 
        /// Event Handler to be added
        public static void AddCleanUpVirtualizedItemHandler(DependencyObject element, CleanUpVirtualizedItemEventHandler handler) 
        {
            FrameworkElement.AddHandler(element, CleanUpVirtualizedItemEvent, handler);
        }
 
        /// 
        ///     Removes a handler for the CleanUpVirtualizedItem attached event 
        ///  
        /// DependencyObject that listens to this event
        /// Event Handler to be removed 
        public static void RemoveCleanUpVirtualizedItemHandler(DependencyObject element, CleanUpVirtualizedItemEventHandler handler)
        {
            FrameworkElement.RemoveHandler(element, CleanUpVirtualizedItemEvent, handler);
        } 

        ///  
        ///     Called when an item is being re-virtualized. 
        /// 
        protected virtual void OnCleanUpVirtualizedItem(CleanUpVirtualizedItemEventArgs e) 
        {
            ItemsControl itemsControl = ItemsControl.GetItemsOwner(this);

            if (itemsControl != null) 
            {
                itemsControl.RaiseEvent(e); 
            } 
        }
 
        #endregion

        //-------------------------------------------------------------------
        // 
        //  Protected Methods
        // 
        //-------------------------------------------------------------------- 

        #region Protected Methods 

        /// 
        /// General VirtualizingStackPanel 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, 
        /// VirtualizingStackPanel will assume the maximum size of its children.
        ///  
        ///  
        /// When scrolling, VirtualizingStackPanel 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, "VirtualizingStackPanel :MeasureOverride"); 
            }
 
            //
            // Collect information from the ItemsControl, if there is one.
            //
            IItemContainerGenerator generator = Generator; 
            ItemsControl itemsControl = ItemsControl.GetItemsOwner(this);
            int itemCount = (itemsControl != null) ? itemsControl.Items.Count : 0; 
            bool isVirtualizing = (itemsControl != null) ? GetIsVirtualizing(itemsControl) : true; 

            // 
            // 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, itemCount) : 0;
                logicalVisibleSpace = constraint.Width; 
            }
            else
            {
                layoutSlotSize.Height = Double.PositiveInfinity; 
                if (IsScrolling && CanHorizontallyScroll) { layoutSlotSize.Width = Double.PositiveInfinity; }
                firstViewport = (IsScrolling) ? CoerceOffsetToInteger(_scrollData._offset.Y, itemCount) : 0; 
                logicalVisibleSpace = constraint.Height; 
            }
 
            //
            // Figure out the position of the first visible item
            //
            GeneratorPosition startPos = IndexToGeneratorPositionForStart(isVirtualizing ? firstViewport : 0, out _firstVisibleChildIndex); 
            int childIndex = _firstVisibleChildIndex;
 
            bool ranOutOfItems = true; 
            _visibleCount = 0;
            if (itemCount > 0) 
            {
                _afterTrail = 0;
                using (generator.StartAt(startPos, GeneratorDirection.Forward, true))
                { 
                    for (int i = isVirtualizing ? firstViewport : 0, count = itemCount; i < count; ++i)
                    { 
                        // Get next child. 
                        bool newlyRealized;
                        UIElement child = generator.GenerateNext(out newlyRealized) as UIElement; 
                        if (child == null)
                        {
                            Debug.Assert(!newlyRealized, "The generator realized a null value.");
 
                            // We reached the end of the items (because of a group)
                            break; 
                        } 
                        if (!newlyRealized)
                        { 
                            Debug.Assert(child == children[childIndex], "Wrong child was generated");
                            Debug.Assert(GetGeneratedIndex(childIndex) == i, "Wrong index for previously generated child");
                        }
                        else 
                        {
                            Debug.Assert(child != null, "Null child was generated"); 
                            if (childIndex >= children.Count) 
                            {
                                VirtualizingPanel.AddInternalChild(children, child); 
                            }
                            else
                            {
                                VirtualizingPanel.InsertInternalChild(children, childIndex, child); 
                            }
                            generator.PrepareItemContainer(child); 
                        } 
                        childIndex++;
                        _visibleCount++; 

                        // Measure the child.
                        Size childDesiredSize = child.DesiredSize;
                        child.Measure(layoutSlotSize); 
                        if (childDesiredSize != child.DesiredSize)
                        { 
                            childDesiredSize = child.DesiredSize; 

                            // Reset the _maxDesiredSize cache if child DesiredSize changes 
                            if (_scrollData != null)
                                _scrollData._maxDesiredSize = new Size();
                        }
 

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

                        // When scrolling, virtualizing, and beyond the first element, 
                        // then stop creating elements when out of space.
                        if (IsScrolling && isVirtualizing && (i > firstViewport)) 
                        { 
                            // If begining of the item is outside the constraint
                            if ((fHorizontal && (stackDesiredSize.Width - childDesiredSize.Width > constraint.Width)) || 
                                (!fHorizontal && (stackDesiredSize.Height - childDesiredSize.Height > constraint.Height)))
                            {
                                // We want to keep a focusable item after the end so that keyboard navigation
                                // can work, but we want to limit that to FocusTrail number of items 
                                // in case all the items are not focusable.
                                _afterTrail++; 
                                if ((_afterTrail >= FocusTrail) || Keyboard.IsFocusable(child)) 
                                {
                                    // Either we past the limit or the child was focusable 
                                    ranOutOfItems = false;
                                    break;
                                }
                                // Loop around and generate another item 
                            }
                        } 
                    } 
                }
            } 

            _visibleStart = firstViewport;

            // 
            // Compute Scrolling stuff.
            // 
            if (IsScrolling) 
            {
                // Compute viewport and extent. 
                Size viewport = constraint;
                Vector offset = _scrollData._offset;

                bool accumulateExtent = false; 
                if (ScrollOwner != null)
                { 
                    accumulateExtent = ScrollOwner.InChildInvalidateMeasure; 
                    ScrollOwner.InChildInvalidateMeasure = false;
                } 

                Size extent = new Size();
                if (fHorizontal)
                { 
                    extent.Width = itemCount;
                    extent.Height = accumulateExtent ? Math.Max(stackDesiredSize.Height, _scrollData._extent.Height) : stackDesiredSize.Height; 
                } 
                else
                { 
                    extent.Width = accumulateExtent ? Math.Max(stackDesiredSize.Width, _scrollData._extent.Width) : stackDesiredSize.Width;
                    extent.Height = itemCount;
                }
 
                // If we have not yet set the last child in the viewport, set it to the last child.
                if (lastViewport == -1) { lastViewport = itemCount - 1; } 
 
                if (ranOutOfItems)
                { 
                    // 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.
                    childIndex = isVirtualizing ? _firstVisibleChildIndex : firstViewport; 
                    while (childIndex > 0)
                    { 
                        if (!PreviousChildIsGenerated(childIndex)) 
                        {
                            GeneratePreviousChild(childIndex, layoutSlotSize); 
                            childIndex++; // We just inserted a child, so increment the index
                        }
                        else if (childIndex <= _firstVisibleChildIndex)
                        { 
                            // This child has not been measured yet
                            children[childIndex - 1].Measure(layoutSlotSize); 
                        } 

                        double projectedLogicalVisibleSpace = logicalVisibleSpace; 
                        Size childDesiredSize = children[childIndex - 1].DesiredSize;

                        if (fHorizontal)
                        { 
                            projectedLogicalVisibleSpace -= childDesiredSize.Width;
                        } 
                        else 
                        {
                            projectedLogicalVisibleSpace -= childDesiredSize.Height; 
                        }

                        // If we have run out of room, break.
                        if (DoubleUtil.LessThan(projectedLogicalVisibleSpace, 0.0)) { break; } 

                        // Account for the child in the panel's desired size 
                        if (fHorizontal) 
                        {
                            stackDesiredSize.Width += childDesiredSize.Width; 
                            stackDesiredSize.Height = Math.Max(stackDesiredSize.Height, childDesiredSize.Height);
                        }
                        else
                        { 
                            stackDesiredSize.Width = Math.Max(stackDesiredSize.Width, childDesiredSize.Width);
                            stackDesiredSize.Height += childDesiredSize.Height; 
                        } 

                        // Adjust viewport 
                        childIndex--;
                        logicalVisibleSpace = projectedLogicalVisibleSpace;
                    }
                    if ((childIndex < _firstVisibleChildIndex) || !isVirtualizing) 
                    {
                        _firstVisibleChildIndex = childIndex; 
                    } 
                    _visibleStart = firstViewport = (GetGeneratedCount(children) == 0) ? 0 : GetGeneratedIndex(_firstVisibleChildIndex);
                } 
                EnsureTopCapGenerated(layoutSlotSize);

                int logicalExtent = itemCount;
                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; 
                    offset.X = firstViewport;
                    offset.Y = Math.Max(0, Math.Min(offset.Y, extent.Height - viewport.Height)); 
 
                    // In case last item is visible because we scroll all the way to the right and scrolling is on
                    // we want desired size not to be smaller than constraint to avoid another relayout 
                    if (logicalExtent > logicalViewport && !Double.IsPositiveInfinity(constraint.Width))
                    {
                        stackDesiredSize.Width = constraint.Width;
                    } 
                }
                else 
                { 
                    _scrollData._physicalViewport = viewport.Height;
                    viewport.Height = logicalViewport; 
                    offset.Y = firstViewport;
                    offset.X = Math.Max(0, Math.Min(offset.X, extent.Width - viewport.Width));

                    // In case last item is visible because we scroll all the way to the bottom and scrolling is on 
                    // we want desired size not to be smaller than constraint to avoid another relayout
                    if (logicalExtent > logicalViewport && !Double.IsPositiveInfinity(constraint.Height)) 
                    { 
                        stackDesiredSize.Height = constraint.Height;
                    } 
                }

                // 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); 
 
                // When scrolling, the maximum horizontal or vertical size of items can cause the desired size of the
                // panel to change, which can cause the owning ScrollViewer re-layout as well when it is not necessary. 
                // We will thus remember the maximum desired size and always return that. The actual arrangement and
                // clipping still be calculated from actual scroll data values.
                // The maximum desired size is reset when the items change.
                _scrollData._maxDesiredSize.Width = Math.Max(stackDesiredSize.Width, _scrollData._maxDesiredSize.Width); 
                _scrollData._maxDesiredSize.Height = Math.Max(stackDesiredSize.Height, _scrollData._maxDesiredSize.Height);
                stackDesiredSize = _scrollData._maxDesiredSize; 
 
                // Verify Scroll Info, invalidate ScrollOwner if necessary.
                VerifyScrollingData(viewport, extent, offset); 
            }

            if (isVirtualizing)
            { 
                EnsureCleanupOperation(false /* delay */);
            } 
 
            if (etwTracingEnabled)
            { 
                EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.EndEvent, "VirtualizingStackPanel :MeasureOverride");
            }

            return stackDesiredSize; 
        }
 
        private void EnsureCleanupOperation(bool delay) 
        {
            if (delay) 
            {
                bool noPendingOperations = true;
                if (_cleanupOperation != null)
                { 
                    noPendingOperations = _cleanupOperation.Abort();
                    if (noPendingOperations) 
                    { 
                        _cleanupOperation = null;
                    } 
                }
                if (noPendingOperations && (_cleanupDelay == null))
                {
                    _cleanupDelay = new DispatcherTimer(); 
                    _cleanupDelay.Tick += new EventHandler(OnDelayCleanup);
                    _cleanupDelay.Interval = TimeSpan.FromMilliseconds(500.0); 
                    _cleanupDelay.Start(); 
                }
            } 
            else
            {
                if ((_cleanupOperation == null) && (_cleanupDelay == null))
                { 
                    _cleanupOperation = Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(OnCleanUp), null);
                } 
            } 
        }
 
        private bool PreviousChildIsGenerated(int childIndex)
        {
            GeneratorPosition position = new GeneratorPosition(childIndex, 0);
            position = Generator.GeneratorPositionFromIndex(Generator.IndexFromGeneratorPosition(position) - 1); 
            return (position.Offset == 0 && position.Index >= 0);
        } 
 
        private UIElement GeneratePreviousChild(int childIndex, Size layoutSlotSize)
        { 
            int newIndex = Generator.IndexFromGeneratorPosition(new GeneratorPosition(childIndex, 0)) - 1;
            if (newIndex >= 0)
            {
                UIElement child; 
                IItemContainerGenerator generator = Generator;
                ItemsControl itemsControl = ItemsControl.GetItemsOwner(this); 
                UIElementCollection children = InternalChildren; 

                int newGeneratedIndex; 
                GeneratorPosition newStartPos = IndexToGeneratorPositionForStart(newIndex, out newGeneratedIndex);
                using (generator.StartAt(newStartPos, GeneratorDirection.Forward, true))
                {
                    bool newlyRealized; 
                    child = generator.GenerateNext(out newlyRealized) as UIElement;
 
                    Debug.Assert(child != null, "Null child was generated"); 
                    Debug.Assert(newlyRealized, "New child should have been realized");
 
                    VirtualizingPanel.InsertInternalChild(children, childIndex, child);

                    generator.PrepareItemContainer(child);
 
                    if (childIndex <= _firstVisibleChildIndex)
                    { 
                        _firstVisibleChildIndex++; 
                    }
 
                    child.Measure(layoutSlotSize);
                }

                return child; 
            }
 
            return null; 
        }
 
        /// 
        ///     Called when the Items collection associated with the containing ItemsControl changes.
        /// 
        /// sender 
        /// Event arguments
        protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args) 
        { 
            base.OnItemsChanged(sender, args);
 
            bool resetMaximumDesiredSize = false;

            switch (args.Action)
            { 
                case NotifyCollectionChangedAction.Remove:
                    OnItemsRemove(args); 
                    resetMaximumDesiredSize = true; 
                    break;
 
                case NotifyCollectionChangedAction.Replace:
                    OnItemsReplace(args);
                    resetMaximumDesiredSize = true;
                    break; 

                case NotifyCollectionChangedAction.Move: 
                    OnItemsMove(args); 
                    break;
 
                case NotifyCollectionChangedAction.Reset:
                    resetMaximumDesiredSize = true;
                    break;
            } 

            if (resetMaximumDesiredSize && IsScrolling) 
            { 
                // The items changed such that the maximum size may no longer be valid.
                // The next layout pass will update this value. 
                _scrollData._maxDesiredSize = new Size();
            }

        } 

        ///  
        ///     Called when the UI collection of children is cleared by the base Panel class. 
        /// 
        protected override void OnClearChildren() 
        {
            base.OnClearChildren();

            _visibleStart = _firstVisibleChildIndex = _visibleCount = 0; 
        }
 
        private void OnItemsRemove(ItemsChangedEventArgs args) 
        {
            RemoveChildRange(args.Position, args.ItemCount, args.ItemUICount); 
        }

        private void OnItemsReplace(ItemsChangedEventArgs args)
        { 
            RemoveChildRange(args.Position, args.ItemCount, args.ItemUICount);
        } 
 
        private void OnItemsMove(ItemsChangedEventArgs args)
        { 
            RemoveChildRange(args.OldPosition, args.ItemCount, args.ItemUICount);
        }

        private void RemoveChildRange(GeneratorPosition position, int itemCount, int itemUICount) 
        {
            UIElementCollection children = InternalChildren; 
            int pos = position.Index; 
            if (position.Offset > 0)
            { 
                // An item is being removed after the one at the index
                pos++;
            }
 
            if (pos < GetGeneratedCount(children))
            { 
                int uiCount = itemUICount; 
                Debug.Assert((itemCount == itemUICount) || (itemUICount == 0), "Both ItemUICount and ItemCount should be equal or ItemUICount should be 0.");
                if (uiCount > 0) 
                {
                    VirtualizingPanel.RemoveInternalChildRange(children, pos, uiCount);
                }
            } 
        }
 
 
        private GeneratorPosition IndexToGeneratorPositionForStart(int index, out int childIndex)
        { 
            IItemContainerGenerator generator = Generator;
            GeneratorPosition position = (generator != null) ? generator.GeneratorPositionFromIndex(index) : new GeneratorPosition(-1, index + 1);

            // determine the position in the children collection for the first 
            // generated container.  This assumes that generator.StartAt will be called
            // with direction=Forward and  allowStartAtRealizedItem=true. 
            childIndex = (position.Offset == 0) ? position.Index : position.Index + 1; 

            return position; 
        }

        private void OnDelayCleanup(object sender, EventArgs e)
        { 
            Debug.Assert(_cleanupDelay != null);
 
            bool needsMoreCleanup = false; 

            try 
            {
                needsMoreCleanup = CleanUp();
            }
            finally 
            {
                // Cleanup the timer if more cleanup is unnecessary 
                if (!needsMoreCleanup) 
                {
                    _cleanupDelay.Stop(); 
                    _cleanupDelay = null;
                }
            }
        } 

        private object OnCleanUp(object args) 
        { 
            Debug.Assert(_cleanupOperation != null);
 
            bool needsMoreCleanup = false;

            try
            { 
                needsMoreCleanup = CleanUp();
            } 
            finally 
            {
                // Keeping this non-null until here in case cleaning up causes re-entrancy 
                _cleanupOperation = null;
            }

            if (needsMoreCleanup) 
            {
                EnsureCleanupOperation(true /* delay */); 
            } 

            return null; 
        }

        private bool CleanUp()
        { 
            ItemsControl itemsControl = ItemsControl.GetItemsOwner(this);
            if ((itemsControl != null) && !GetIsVirtualizing(itemsControl)) 
            { 
                // Virtualization is turned off, no need to cleanup.
                return false; 
            }

            int startMilliseconds = Environment.TickCount;
            bool needsMoreCleanup = false; 
            UIElementCollection children = InternalChildren;
            int minDesiredGenerated = MinDesiredGenerated; 
            int maxDesiredGenerated = MaxDesiredGenerated; 
            int pageSize = maxDesiredGenerated - minDesiredGenerated;
            int extraChildren = GetGeneratedCount(children) - pageSize; 

            if (extraChildren > (pageSize * 2))
            {
                if ((Mouse.LeftButton == MouseButtonState.Pressed) && 
                    (extraChildren < 1000))
                { 
                    // An optimization for when we are dragging the mouse. 
                    needsMoreCleanup = true;
                } 
                else
                {
                    bool trailingFocus = IsKeyboardFocusWithin;
                    bool keepForwardTrail = false; 
                    int focusIndex = -1;
                    IItemContainerGenerator generator = Generator; 
 
                    int cleanupRangeStart = 0;
                    int cleanupCount = 0; 
                    int lastGeneratedIndex = -1;
                    int counterAdjust;

                    for (int i = 0; i < GetGeneratedCount(children); i++) 
                    {
                        // It is possible for TickCount to wrap around about every 30 days. 
                        // If that were to occur, then this particular cleanup may not be interrupted. 
                        // That is OK since the worst that can happen is that there is more of a stutter than normal.
                        int totalMilliseconds = Environment.TickCount - startMilliseconds; 
                        if ((totalMilliseconds > 50) && (cleanupCount > 0))
                        {
                            // Cleanup has been working for 50ms already and the user might start
                            // noticing a lag. Stop cleaning up and release the thread for other work. 
                            // Cleanup will continue later.
                            // Don't break out until after at least one item has been found to cleanup. 
                            // Otherwise, we might end up in an infinite loop. 
                            needsMoreCleanup = true;
                            break; 
                        }

                        int childIndex = i;
                        if (trailingFocus) 
                        {
                            // Focus lies somewhere within the panel, but it has not been found yet. 
                            UIElement child = children[i]; 
                            if (child.IsKeyboardFocusWithin)
                            { 
                                // Focus has been found, we can now re-virtualize items before the focus.
                                trailingFocus = false;
                                keepForwardTrail = true;
                                focusIndex = i; 
                                if (i > 0)
                                { 
                                    // Go through the trailing items and find a focusable item to keep. 
                                    int trailIndex = i - 1;
                                    int end = Math.Max(0, i - FocusTrail); 
                                    for (; trailIndex >= end; trailIndex--)
                                    {
                                        child = children[trailIndex];
                                        if (Keyboard.IsFocusable(child)) 
                                        {
                                            trailIndex--; 
                                            break; 
                                        }
                                    } 

                                    // The rest of the trailing items can be re-virtualized.
                                    for (childIndex = end; childIndex <= trailIndex; childIndex++)
                                    { 
                                        ManageCleanup(
                                            children, 
                                            itemsControl, 
                                            generator,
                                            childIndex, 
                                            minDesiredGenerated,
                                            maxDesiredGenerated,
                                            ref childIndex,
                                            ref cleanupRangeStart, 
                                            ref cleanupCount,
                                            ref lastGeneratedIndex, 
                                            out counterAdjust); 
                                        if (counterAdjust > 0)
                                        { 
                                            i -= counterAdjust;
                                            trailIndex -= counterAdjust;
                                        }
                                    } 

                                    if (cleanupCount > 0) 
                                    { 
                                        // Cleanup the last batch for the focused item
                                        CleanupRange(children, generator, cleanupRangeStart, cleanupCount); 
                                        i -= cleanupCount;
                                        cleanupCount = 0;
                                    }
                                    cleanupRangeStart = i + 1; 

                                    // At this point, we are caught up and should go to the next item 
                                    continue; 
                                }
                            } 
                            else if (i >= FocusTrail)
                            {
                                childIndex = i - FocusTrail;
                            } 
                            else
                            { 
                                continue; 
                            }
                        } 

                        if (keepForwardTrail)
                        {
                            // Find a focusable item after the focused item to keep 
                            if (childIndex <= (focusIndex + FocusTrail))
                            { 
                                UIElement child = children[childIndex]; 
                                if (Keyboard.IsFocusable(child))
                                { 
                                    // A focusable item was found, all items after this one can be re-virtualized
                                    keepForwardTrail = false;
                                    cleanupRangeStart = childIndex + 1;
                                    cleanupCount = 0; 
                                }
                                continue; 
                            } 
                            else
                            { 
                                keepForwardTrail = false;
                            }
                        }
 
                        ManageCleanup(
                            children, 
                            itemsControl, 
                            generator,
                            childIndex, 
                            minDesiredGenerated,
                            maxDesiredGenerated,
                            ref i,
                            ref cleanupRangeStart, 
                            ref cleanupCount,
                            ref lastGeneratedIndex, 
                            out counterAdjust); 
                    }
 
                    if (cleanupCount > 0)
                    {
                        // Cleanup the final batch
                        CleanupRange(children, generator, cleanupRangeStart, cleanupCount); 
                    }
                } 
            } 

            return needsMoreCleanup; 
        }

        private void ManageCleanup(
            UIElementCollection children, 
            ItemsControl itemsControl,
            IItemContainerGenerator generator, 
            int childIndex, 
            int minDesiredGenerated,
            int maxDesiredGenerated, 
            ref int counter,
            ref int cleanupRangeStart,
            ref int cleanupCount,
            ref int lastGeneratedIndex, 
            out int counterAdjust)
        { 
            counterAdjust = 0; 
            bool performCleanup = false;
            bool countThisChild = false; 
            int generatedIndex = GetGeneratedIndex(childIndex);

            if (OutsideMinMax(generatedIndex, minDesiredGenerated, maxDesiredGenerated) &&
                NotifyCleanupItem(childIndex, children, itemsControl, generator)) 
            {
                // The item can be re-virtualized. 
                if ((generatedIndex - lastGeneratedIndex) == 1) 
                {
                    // Add another to the current batch. 
                    cleanupCount++;
                }
                else
                { 
                    // There was a gap in generated items. Cleanup any from the previous batch.
                    performCleanup = countThisChild = true; 
                } 
            }
            else 
            {
                // The item cannot be re-virtualized. Cleanup any from the previous batch.
                performCleanup = true;
            } 

            if (performCleanup) 
            { 
                // Cleanup a batch of items
                if (cleanupCount > 0) 
                {
                    CleanupRange(children, generator, cleanupRangeStart, cleanupCount);
                    counterAdjust = cleanupCount;
                    counter -= counterAdjust; 
                    childIndex -= counterAdjust;
                    cleanupCount = 0; 
                } 

                if (countThisChild) 
                {
                    // The current child was not included in the batch and should be saved for later
                    cleanupRangeStart = childIndex;
                    cleanupCount = 1; 
                }
                else 
                { 
                    // The next child will start the next batch.
                    cleanupRangeStart = childIndex + 1; 
                }
            }
            lastGeneratedIndex = generatedIndex;
        } 

        private bool NotifyCleanupItem(int childIndex, UIElementCollection children, ItemsControl itemsControl, IItemContainerGenerator generator) 
        { 
            UIElement child = children[childIndex];
 
            CleanUpVirtualizedItemEventArgs e = new CleanUpVirtualizedItemEventArgs(itemsControl.ItemContainerGenerator.ItemFromContainer(child), child);
            e.Source = this;
            OnCleanUpVirtualizedItem(e);
 
            return !e.Cancel;
        } 
 
        private void CleanupRange(UIElementCollection children, IItemContainerGenerator generator, int startIndex, int count)
        { 
            // Remove the desired range of children
            VirtualizingPanel.RemoveInternalChildRange(children, startIndex, count);
            generator.Remove(new GeneratorPosition(startIndex, 0), count);
 
            // Update the index of the first visible generated child
            if (startIndex < _firstVisibleChildIndex) 
            { 
                int endIndex = startIndex + count - 1;
                if (endIndex < _firstVisibleChildIndex) 
                {
                    // The first visible index is after the items that were removed
                    _firstVisibleChildIndex -= count;
                } 
                else
                { 
                    // The first visible index was within the items that were removed 
                    _firstVisibleChildIndex = startIndex;
                } 
            }
        }

        private static bool OutsideMinMax(int i, int min, int max) 
        {
            return ((i < min) || (i > max)); 
        } 

        private void EnsureTopCapGenerated(Size layoutSlotSize) 
        {
            // Ensure that a focusable item is generated above the first visible item
            // so that keyboard navigation works.
 
            _beforeTrail = 0;
            if (_visibleStart > 0) 
            { 
                int childIndex = _firstVisibleChildIndex;
                UIElementCollection children = InternalChildren; 
                UIElement child;

                // At most, we will search FocusTrail number of items for a focusable item
                for (; _beforeTrail < FocusTrail; _beforeTrail++) 
                {
                    if (PreviousChildIsGenerated(childIndex)) 
                    { 
                        // The previous child is already generated, check its focusability
                        childIndex--; 
                        child = children[childIndex];
                    }
                    else
                    { 
                        // Generate the previous child
                        child = GeneratePreviousChild(childIndex, layoutSlotSize); 
                    } 

                    if ((child == null) || Keyboard.IsFocusable(child)) 
                    {
                        // Either a focusable item was found, or no child was generated
                        _beforeTrail++;
                        break; 
                    }
                } 
            } 
        }
 
        private int MinDesiredGenerated
        {
            get
            { 
                return Math.Max(0, _visibleStart - _beforeTrail);
            } 
        } 

        private int MaxDesiredGenerated 
        {
            get
            {
                return Math.Min(ItemCount, _visibleStart + _visibleCount + _afterTrail); 
            }
        } 
 
        private int ItemCount
        { 
            get
            {
                ItemsControl itemsControl = ItemsControl.GetItemsOwner(this);
                return (itemsControl != null) ? itemsControl.Items.Count : 0; 
            }
        } 
 
        /// 
        /// Content arrangement. 
        /// 
        /// Arrange size
        protected override Size ArrangeOverride(Size arrangeSize)
        { 
            ItemsControl itemsControl = ItemsControl.GetItemsOwner(this);
            bool isVirtualizing = (itemsControl != null) ? GetIsVirtualizing(itemsControl) : true; 
 
            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, "VirtualizingStackPanel :ArrangeOverride"); 
            } 

            // 
            // Compute scroll offset and seed it into rcChild.
            //
            if (IsScrolling)
            { 
                if (fHorizontal)
                { 
                    rcChild.X = ComputePhysicalFromLogicalOffset(isVirtualizing ? _firstVisibleChildIndex : _scrollData._computedOffset.X, true); 
                    rcChild.Y = -1.0 * _scrollData._computedOffset.Y;
                } 
                else
                {
                    rcChild.X = -1.0 * _scrollData._computedOffset.X;
                    rcChild.Y = ComputePhysicalFromLogicalOffset(isVirtualizing ? _firstVisibleChildIndex : _scrollData._computedOffset.Y, false); 
                }
            } 
 
            //
            // Arrange and Position Children. 
            //
            int childrenCount = InternalChildren.Count;
            for (int i = 0; i < childrenCount; ++i)
            { 
                UIElement child = InternalChildren[i];
                if (child != null) 
                { 
                    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, "VirtualizingStackPanel :ArrangeOverride");
            }
 
            return arrangeSize;
        } 
 

        #endregion Protected Methods 

        //------------------------------------------------------
        //
        //  Private Methods 
        //
        //----------------------------------------------------- 
 
        #region Private Methods
 
        private void EnsureScrollData()
        {
            if (_scrollData == null) { _scrollData = new ScrollData(); }
        } 

        private static void ResetScrolling(VirtualizingStackPanel 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;
            int childrenCount = InternalChildren.Count; 
            Debug.Assert(logicalOffset == 0 || (logicalOffset > 0 && logicalOffset < childrenCount)); 

            for (int i = 0; i < logicalOffset; i++) 
            {
                physicalOffset -= (fHorizontal)
                    ? InternalChildren[i].DesiredSize.Width
                    : InternalChildren[i].DesiredSize.Height; 
            }
 
            return physicalOffset; 
        }
 
        private int FindChildIndexThatParentsVisual(Visual v)
        {
            DependencyObject child = v;
            DependencyObject parent = VisualTreeHelper.GetParent(child); 
            while (parent != this)
            { 
                child = parent; 
                parent = VisualTreeHelper.GetParent(child);
            } 

            UIElementCollection children = InternalChildren;
            for (int i = 0; i < children.Count; i++)
            { 
                if (children[i] == child)
                { 
                    return GetGeneratedIndex(i); 
                }
            } 

            return -1;
        }
 
        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, Rect r, ref Vector newOffset, ref Rect newRect)
        { 
            bool fHorizontal = (Orientation == Orientation.Horizontal);
            int firstChildInView;
            int newFirstChild;
            int viewportSize; 
            double childOffsetWithinViewport = r.Y;
 
            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)
            { 
                childOffsetWithinViewport = 0;
                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) 
            {
                newFirstChild = childIndex - viewportSize + 1;
                double pixelSize = fHorizontal ? ActualWidth : ActualHeight;
                childOffsetWithinViewport = pixelSize * (1.0 - (1.0 / viewportSize)); 
            }
 
            if (fHorizontal) 
            {
                newOffset.X = newFirstChild; 
                newRect.X = childOffsetWithinViewport;
                newRect.Width = r.Width;
            }
            else 
            {
                newOffset.Y = newFirstChild; 
                newRect.Y = childOffsetWithinViewport; 
                newRect.Height = r.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;
        } 

        private int GetGeneratedCount(UIElementCollection children) 
        { 
            return IsItemsHost ? children.Count : 0;
        } 

        private int GetGeneratedIndex(int childIndex)
        {
            return Generator.IndexFromGeneratorPosition(new GeneratorPosition(childIndex, 0)); 
        }
 
        //------------------------------------------------------------ 
        // 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 VirtualizingStackPanel);
        }

        #endregion 

        #endregion Private Methods 
 
        //-----------------------------------------------------
        // 
        //  Private Properties
        //
        //-----------------------------------------------------
 
        #region Private Properties
 
        private bool IsScrolling 
        {
            get { return (_scrollData != null) && (_scrollData._scrollOwner != null); } 
        }

        #endregion Private Properties
 
        //------------------------------------------------------
        // 
        //  Private Fields 
        //
        //----------------------------------------------------- 

        #region Private Fields

        // Logical scrolling and virtualization data. 
        private ScrollData _scrollData;
 
        // Virtualization state 
        private int _visibleStart;
        private int _visibleCount; 
        private int _firstVisibleChildIndex;

        // Cleanup
        private DispatcherOperation _cleanupOperation; 
        private DispatcherTimer _cleanupDelay;
        private int _beforeTrail = 0; 
        private int _afterTrail = 0; 
        private const int FocusTrail = 5; // The maximum number of items off the edge we will generate to get a focused item (so that keyboard navigation can work)
 
        #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 VirtualizingStackPanel 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 = _maxDesiredSize = 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. 

            internal Size _maxDesiredSize;      // Hold onto the maximum desired size to avoid re-laying out the parent ScrollViewer. 
        }

        #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