ScrollContentPresenter.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 / Primitives / ScrollContentPresenter.cs / 2 / ScrollContentPresenter.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
// File: ScrollContentPresenter.cs 
//
// Description: Contains the ScrollContentPresenter class. 
// 
// History:
//  12/08/2003 : greglett  - Created in WCP_break branch 
//
//---------------------------------------------------------------------------

using MS.Internal; 
using MS.Utility;
using System; 
using System.Collections; 
using System.ComponentModel;
using System.Diagnostics; 
using System.Windows.Threading;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Automation.Provider; 
using System.Windows.Controls.Primitives;
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input;
using System.Windows.Media; 
using System.Windows.Markup;

namespace System.Windows.Controls
{ 
    /// 
    ///  
    sealed public class ScrollContentPresenter : ContentPresenter, IScrollInfo 
    {
        //------------------------------------------------------------------- 
        //
        //  Constructors
        //
        //------------------------------------------------------------------- 

        #region Constructors 
 
        /// 
        ///     Default DependencyObject constructor 
        /// 
        /// 
        ///     Automatic determination of current Dispatcher. Use alternative constructor
        ///     that accepts a Dispatcher for best performance. 
        /// 
        public ScrollContentPresenter() : base() 
        { 
            _adornerLayer = new AdornerLayer();
        } 

        #endregion

        //-------------------------------------------------------------------- 
        //
        //  Public Methods 
        // 
        //-------------------------------------------------------------------
 
        #region Public Methods

        /// 
        /// Scroll content by one line to the top. 
        /// 
        public void LineUp() 
        { 
            if (IsScrollClient) { SetVerticalOffset(VerticalOffset - ScrollViewer._scrollLineDelta); }
        } 
        /// 
        /// Scroll content by one line to the bottom.
        /// 
        public void LineDown() 
        {
            if (IsScrollClient) { SetVerticalOffset(VerticalOffset + ScrollViewer._scrollLineDelta); } 
        } 
        /// 
        /// Scroll content by one line to the left. 
        /// 
        public void LineLeft()
        {
            if (IsScrollClient) { SetHorizontalOffset(HorizontalOffset - ScrollViewer._scrollLineDelta); } 
        }
        ///  
        /// Scroll content by one line to the right. 
        /// 
        public void LineRight() 
        {
            if (IsScrollClient) { SetHorizontalOffset(HorizontalOffset + ScrollViewer._scrollLineDelta); }
        }
 
        /// 
        /// Scroll content by one page to the top. 
        ///  
        public void PageUp()
        { 
            if (IsScrollClient) { SetVerticalOffset(VerticalOffset - ViewportHeight); }
        }
        /// 
        /// Scroll content by one page to the bottom. 
        /// 
        public void PageDown() 
        { 
            if (IsScrollClient) { SetVerticalOffset(VerticalOffset + ViewportHeight); }
        } 
        /// 
        /// Scroll content by one page to the left.
        /// 
        public void PageLeft() 
        {
            if (IsScrollClient) { SetHorizontalOffset(HorizontalOffset - ViewportWidth); } 
        } 
        /// 
        /// Scroll content by one page to the right. 
        /// 
        public void PageRight()
        {
            if (IsScrollClient) { SetHorizontalOffset(HorizontalOffset + ViewportWidth); } 
        }
 
        ///  
        /// Scroll content by one line to the top.
        ///  
        public void MouseWheelUp()
        {
            if (IsScrollClient) { SetVerticalOffset(VerticalOffset - ScrollViewer._mouseWheelDelta); }
        } 
        /// 
        /// Scroll content by one line to the bottom. 
        ///  
        public void MouseWheelDown()
        { 
            if (IsScrollClient) { SetVerticalOffset(VerticalOffset + ScrollViewer._mouseWheelDelta); }
        }
        /// 
        /// Scroll content by one page to the top. 
        /// 
        public void MouseWheelLeft() 
        { 
            if (IsScrollClient) { SetHorizontalOffset(HorizontalOffset - ScrollViewer._mouseWheelDelta); }
        } 
        /// 
        /// Scroll content by one page to the bottom.
        /// 
        public void MouseWheelRight() 
        {
            if (IsScrollClient) { SetHorizontalOffset(HorizontalOffset + ScrollViewer._mouseWheelDelta); } 
        } 

        ///  
        /// Set the HorizontalOffset to the passed value.
        /// 
        public void SetHorizontalOffset(double offset)
        { 
            if (IsScrollClient)
            { 
                double newValue = ValidateInputOffset(offset, "HorizontalOffset"); 
                if (!DoubleUtil.AreClose(EnsureScrollData()._offset.X, newValue))
                { 
                    _scrollData._offset.X = newValue;
                    InvalidateArrange();
                }
            } 

        } 
 
        /// 
        /// Set the VerticalOffset to the passed value. 
        /// 
        public void SetVerticalOffset(double offset)
        {
            if (IsScrollClient) 
            {
                double newValue = ValidateInputOffset(offset, "VerticalOffset"); 
                if (!DoubleUtil.AreClose(EnsureScrollData()._offset.Y, newValue)) 
                {
                    _scrollData._offset.Y = newValue; 
                    InvalidateArrange();
                }
            }
        } 

        ///  
        /// ScrollContentPresenter implementation of . 
        /// 
        public Rect MakeVisible(Visual visual, Rect rectangle) 
        {
            return MakeVisible(visual, rectangle, true);
        }
 
        #endregion
 
        //-------------------------------------------------------------------- 
        //
        //  Public Properties (CLR + Avalon) 
        //
        //--------------------------------------------------------------------

        #region Public Properties 

        ///  
        /// AdornerLayer on which adorners are rendered. 
        /// Adorners are rendered under the ScrollContentPresenter's clip region.
        ///  
        public AdornerLayer AdornerLayer
        {
            get { return _adornerLayer; }
        } 

        ///  
        /// This property indicates whether the ScrollContentPresenter should try to allow the Content 
        /// to scroll or not.  A true value indicates Content should be allowed to scroll if it supports
        /// IScrollInfo.  A false value will cause ScrollContentPresenter to always act as the scrolling 
        /// client.
        /// 
        public bool CanContentScroll
        { 
            get { return (bool) GetValue(CanContentScrollProperty); }
            set { SetValue(CanContentScrollProperty, value); } 
        } 

        ///  
        /// ScrollContentPresenter 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.
        /// 
        public bool CanHorizontallyScroll 
        {
            get { return (IsScrollClient) ? EnsureScrollData()._canHorizontallyScroll : false;  } 
            set 
            {
                if (IsScrollClient && (EnsureScrollData()._canHorizontallyScroll != value)) 
                {
                    _scrollData._canHorizontallyScroll = value;
                    InvalidateMeasure();
                } 
            }
        } 
 
        /// 
        /// ScrollContentPresenter 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.
        /// 
        public bool CanVerticallyScroll
        { 
            get { return (IsScrollClient) ? EnsureScrollData()._canVerticallyScroll : false; }
            set 
            { 
                if (IsScrollClient && (EnsureScrollData()._canVerticallyScroll != value))
                { 
                    _scrollData._canVerticallyScroll = value;
                    InvalidateMeasure();
                }
            } 
        }
 
        ///  
        /// ExtentWidth contains the horizontal size of the scrolled content element in 1/96"
        ///  
        public double ExtentWidth
        {
            get  { return (IsScrollClient) ? EnsureScrollData()._extent.Width : 0.0; }
        } 
        /// 
        /// ExtentHeight contains the vertical size of the scrolled content element in 1/96" 
        ///  
        public double ExtentHeight
        { 
            get  { return (IsScrollClient) ? EnsureScrollData()._extent.Height : 0.0; }
        }
        /// 
        /// ViewportWidth contains the horizontal size of content's visible range in 1/96" 
        /// 
        public double ViewportWidth 
        { 
            get { return (IsScrollClient) ? EnsureScrollData()._viewport.Width : 0.0; }
        } 
        /// 
        /// ViewportHeight contains the vertical size of content's visible range in 1/96"
        /// 
        public double ViewportHeight 
        {
            get { return (IsScrollClient) ? EnsureScrollData()._viewport.Height : 0.0; } 
        } 

        ///  
        /// HorizontalOffset is the horizontal offset of the scrolled content in 1/96".
        /// 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public double HorizontalOffset 
        {
            get { return (IsScrollClient) ? EnsureScrollData()._computedOffset.X : 0.0; } 
        } 
        /// 
        /// VerticalOffset is the vertical offset of the scrolled content in 1/96". 
        /// 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public double VerticalOffset
        { 
            get { return (IsScrollClient) ? EnsureScrollData()._computedOffset.Y : 0.0; }
        } 
 
        /// 
        /// ScrollOwner is the container that controls any scrollbars, headers, etc... that are dependant 
        /// on this ScrollArea's properties.
        /// 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ScrollViewer ScrollOwner 
        {
            get { return (IsScrollClient) ? _scrollData._scrollOwner: null; } 
            set { if (IsScrollClient) { _scrollData._scrollOwner = value; } } 
        }
 
        /// 
        /// DependencyProperty for  property.
        /// 
        public static readonly DependencyProperty CanContentScrollProperty = 
                ScrollViewer.CanContentScrollProperty.AddOwner(
                        typeof(ScrollContentPresenter), 
                        new FrameworkPropertyMetadata(new PropertyChangedCallback(OnCanContentScrollChanged))); 

        #endregion 

        //-------------------------------------------------------------------
        //
        //  Protected Methods 
        //
        //-------------------------------------------------------------------- 
 
        #region Protected Methods
        ///  
        /// Returns the Visual children count.
        /// 
        protected override int VisualChildrenCount
        { 
            get
            { 
                // Four states make sense: 
                // 0 Children.  No Content or AdornerLayer.  Valid - do nothing.
                // 2 Children.  Content is first child, AdornerLayer 

                // One for the base.TemplateChild and one for the _adornerlayer.
                return (base.TemplateChild == null) ? 0 : 2;
            } 
        }
 
        ///  
        /// Returns the child at the specified index.
        ///  
        protected override Visual GetVisualChild(int index)
        {
            //check if there is a TemplateChild on FrameworkElement
            if (base.TemplateChild == null) 
            {
                throw new ArgumentOutOfRangeException("index", index, SR.Get(SRID.Visual_ArgumentOutOfRange)); 
            } 
            else
            { 
                switch (index)
                {
                    case 0:
                        return base.TemplateChild; 

                    case 1: 
                        return _adornerLayer; 

                    default: 
                        throw new ArgumentOutOfRangeException("index", index, SR.Get(SRID.Visual_ArgumentOutOfRange));
                }
            }
         } 

        ///  
        /// Gets or sets the template child of the FrameworkElement. 
        /// 
        override internal UIElement TemplateChild 
        {
            get
            {
                return base.TemplateChild; 
            }
            set 
            { 
                UIElement oldTemplate = base.TemplateChild;
                if (value != oldTemplate) 
                {
                    if (oldTemplate != null && value == null)
                    {
                        // If we used to have a template child and we don't have a 
                        // new template child disconnect the adorner layer.
                        this.RemoveVisualChild(_adornerLayer); 
                    } 

                    base.TemplateChild = value; 

                    if(oldTemplate == null && value != null)
                    {
                        // If we did not use to have a template child, but we have one 
                        // now, attach the adorner layer.
                        this.AddVisualChild(_adornerLayer); 
                    } 
                }
            } 
        }


        ///  
        /// 
        protected override Size MeasureOverride(Size constraint) 
        { 
            Size desiredSize = new Size();
            int count = this.VisualChildrenCount; 

            bool etwTracingEnabled = IsScrollClient && EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal);
            if (etwTracingEnabled)
            { 
                EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.StartEvent, "SCROLLCONTENTPRESENTER:MeasureOverride");
            } 
 
            if (count > 0)
            { 
                    // The AdornerLayer is always the size of our surface, and does not contribute to our own size.
                    _adornerLayer.Measure(constraint);

                    if(!IsScrollClient) 
                    {
                        desiredSize = base.MeasureOverride(constraint); 
                    } 
                    else
                    { 
                        Size childConstraint = constraint;

                        if (_scrollData._canHorizontallyScroll) {  childConstraint.Width = Double.PositiveInfinity; }
                        if (_scrollData._canVerticallyScroll) {  childConstraint.Height = Double.PositiveInfinity; } 

                        desiredSize = base.MeasureOverride(childConstraint); 
                    } 
            }
 
            // If we're handling scrolling (as the physical scrolling client, validate properties.
            if (IsScrollClient)
            {
                VerifyScrollData(constraint, desiredSize); 
            }
 
            desiredSize.Width = Math.Min(constraint.Width, desiredSize.Width); 
            desiredSize.Height = Math.Min(constraint.Height, desiredSize.Height);
 
            if (etwTracingEnabled)
            {
                EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.EndEvent, "SCROLLCONTENTPRESENTER:MeasureOverride");
            } 

            return desiredSize; 
        } 

        ///  
        /// 
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            int count = this.VisualChildrenCount; 

            bool etwTracingEnabled = IsScrollClient && EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal); 
            if (etwTracingEnabled) 
            {
                EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.StartEvent, "SCROLLCONTENTPRESENTER:ArrangeOverride"); 
            }

            // Verifies IScrollInfo properties & invalidates ScrollViewer if necessary.
            if (IsScrollClient) 
            {
                VerifyScrollData(arrangeSize, _scrollData._extent); 
            } 

            if (count>0) 
            {
                    _adornerLayer.Arrange(new Rect(arrangeSize));

                    UIElement child = this.GetVisualChild(0) as UIElement; 
                    if (child != null)
                    { 
                        Rect childRect = new Rect(child.DesiredSize); 

                        if (IsScrollClient) 
                        {
                            childRect.X = -HorizontalOffset;
                            childRect.Y = -VerticalOffset;
                        } 

                        //this is needed to stretch the child to arrange space, 
                        childRect.Width = Math.Max(childRect.Width, arrangeSize.Width); 
                        childRect.Height = Math.Max(childRect.Height, arrangeSize.Height);
 
                        child.Arrange(childRect);
                    }
            }
 
            if (etwTracingEnabled)
            { 
                EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.EndEvent, "SCROLLCONTENTPRESENTER:ArrangeOverride"); 
            }
 
            return (arrangeSize);
        }

        ///  
        /// Override of .
        ///  
        /// Viewport geometry 
        protected override Geometry GetLayoutClip(Size layoutSlotSize)
        { 
            return new RectangleGeometry(new Rect(RenderSize));
        }

        ///  
        /// Called when the Template's tree has been generated
        ///  
        public override void OnApplyTemplate() 
        {
            base.OnApplyTemplate(); 


            // Add the AdornerLayer to our visual tree.
            // Iff we have content, we need an adorner layer. 
            // It has Content(eg. Button, TextBlock) as its first child and AdornerLayer as its second child
 
            // Get our scrolling owner and content talking. 
            HookupScrollingComponents();
        } 

        #endregion

        //------------------------------------------------------------------- 
        //
        //  Internal Methods 
        // 
        //-------------------------------------------------------------------
 
        #region Internal Methods

        /// 
        /// ScrollContentPresenter implementation of . 
        /// 
        /// The Visual that should become visible 
        /// A rectangle representing in the visual's coordinate space to make visible. 
        /// If true the method throws an exception when an error is encountered, otherwise the method returns Rect.Empty when an error is encountered
        ///  
        /// A rectangle in the IScrollInfo's coordinate space that has been made visible.
        /// Other ancestors to in turn make this new rectangle visible.
        /// The rectangle should generally be a transformed version of the input rectangle.  In some cases, like
        /// when the input rectangle cannot entirely fit in the viewport, the return value might be smaller. 
        /// 
        internal Rect MakeVisible(Visual visual, Rect rectangle, bool throwOnError) 
        { 
            //
 



 

            // 
            // Note: This code presently assumes we/children are layout clean.  See work item 22269 for more detail. 
            //
 
            // 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;
            } 

            // This is a false positive by PreSharp. visual cannot be null because of the 'if' check above
#pragma warning disable 1634, 1691
#pragma warning disable 56506 
            // Compute the child's rect relative to (0,0) in our coordinate space.
            GeneralTransform childTransform = visual.TransformToAncestor(this); 
#pragma warning restore 56506 
#pragma warning restore 1634, 1691
 
            rectangle = childTransform.TransformBounds(rectangle);

            if (!IsScrollClient)
            { 
                return rectangle;
            } 
 
            // Initialize the viewport
            Rect viewport = new Rect(HorizontalOffset, VerticalOffset, ViewportWidth, ViewportHeight); 
            rectangle.X += viewport.X;
            rectangle.Y += viewport.Y;

            // Compute the offsets required to minimally scroll the child maximally into view. 
            double minX = ComputeScrollOffsetWithMinimalScroll(viewport.Left, viewport.Right, rectangle.Left, rectangle.Right);
            double minY = ComputeScrollOffsetWithMinimalScroll(viewport.Top, viewport.Bottom, rectangle.Top, rectangle.Bottom); 
 
            // We have computed the scrolling offsets; scroll to them.
            SetHorizontalOffset(minX); 
            SetVerticalOffset(minY);

            // Compute the visible rectangle of the child relative to the viewport.
            viewport.X = minX; 
            viewport.Y = minY;
            rectangle.Intersect(viewport); 
 
            if (throwOnError)
            { 
                //

                rectangle.X -= viewport.X;
                rectangle.Y -= viewport.Y; 
            }
            else 
            { 
                //
 
                if (!rectangle.IsEmpty)
                {
                    rectangle.X -= viewport.X;
                    rectangle.Y -= viewport.Y; 
                }
            } 
 
            // Return the rectangle
            return rectangle; 
        }

        internal static double ComputeScrollOffsetWithMinimalScroll(
            double topView, 
            double bottomView,
            double topChild, 
            double bottomChild) 
        {
            // # CHILD POSITION       CHILD SIZE      SCROLL      REMEDY 
            // 1 Above viewport       <= viewport     Down        Align top edge of child & viewport
            // 2 Above viewport       > viewport      Down        Align bottom edge of child & viewport
            // 3 Below viewport       <= viewport     Up          Align bottom edge of child & viewport
            // 4 Below viewport       > viewport      Up          Align top edge of child & viewport 
            // 5 Entirely within viewport             NA          No scroll.
            // 6 Spanning viewport                    NA          No scroll. 
            // 
            // Note: "Above viewport" = childTop above viewportTop, childBottom above viewportBottom
            //       "Below viewport" = childTop below viewportTop, childBottom below viewportBottom 
            // These child thus may overlap with the viewport, but will scroll the same direction/

            bool fAbove = DoubleUtil.LessThan(topChild, topView) && DoubleUtil.LessThan(bottomChild, bottomView);
            bool fBelow = DoubleUtil.GreaterThan(bottomChild, bottomView) && DoubleUtil.GreaterThan(topChild, topView); 
            bool fLarger = (bottomChild - topChild) > (bottomView - topView);
 
            // Handle Cases:  1 & 4 above 
            if ((fAbove && !fLarger)
               || (fBelow && fLarger)) 
            {
                return topChild;
            }
 
            // Handle Cases: 2 & 3 above
            else if (fAbove || fBelow) 
            { 
                return (bottomChild - (bottomView - topView));
            } 

            // Handle cases: 5 & 6 above.
            return topView;
        } 

        static internal double ValidateInputOffset(double offset, string parameterName) 
        { 
            if (DoubleUtil.IsNaN(offset))
            { 
                throw new ArgumentOutOfRangeException(parameterName, SR.Get(SRID.ScrollViewer_CannotBeNaN, parameterName));
            }
            return Math.Max(0.0, offset);
        } 

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

 
        // Helper method to get our ScrollViewer owner and its scrolling content talking.
        // Method introduces the current owner/content, and clears a from any previous content. 
        internal void HookupScrollingComponents() 
        {
            // We need to introduce our IScrollInfo to our ScrollViewer (and break any previous links). 
            ScrollViewer scrollContainer = TemplatedParent as ScrollViewer;

            // If our content is not an IScrollInfo, we should have selected a style that contains one.
            // This (readonly) style contains an AdornerDecorator with a ScrollArea child. 
            if (scrollContainer != null)
            { 
                IScrollInfo si = null; 

                if (CanContentScroll) 
                {
                    // We need to get an IScrollInfo to introduce to the ScrollViewer.
                    // 1. Try our content...
                    si = Content as IScrollInfo; 

                    // 2. Our child might be an ItemsPresenter.  In this case check its child for being an IScrollInfo 
                    if (si == null) 
                    {
                        ItemsPresenter itemsPresenter = Content as ItemsPresenter; 
                        if (itemsPresenter != null)
                        {
                            itemsPresenter.ApplyTemplate();
 
                            int count = VisualTreeHelper.GetChildrenCount(itemsPresenter);
                            if(count > 0) 
                                si = VisualTreeHelper.GetChild(itemsPresenter, 0) as IScrollInfo; 
                        }
                    } 
                }

                // 3. As a final fallback, we use ourself.
                if (si == null) 
                {
                    si = (IScrollInfo)this; 
                    EnsureScrollData(); 
                }
 
                // Detach any differing previous IScrollInfo from ScrollViewer
                if (si != _scrollInfo && _scrollInfo != null)
                {
                    if (IsScrollClient) { _scrollData = null; } 
                    else _scrollInfo.ScrollOwner = null;
                } 
 
                // Introduce our ScrollViewer and IScrollInfo to each other.
                if (si != null) 
                {
                    _scrollInfo = si;                   // At this point, we pass IsScrollClient if si == this.
                    si.ScrollOwner = scrollContainer;
                    scrollContainer.ScrollInfo = si; 
                }
            } 
 
            // We're not really in a valid scrolling scenario.  Break any previous references, and get us
            // back into a totally unlinked state. 
            else if (_scrollInfo != null)
            {
                if (_scrollInfo.ScrollOwner != null) { _scrollInfo.ScrollOwner.ScrollInfo = null; }
                _scrollInfo.ScrollOwner = null; 
                _scrollInfo = null;
                _scrollData = null; 
            } 
        }
 
        // Verifies scrolling data using the passed viewport and extent as newly computed values.
        // Checks the X/Y offset and coerces them into the range [0, Extent - ViewportSize]
        // If extent, viewport, or the newly coerced offsets are different than the existing offset,
        //   cachces are updated and InvalidateScrollInfo() is called. 
        private void VerifyScrollData(Size viewport, Size extent)
        { 
            Debug.Assert(IsScrollClient); 

            bool fValid = true; 

            // These two lines of code are questionable, but they are needed right now as VSB may return
            //  Infinity size from measure, which is a regression from the old scrolling model.
            // They also have the incidental affect of probably avoiding reinvalidation at Arrange 
            //   when inside a parent that measures you to Infinity.
            if (Double.IsInfinity(viewport.Width)) viewport.Width = extent.Width; 
            if (Double.IsInfinity(viewport.Height)) viewport.Height = extent.Height; 

            fValid &= DoubleUtil.AreClose(viewport, _scrollData._viewport); 
            fValid &= DoubleUtil.AreClose(extent, _scrollData._extent);
            _scrollData._viewport = viewport;
            _scrollData._extent = extent;
 
            fValid &= CoerceOffsets();
 
            if (!fValid) 
            {
                ScrollOwner.InvalidateScrollInfo(); 
            }
        }

        // Returns an offset coerced into the [0, Extent - Viewport] range. 
        // Internal because it is also used by other Avalon ISI implementations (just to avoid code duplication).
        static internal double CoerceOffset(double offset, double extent, double viewport) 
        { 
            if (offset > extent - viewport) { offset = extent - viewport; }
            if (offset < 0) { offset = 0; } 
            return offset;
        }

        private bool CoerceOffsets() 
        {
            Debug.Assert(IsScrollClient); 
            Vector computedOffset = new Vector( 
                CoerceOffset(_scrollData._offset.X, _scrollData._extent.Width, _scrollData._viewport.Width),
                CoerceOffset(_scrollData._offset.Y, _scrollData._extent.Height, _scrollData._viewport.Height)); 

            bool fValid = DoubleUtil.AreClose(_scrollData._computedOffset, computedOffset);
            _scrollData._computedOffset = computedOffset;
 
            return fValid;
        } 
 
        // This property is structurally important; we can't do layout without it set right.
        // So, we synchronously make changes. 
        static private void OnCanContentScrollChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ScrollContentPresenter scp = (ScrollContentPresenter)d;
            if (scp._scrollInfo == null) 
            {
                return; 
            } 

// 
            scp.HookupScrollingComponents();
            scp.InvalidateMeasure();
        }
        #endregion 

        //------------------------------------------------------------------- 
        // 
        //  Private Properties
        // 
        //--------------------------------------------------------------------

        #region Private Properties
 
        private bool IsScrollClient
        { 
            get { return (_scrollInfo == this); } 
        }
 
        //
        //  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 42; }
        } 

        #endregion

        //-------------------------------------------------------------------- 
        //
        //  Private Fields 
        // 
        //-------------------------------------------------------------------
 
        #region Private Fields

        // Only one of the following will be used.
        // The _scrollInfo holds a content IScrollInfo implementation that is given to the ScrollViewer. 
        // _scrollData holds values for the scrolling properties we use if we are handling IScrollInfo for the ScrollViewer ourself.
        // ScrollData could implement IScrollInfo, but then the v-table would hurt in the common case as much as we save 
        //   in the less common case. 
        private IScrollInfo _scrollInfo;
        private ScrollData _scrollData; 
        // To hold adorners (caret, &c...) under the clipping region of the scroller.
        private readonly AdornerLayer _adornerLayer;

        #endregion 

        //------------------------------------------------------ 
        // 
        //  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 SCP is delegating to another implementation of ISI.
        // Standard "extra pointer always for less data sometimes" cache savings model:
        // 
        private class ScrollData
        { 
            internal ScrollViewer _scrollOwner; 

            internal bool _canHorizontallyScroll; 
            internal bool _canVerticallyScroll;

            internal Vector _offset;            // Set scroll offset of content.  Positive corresponds to a visually upward offset.
            internal Vector _computedOffset;    // Actual (computed) scroll offset of content. ""  "" 

            internal Size _viewport;    // ViewportSize is computed from our FinalSize, but may be in different units. 
            internal Size _extent;      // Extent is the total size of our content. 
        }
 
        #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: ScrollContentPresenter.cs 
//
// Description: Contains the ScrollContentPresenter class. 
// 
// History:
//  12/08/2003 : greglett  - Created in WCP_break branch 
//
//---------------------------------------------------------------------------

using MS.Internal; 
using MS.Utility;
using System; 
using System.Collections; 
using System.ComponentModel;
using System.Diagnostics; 
using System.Windows.Threading;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Automation.Provider; 
using System.Windows.Controls.Primitives;
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input;
using System.Windows.Media; 
using System.Windows.Markup;

namespace System.Windows.Controls
{ 
    /// 
    ///  
    sealed public class ScrollContentPresenter : ContentPresenter, IScrollInfo 
    {
        //------------------------------------------------------------------- 
        //
        //  Constructors
        //
        //------------------------------------------------------------------- 

        #region Constructors 
 
        /// 
        ///     Default DependencyObject constructor 
        /// 
        /// 
        ///     Automatic determination of current Dispatcher. Use alternative constructor
        ///     that accepts a Dispatcher for best performance. 
        /// 
        public ScrollContentPresenter() : base() 
        { 
            _adornerLayer = new AdornerLayer();
        } 

        #endregion

        //-------------------------------------------------------------------- 
        //
        //  Public Methods 
        // 
        //-------------------------------------------------------------------
 
        #region Public Methods

        /// 
        /// Scroll content by one line to the top. 
        /// 
        public void LineUp() 
        { 
            if (IsScrollClient) { SetVerticalOffset(VerticalOffset - ScrollViewer._scrollLineDelta); }
        } 
        /// 
        /// Scroll content by one line to the bottom.
        /// 
        public void LineDown() 
        {
            if (IsScrollClient) { SetVerticalOffset(VerticalOffset + ScrollViewer._scrollLineDelta); } 
        } 
        /// 
        /// Scroll content by one line to the left. 
        /// 
        public void LineLeft()
        {
            if (IsScrollClient) { SetHorizontalOffset(HorizontalOffset - ScrollViewer._scrollLineDelta); } 
        }
        ///  
        /// Scroll content by one line to the right. 
        /// 
        public void LineRight() 
        {
            if (IsScrollClient) { SetHorizontalOffset(HorizontalOffset + ScrollViewer._scrollLineDelta); }
        }
 
        /// 
        /// Scroll content by one page to the top. 
        ///  
        public void PageUp()
        { 
            if (IsScrollClient) { SetVerticalOffset(VerticalOffset - ViewportHeight); }
        }
        /// 
        /// Scroll content by one page to the bottom. 
        /// 
        public void PageDown() 
        { 
            if (IsScrollClient) { SetVerticalOffset(VerticalOffset + ViewportHeight); }
        } 
        /// 
        /// Scroll content by one page to the left.
        /// 
        public void PageLeft() 
        {
            if (IsScrollClient) { SetHorizontalOffset(HorizontalOffset - ViewportWidth); } 
        } 
        /// 
        /// Scroll content by one page to the right. 
        /// 
        public void PageRight()
        {
            if (IsScrollClient) { SetHorizontalOffset(HorizontalOffset + ViewportWidth); } 
        }
 
        ///  
        /// Scroll content by one line to the top.
        ///  
        public void MouseWheelUp()
        {
            if (IsScrollClient) { SetVerticalOffset(VerticalOffset - ScrollViewer._mouseWheelDelta); }
        } 
        /// 
        /// Scroll content by one line to the bottom. 
        ///  
        public void MouseWheelDown()
        { 
            if (IsScrollClient) { SetVerticalOffset(VerticalOffset + ScrollViewer._mouseWheelDelta); }
        }
        /// 
        /// Scroll content by one page to the top. 
        /// 
        public void MouseWheelLeft() 
        { 
            if (IsScrollClient) { SetHorizontalOffset(HorizontalOffset - ScrollViewer._mouseWheelDelta); }
        } 
        /// 
        /// Scroll content by one page to the bottom.
        /// 
        public void MouseWheelRight() 
        {
            if (IsScrollClient) { SetHorizontalOffset(HorizontalOffset + ScrollViewer._mouseWheelDelta); } 
        } 

        ///  
        /// Set the HorizontalOffset to the passed value.
        /// 
        public void SetHorizontalOffset(double offset)
        { 
            if (IsScrollClient)
            { 
                double newValue = ValidateInputOffset(offset, "HorizontalOffset"); 
                if (!DoubleUtil.AreClose(EnsureScrollData()._offset.X, newValue))
                { 
                    _scrollData._offset.X = newValue;
                    InvalidateArrange();
                }
            } 

        } 
 
        /// 
        /// Set the VerticalOffset to the passed value. 
        /// 
        public void SetVerticalOffset(double offset)
        {
            if (IsScrollClient) 
            {
                double newValue = ValidateInputOffset(offset, "VerticalOffset"); 
                if (!DoubleUtil.AreClose(EnsureScrollData()._offset.Y, newValue)) 
                {
                    _scrollData._offset.Y = newValue; 
                    InvalidateArrange();
                }
            }
        } 

        ///  
        /// ScrollContentPresenter implementation of . 
        /// 
        public Rect MakeVisible(Visual visual, Rect rectangle) 
        {
            return MakeVisible(visual, rectangle, true);
        }
 
        #endregion
 
        //-------------------------------------------------------------------- 
        //
        //  Public Properties (CLR + Avalon) 
        //
        //--------------------------------------------------------------------

        #region Public Properties 

        ///  
        /// AdornerLayer on which adorners are rendered. 
        /// Adorners are rendered under the ScrollContentPresenter's clip region.
        ///  
        public AdornerLayer AdornerLayer
        {
            get { return _adornerLayer; }
        } 

        ///  
        /// This property indicates whether the ScrollContentPresenter should try to allow the Content 
        /// to scroll or not.  A true value indicates Content should be allowed to scroll if it supports
        /// IScrollInfo.  A false value will cause ScrollContentPresenter to always act as the scrolling 
        /// client.
        /// 
        public bool CanContentScroll
        { 
            get { return (bool) GetValue(CanContentScrollProperty); }
            set { SetValue(CanContentScrollProperty, value); } 
        } 

        ///  
        /// ScrollContentPresenter 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.
        /// 
        public bool CanHorizontallyScroll 
        {
            get { return (IsScrollClient) ? EnsureScrollData()._canHorizontallyScroll : false;  } 
            set 
            {
                if (IsScrollClient && (EnsureScrollData()._canHorizontallyScroll != value)) 
                {
                    _scrollData._canHorizontallyScroll = value;
                    InvalidateMeasure();
                } 
            }
        } 
 
        /// 
        /// ScrollContentPresenter 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.
        /// 
        public bool CanVerticallyScroll
        { 
            get { return (IsScrollClient) ? EnsureScrollData()._canVerticallyScroll : false; }
            set 
            { 
                if (IsScrollClient && (EnsureScrollData()._canVerticallyScroll != value))
                { 
                    _scrollData._canVerticallyScroll = value;
                    InvalidateMeasure();
                }
            } 
        }
 
        ///  
        /// ExtentWidth contains the horizontal size of the scrolled content element in 1/96"
        ///  
        public double ExtentWidth
        {
            get  { return (IsScrollClient) ? EnsureScrollData()._extent.Width : 0.0; }
        } 
        /// 
        /// ExtentHeight contains the vertical size of the scrolled content element in 1/96" 
        ///  
        public double ExtentHeight
        { 
            get  { return (IsScrollClient) ? EnsureScrollData()._extent.Height : 0.0; }
        }
        /// 
        /// ViewportWidth contains the horizontal size of content's visible range in 1/96" 
        /// 
        public double ViewportWidth 
        { 
            get { return (IsScrollClient) ? EnsureScrollData()._viewport.Width : 0.0; }
        } 
        /// 
        /// ViewportHeight contains the vertical size of content's visible range in 1/96"
        /// 
        public double ViewportHeight 
        {
            get { return (IsScrollClient) ? EnsureScrollData()._viewport.Height : 0.0; } 
        } 

        ///  
        /// HorizontalOffset is the horizontal offset of the scrolled content in 1/96".
        /// 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public double HorizontalOffset 
        {
            get { return (IsScrollClient) ? EnsureScrollData()._computedOffset.X : 0.0; } 
        } 
        /// 
        /// VerticalOffset is the vertical offset of the scrolled content in 1/96". 
        /// 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public double VerticalOffset
        { 
            get { return (IsScrollClient) ? EnsureScrollData()._computedOffset.Y : 0.0; }
        } 
 
        /// 
        /// ScrollOwner is the container that controls any scrollbars, headers, etc... that are dependant 
        /// on this ScrollArea's properties.
        /// 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ScrollViewer ScrollOwner 
        {
            get { return (IsScrollClient) ? _scrollData._scrollOwner: null; } 
            set { if (IsScrollClient) { _scrollData._scrollOwner = value; } } 
        }
 
        /// 
        /// DependencyProperty for  property.
        /// 
        public static readonly DependencyProperty CanContentScrollProperty = 
                ScrollViewer.CanContentScrollProperty.AddOwner(
                        typeof(ScrollContentPresenter), 
                        new FrameworkPropertyMetadata(new PropertyChangedCallback(OnCanContentScrollChanged))); 

        #endregion 

        //-------------------------------------------------------------------
        //
        //  Protected Methods 
        //
        //-------------------------------------------------------------------- 
 
        #region Protected Methods
        ///  
        /// Returns the Visual children count.
        /// 
        protected override int VisualChildrenCount
        { 
            get
            { 
                // Four states make sense: 
                // 0 Children.  No Content or AdornerLayer.  Valid - do nothing.
                // 2 Children.  Content is first child, AdornerLayer 

                // One for the base.TemplateChild and one for the _adornerlayer.
                return (base.TemplateChild == null) ? 0 : 2;
            } 
        }
 
        ///  
        /// Returns the child at the specified index.
        ///  
        protected override Visual GetVisualChild(int index)
        {
            //check if there is a TemplateChild on FrameworkElement
            if (base.TemplateChild == null) 
            {
                throw new ArgumentOutOfRangeException("index", index, SR.Get(SRID.Visual_ArgumentOutOfRange)); 
            } 
            else
            { 
                switch (index)
                {
                    case 0:
                        return base.TemplateChild; 

                    case 1: 
                        return _adornerLayer; 

                    default: 
                        throw new ArgumentOutOfRangeException("index", index, SR.Get(SRID.Visual_ArgumentOutOfRange));
                }
            }
         } 

        ///  
        /// Gets or sets the template child of the FrameworkElement. 
        /// 
        override internal UIElement TemplateChild 
        {
            get
            {
                return base.TemplateChild; 
            }
            set 
            { 
                UIElement oldTemplate = base.TemplateChild;
                if (value != oldTemplate) 
                {
                    if (oldTemplate != null && value == null)
                    {
                        // If we used to have a template child and we don't have a 
                        // new template child disconnect the adorner layer.
                        this.RemoveVisualChild(_adornerLayer); 
                    } 

                    base.TemplateChild = value; 

                    if(oldTemplate == null && value != null)
                    {
                        // If we did not use to have a template child, but we have one 
                        // now, attach the adorner layer.
                        this.AddVisualChild(_adornerLayer); 
                    } 
                }
            } 
        }


        ///  
        /// 
        protected override Size MeasureOverride(Size constraint) 
        { 
            Size desiredSize = new Size();
            int count = this.VisualChildrenCount; 

            bool etwTracingEnabled = IsScrollClient && EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal);
            if (etwTracingEnabled)
            { 
                EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.StartEvent, "SCROLLCONTENTPRESENTER:MeasureOverride");
            } 
 
            if (count > 0)
            { 
                    // The AdornerLayer is always the size of our surface, and does not contribute to our own size.
                    _adornerLayer.Measure(constraint);

                    if(!IsScrollClient) 
                    {
                        desiredSize = base.MeasureOverride(constraint); 
                    } 
                    else
                    { 
                        Size childConstraint = constraint;

                        if (_scrollData._canHorizontallyScroll) {  childConstraint.Width = Double.PositiveInfinity; }
                        if (_scrollData._canVerticallyScroll) {  childConstraint.Height = Double.PositiveInfinity; } 

                        desiredSize = base.MeasureOverride(childConstraint); 
                    } 
            }
 
            // If we're handling scrolling (as the physical scrolling client, validate properties.
            if (IsScrollClient)
            {
                VerifyScrollData(constraint, desiredSize); 
            }
 
            desiredSize.Width = Math.Min(constraint.Width, desiredSize.Width); 
            desiredSize.Height = Math.Min(constraint.Height, desiredSize.Height);
 
            if (etwTracingEnabled)
            {
                EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.EndEvent, "SCROLLCONTENTPRESENTER:MeasureOverride");
            } 

            return desiredSize; 
        } 

        ///  
        /// 
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            int count = this.VisualChildrenCount; 

            bool etwTracingEnabled = IsScrollClient && EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal); 
            if (etwTracingEnabled) 
            {
                EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.StartEvent, "SCROLLCONTENTPRESENTER:ArrangeOverride"); 
            }

            // Verifies IScrollInfo properties & invalidates ScrollViewer if necessary.
            if (IsScrollClient) 
            {
                VerifyScrollData(arrangeSize, _scrollData._extent); 
            } 

            if (count>0) 
            {
                    _adornerLayer.Arrange(new Rect(arrangeSize));

                    UIElement child = this.GetVisualChild(0) as UIElement; 
                    if (child != null)
                    { 
                        Rect childRect = new Rect(child.DesiredSize); 

                        if (IsScrollClient) 
                        {
                            childRect.X = -HorizontalOffset;
                            childRect.Y = -VerticalOffset;
                        } 

                        //this is needed to stretch the child to arrange space, 
                        childRect.Width = Math.Max(childRect.Width, arrangeSize.Width); 
                        childRect.Height = Math.Max(childRect.Height, arrangeSize.Height);
 
                        child.Arrange(childRect);
                    }
            }
 
            if (etwTracingEnabled)
            { 
                EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.EndEvent, "SCROLLCONTENTPRESENTER:ArrangeOverride"); 
            }
 
            return (arrangeSize);
        }

        ///  
        /// Override of .
        ///  
        /// Viewport geometry 
        protected override Geometry GetLayoutClip(Size layoutSlotSize)
        { 
            return new RectangleGeometry(new Rect(RenderSize));
        }

        ///  
        /// Called when the Template's tree has been generated
        ///  
        public override void OnApplyTemplate() 
        {
            base.OnApplyTemplate(); 


            // Add the AdornerLayer to our visual tree.
            // Iff we have content, we need an adorner layer. 
            // It has Content(eg. Button, TextBlock) as its first child and AdornerLayer as its second child
 
            // Get our scrolling owner and content talking. 
            HookupScrollingComponents();
        } 

        #endregion

        //------------------------------------------------------------------- 
        //
        //  Internal Methods 
        // 
        //-------------------------------------------------------------------
 
        #region Internal Methods

        /// 
        /// ScrollContentPresenter implementation of . 
        /// 
        /// The Visual that should become visible 
        /// A rectangle representing in the visual's coordinate space to make visible. 
        /// If true the method throws an exception when an error is encountered, otherwise the method returns Rect.Empty when an error is encountered
        ///  
        /// A rectangle in the IScrollInfo's coordinate space that has been made visible.
        /// Other ancestors to in turn make this new rectangle visible.
        /// The rectangle should generally be a transformed version of the input rectangle.  In some cases, like
        /// when the input rectangle cannot entirely fit in the viewport, the return value might be smaller. 
        /// 
        internal Rect MakeVisible(Visual visual, Rect rectangle, bool throwOnError) 
        { 
            //
 



 

            // 
            // Note: This code presently assumes we/children are layout clean.  See work item 22269 for more detail. 
            //
 
            // 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;
            } 

            // This is a false positive by PreSharp. visual cannot be null because of the 'if' check above
#pragma warning disable 1634, 1691
#pragma warning disable 56506 
            // Compute the child's rect relative to (0,0) in our coordinate space.
            GeneralTransform childTransform = visual.TransformToAncestor(this); 
#pragma warning restore 56506 
#pragma warning restore 1634, 1691
 
            rectangle = childTransform.TransformBounds(rectangle);

            if (!IsScrollClient)
            { 
                return rectangle;
            } 
 
            // Initialize the viewport
            Rect viewport = new Rect(HorizontalOffset, VerticalOffset, ViewportWidth, ViewportHeight); 
            rectangle.X += viewport.X;
            rectangle.Y += viewport.Y;

            // Compute the offsets required to minimally scroll the child maximally into view. 
            double minX = ComputeScrollOffsetWithMinimalScroll(viewport.Left, viewport.Right, rectangle.Left, rectangle.Right);
            double minY = ComputeScrollOffsetWithMinimalScroll(viewport.Top, viewport.Bottom, rectangle.Top, rectangle.Bottom); 
 
            // We have computed the scrolling offsets; scroll to them.
            SetHorizontalOffset(minX); 
            SetVerticalOffset(minY);

            // Compute the visible rectangle of the child relative to the viewport.
            viewport.X = minX; 
            viewport.Y = minY;
            rectangle.Intersect(viewport); 
 
            if (throwOnError)
            { 
                //

                rectangle.X -= viewport.X;
                rectangle.Y -= viewport.Y; 
            }
            else 
            { 
                //
 
                if (!rectangle.IsEmpty)
                {
                    rectangle.X -= viewport.X;
                    rectangle.Y -= viewport.Y; 
                }
            } 
 
            // Return the rectangle
            return rectangle; 
        }

        internal static double ComputeScrollOffsetWithMinimalScroll(
            double topView, 
            double bottomView,
            double topChild, 
            double bottomChild) 
        {
            // # CHILD POSITION       CHILD SIZE      SCROLL      REMEDY 
            // 1 Above viewport       <= viewport     Down        Align top edge of child & viewport
            // 2 Above viewport       > viewport      Down        Align bottom edge of child & viewport
            // 3 Below viewport       <= viewport     Up          Align bottom edge of child & viewport
            // 4 Below viewport       > viewport      Up          Align top edge of child & viewport 
            // 5 Entirely within viewport             NA          No scroll.
            // 6 Spanning viewport                    NA          No scroll. 
            // 
            // Note: "Above viewport" = childTop above viewportTop, childBottom above viewportBottom
            //       "Below viewport" = childTop below viewportTop, childBottom below viewportBottom 
            // These child thus may overlap with the viewport, but will scroll the same direction/

            bool fAbove = DoubleUtil.LessThan(topChild, topView) && DoubleUtil.LessThan(bottomChild, bottomView);
            bool fBelow = DoubleUtil.GreaterThan(bottomChild, bottomView) && DoubleUtil.GreaterThan(topChild, topView); 
            bool fLarger = (bottomChild - topChild) > (bottomView - topView);
 
            // Handle Cases:  1 & 4 above 
            if ((fAbove && !fLarger)
               || (fBelow && fLarger)) 
            {
                return topChild;
            }
 
            // Handle Cases: 2 & 3 above
            else if (fAbove || fBelow) 
            { 
                return (bottomChild - (bottomView - topView));
            } 

            // Handle cases: 5 & 6 above.
            return topView;
        } 

        static internal double ValidateInputOffset(double offset, string parameterName) 
        { 
            if (DoubleUtil.IsNaN(offset))
            { 
                throw new ArgumentOutOfRangeException(parameterName, SR.Get(SRID.ScrollViewer_CannotBeNaN, parameterName));
            }
            return Math.Max(0.0, offset);
        } 

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

 
        // Helper method to get our ScrollViewer owner and its scrolling content talking.
        // Method introduces the current owner/content, and clears a from any previous content. 
        internal void HookupScrollingComponents() 
        {
            // We need to introduce our IScrollInfo to our ScrollViewer (and break any previous links). 
            ScrollViewer scrollContainer = TemplatedParent as ScrollViewer;

            // If our content is not an IScrollInfo, we should have selected a style that contains one.
            // This (readonly) style contains an AdornerDecorator with a ScrollArea child. 
            if (scrollContainer != null)
            { 
                IScrollInfo si = null; 

                if (CanContentScroll) 
                {
                    // We need to get an IScrollInfo to introduce to the ScrollViewer.
                    // 1. Try our content...
                    si = Content as IScrollInfo; 

                    // 2. Our child might be an ItemsPresenter.  In this case check its child for being an IScrollInfo 
                    if (si == null) 
                    {
                        ItemsPresenter itemsPresenter = Content as ItemsPresenter; 
                        if (itemsPresenter != null)
                        {
                            itemsPresenter.ApplyTemplate();
 
                            int count = VisualTreeHelper.GetChildrenCount(itemsPresenter);
                            if(count > 0) 
                                si = VisualTreeHelper.GetChild(itemsPresenter, 0) as IScrollInfo; 
                        }
                    } 
                }

                // 3. As a final fallback, we use ourself.
                if (si == null) 
                {
                    si = (IScrollInfo)this; 
                    EnsureScrollData(); 
                }
 
                // Detach any differing previous IScrollInfo from ScrollViewer
                if (si != _scrollInfo && _scrollInfo != null)
                {
                    if (IsScrollClient) { _scrollData = null; } 
                    else _scrollInfo.ScrollOwner = null;
                } 
 
                // Introduce our ScrollViewer and IScrollInfo to each other.
                if (si != null) 
                {
                    _scrollInfo = si;                   // At this point, we pass IsScrollClient if si == this.
                    si.ScrollOwner = scrollContainer;
                    scrollContainer.ScrollInfo = si; 
                }
            } 
 
            // We're not really in a valid scrolling scenario.  Break any previous references, and get us
            // back into a totally unlinked state. 
            else if (_scrollInfo != null)
            {
                if (_scrollInfo.ScrollOwner != null) { _scrollInfo.ScrollOwner.ScrollInfo = null; }
                _scrollInfo.ScrollOwner = null; 
                _scrollInfo = null;
                _scrollData = null; 
            } 
        }
 
        // Verifies scrolling data using the passed viewport and extent as newly computed values.
        // Checks the X/Y offset and coerces them into the range [0, Extent - ViewportSize]
        // If extent, viewport, or the newly coerced offsets are different than the existing offset,
        //   cachces are updated and InvalidateScrollInfo() is called. 
        private void VerifyScrollData(Size viewport, Size extent)
        { 
            Debug.Assert(IsScrollClient); 

            bool fValid = true; 

            // These two lines of code are questionable, but they are needed right now as VSB may return
            //  Infinity size from measure, which is a regression from the old scrolling model.
            // They also have the incidental affect of probably avoiding reinvalidation at Arrange 
            //   when inside a parent that measures you to Infinity.
            if (Double.IsInfinity(viewport.Width)) viewport.Width = extent.Width; 
            if (Double.IsInfinity(viewport.Height)) viewport.Height = extent.Height; 

            fValid &= DoubleUtil.AreClose(viewport, _scrollData._viewport); 
            fValid &= DoubleUtil.AreClose(extent, _scrollData._extent);
            _scrollData._viewport = viewport;
            _scrollData._extent = extent;
 
            fValid &= CoerceOffsets();
 
            if (!fValid) 
            {
                ScrollOwner.InvalidateScrollInfo(); 
            }
        }

        // Returns an offset coerced into the [0, Extent - Viewport] range. 
        // Internal because it is also used by other Avalon ISI implementations (just to avoid code duplication).
        static internal double CoerceOffset(double offset, double extent, double viewport) 
        { 
            if (offset > extent - viewport) { offset = extent - viewport; }
            if (offset < 0) { offset = 0; } 
            return offset;
        }

        private bool CoerceOffsets() 
        {
            Debug.Assert(IsScrollClient); 
            Vector computedOffset = new Vector( 
                CoerceOffset(_scrollData._offset.X, _scrollData._extent.Width, _scrollData._viewport.Width),
                CoerceOffset(_scrollData._offset.Y, _scrollData._extent.Height, _scrollData._viewport.Height)); 

            bool fValid = DoubleUtil.AreClose(_scrollData._computedOffset, computedOffset);
            _scrollData._computedOffset = computedOffset;
 
            return fValid;
        } 
 
        // This property is structurally important; we can't do layout without it set right.
        // So, we synchronously make changes. 
        static private void OnCanContentScrollChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ScrollContentPresenter scp = (ScrollContentPresenter)d;
            if (scp._scrollInfo == null) 
            {
                return; 
            } 

// 
            scp.HookupScrollingComponents();
            scp.InvalidateMeasure();
        }
        #endregion 

        //------------------------------------------------------------------- 
        // 
        //  Private Properties
        // 
        //--------------------------------------------------------------------

        #region Private Properties
 
        private bool IsScrollClient
        { 
            get { return (_scrollInfo == this); } 
        }
 
        //
        //  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 42; }
        } 

        #endregion

        //-------------------------------------------------------------------- 
        //
        //  Private Fields 
        // 
        //-------------------------------------------------------------------
 
        #region Private Fields

        // Only one of the following will be used.
        // The _scrollInfo holds a content IScrollInfo implementation that is given to the ScrollViewer. 
        // _scrollData holds values for the scrolling properties we use if we are handling IScrollInfo for the ScrollViewer ourself.
        // ScrollData could implement IScrollInfo, but then the v-table would hurt in the common case as much as we save 
        //   in the less common case. 
        private IScrollInfo _scrollInfo;
        private ScrollData _scrollData; 
        // To hold adorners (caret, &c...) under the clipping region of the scroller.
        private readonly AdornerLayer _adornerLayer;

        #endregion 

        //------------------------------------------------------ 
        // 
        //  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 SCP is delegating to another implementation of ISI.
        // Standard "extra pointer always for less data sometimes" cache savings model:
        // 
        private class ScrollData
        { 
            internal ScrollViewer _scrollOwner; 

            internal bool _canHorizontallyScroll; 
            internal bool _canVerticallyScroll;

            internal Vector _offset;            // Set scroll offset of content.  Positive corresponds to a visually upward offset.
            internal Vector _computedOffset;    // Actual (computed) scroll offset of content. ""  "" 

            internal Size _viewport;    // ViewportSize is computed from our FinalSize, but may be in different units. 
            internal Size _extent;      // Extent is the total size of our content. 
        }
 
        #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