Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Framework / System / Windows / Controls / ScrollViewer.cs / 2 / ScrollViewer.cs
//---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using MS.Internal; using MS.Internal.Commands; using MS.Internal.KnownBoxes; using MS.Internal.PresentationFramework; using MS.Utility; using System; using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Windows.Threading; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Windows.Automation.Provider; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Windows.Markup; using System.Windows.Shapes; namespace System.Windows.Controls { #region ScrollBarVisibility enum ////// ScrollBarVisibilty defines the visibility behavior of a scrollbar. /// public enum ScrollBarVisibility { ////// No scrollbars and no scrolling in this dimension. /// Disabled = 0, ////// The scrollbar should be visible only if there is more content than fits in the viewport. /// Auto, ////// The scrollbar should never be visible. No space should ever be reserved for the scrollbar. /// Hidden, ////// The scrollbar should always be visible. Space should always be reserved for the scrollbar. /// Visible, // NOTE: if you add or remove any values in this enum, be sure to update ScrollViewer.IsValidScrollBarVisibility() } #endregion ////// A ScrollViewer accepts content and provides the logic that allows it to scroll. /// [DefaultEvent("ScrollChangedEvent")] [Localizability(LocalizationCategory.Ignore)] [TemplatePart(Name = "PART_HorizontalScrollBar", Type = typeof(ScrollBar))] [TemplatePart(Name = "PART_VerticalScrollBar", Type = typeof(ScrollBar))] [TemplatePart(Name = "PART_ScrollContentPresenter", Type = typeof(ScrollContentPresenter))] public class ScrollViewer : ContentControl { //------------------------------------------------------------------- // // Public Methods // //------------------------------------------------------------------- #region Public Methods ////// Scroll content by one line to the top. /// public void LineUp() { EnqueueCommand(Commands.LineUp, 0, null); } ////// Scroll content by one line to the bottom. /// public void LineDown() { EnqueueCommand(Commands.LineDown, 0, null); } ////// Scroll content by one line to the left. /// public void LineLeft() { EnqueueCommand(Commands.LineLeft, 0, null); } ////// Scroll content by one line to the right. /// public void LineRight() { EnqueueCommand(Commands.LineRight, 0, null); } ////// Scroll content by one page to the top. /// public void PageUp() { EnqueueCommand(Commands.PageUp, 0, null); } ////// Scroll content by one page to the bottom. /// public void PageDown() { EnqueueCommand(Commands.PageDown, 0, null); } ////// Scroll content by one page to the left. /// public void PageLeft() { EnqueueCommand(Commands.PageLeft, 0, null); } ////// Scroll content by one page to the right. /// public void PageRight() { EnqueueCommand(Commands.PageRight, 0, null); } ////// Horizontally scroll to the beginning of the content. /// public void ScrollToLeftEnd() { EnqueueCommand(Commands.SetHorizontalOffset, Double.NegativeInfinity, null); } ////// Horizontally scroll to the end of the content. /// public void ScrollToRightEnd() { EnqueueCommand(Commands.SetHorizontalOffset, Double.PositiveInfinity, null); } ////// Scroll to Top-Left of the content. /// public void ScrollToHome() { EnqueueCommand(Commands.SetHorizontalOffset, Double.NegativeInfinity, null); EnqueueCommand(Commands.SetVerticalOffset, Double.NegativeInfinity, null); } ////// Scroll to Bottom-Left of the content. /// public void ScrollToEnd() { EnqueueCommand(Commands.SetHorizontalOffset, Double.NegativeInfinity, null); EnqueueCommand(Commands.SetVerticalOffset, Double.PositiveInfinity, null); } ////// Vertically scroll to the beginning of the content. /// public void ScrollToTop() { EnqueueCommand(Commands.SetVerticalOffset, Double.NegativeInfinity, null); } ////// Vertically scroll to the end of the content. /// public void ScrollToBottom() { EnqueueCommand(Commands.SetVerticalOffset, Double.PositiveInfinity, null); } ////// Scroll horizontally to specified offset. Not guaranteed to end up at the specified offset though. /// public void ScrollToHorizontalOffset(double offset) { double validatedOffset = ScrollContentPresenter.ValidateInputOffset(offset, "offset"); // Queue up the scroll command, which tells the content to scroll. // Will lead to an update of all offsets (both live and deferred). EnqueueCommand(Commands.SetHorizontalOffset, validatedOffset, null); } ////// Scroll vertically to specified offset. Not guaranteed to end up at the specified offset though. /// public void ScrollToVerticalOffset(double offset) { double validatedOffset = ScrollContentPresenter.ValidateInputOffset(offset, "offset"); // Queue up the scroll command, which tells the content to scroll. // Will lead to an update of all offsets (both live and deferred). EnqueueCommand(Commands.SetVerticalOffset, validatedOffset, null); } private void DeferScrollToHorizontalOffset(double offset) { double validatedOffset = ScrollContentPresenter.ValidateInputOffset(offset, "offset"); // Update the offset property but not the deferred (content offset) // property, which will be updated when the drag operation is complete. HorizontalOffset = validatedOffset; } private void DeferScrollToVerticalOffset(double offset) { double validatedOffset = ScrollContentPresenter.ValidateInputOffset(offset, "offset"); // Update the offset property but not the deferred (content offset) // property, which will be updated when the drag operation is complete. VerticalOffset = validatedOffset; } internal void MakeVisible(Visual child, Rect rect) { MakeVisibleParams p = new MakeVisibleParams(child, rect); EnqueueCommand(Commands.MakeVisible, 0, p); } private void EnsureLayoutUpdatedHandler() { if (_layoutUpdatedHandler == null) { _layoutUpdatedHandler = new EventHandler(OnLayoutUpdated); LayoutUpdated += _layoutUpdatedHandler; } InvalidateArrange(); //can be that there is no outstanding need to do layout - make sure it is. } private void ClearLayoutUpdatedHandler() { // If queue is not empty - then we still need that handler to make sure queue is being processed. if ((_layoutUpdatedHandler != null) && (_queue.IsEmpty())) { LayoutUpdated -= _layoutUpdatedHandler; _layoutUpdatedHandler = null; } } ////// This function is called by an IScrollInfo attached to this ScrollViewer when any values /// of scrolling properties (Offset, Extent, and ViewportSize) change. The function schedules /// invalidation of other elements like ScrollBars that are dependant on these properties. /// public void InvalidateScrollInfo() { IScrollInfo isi = this.ScrollInfo; //STRESS 1627654: anybody can call this method even if we don't have ISI... if(isi == null) return; //!InMeasure tells us that ScrollInfo has recomputed its extent/viewport //incrementally, not as a result of remeasuring by this ScrollViewer. //that means we should re-run the logic of determining visibility of //autoscrollbars, if we have them. if(!InMeasure) { // // Check if we should remove/add scrollbars. // double extent = ScrollInfo.ExtentWidth; double viewport = ScrollInfo.ViewportWidth; if ( HorizontalScrollBarVisibility == ScrollBarVisibility.Auto && ( ( _scrollVisibilityX == Visibility.Collapsed && DoubleUtil.GreaterThan(extent, viewport)) || ( _scrollVisibilityX == Visibility.Visible && DoubleUtil.LessThanOrClose(extent, viewport)))) { InvalidateMeasure(); } else { extent = ScrollInfo.ExtentHeight; viewport = ScrollInfo.ViewportHeight; if (VerticalScrollBarVisibility == ScrollBarVisibility.Auto && ((_scrollVisibilityY == Visibility.Collapsed && DoubleUtil.GreaterThan(extent, viewport)) || (_scrollVisibilityY == Visibility.Visible && DoubleUtil.LessThanOrClose(extent, viewport)))) { InvalidateMeasure(); } } } // If any scrolling properties have actually changed, fire public events post-layout if ( !DoubleUtil.AreClose(HorizontalOffset, ScrollInfo.HorizontalOffset) || !DoubleUtil.AreClose(VerticalOffset, ScrollInfo.VerticalOffset) || !DoubleUtil.AreClose(ViewportWidth, ScrollInfo.ViewportWidth) || !DoubleUtil.AreClose(ViewportHeight, ScrollInfo.ViewportHeight) || !DoubleUtil.AreClose(ExtentWidth, ScrollInfo.ExtentWidth) || !DoubleUtil.AreClose(ExtentHeight, ScrollInfo.ExtentHeight)) { EnsureLayoutUpdatedHandler(); } } #endregion //-------------------------------------------------------------------- // // Public Properties // //------------------------------------------------------------------- #region Public Properties ////// This property indicates whether the Content should handle scrolling if it can. /// A true value indicates Content should be allowed to scroll if it supports IScrollInfo. /// A false value will always use the default physically scrolling handler. /// public bool CanContentScroll { get { return (bool)GetValue(CanContentScrollProperty); } set { SetValue(CanContentScrollProperty, value); } } ////// HorizonalScollbarVisibility is a [Bindable(true), Category("Appearance")] public ScrollBarVisibility HorizontalScrollBarVisibility { get { return (ScrollBarVisibility) GetValue(HorizontalScrollBarVisibilityProperty); } set { SetValue(HorizontalScrollBarVisibilityProperty, value); } } ///that /// determines if a horizontal scrollbar is shown. /// /// VerticalScrollBarVisibility is a [Bindable(true), Category("Appearance")] public ScrollBarVisibility VerticalScrollBarVisibility { get { return (ScrollBarVisibility) GetValue(VerticalScrollBarVisibilityProperty); } set { SetValue(VerticalScrollBarVisibilityProperty, value); } } ///that /// determines if a vertical scrollbar is shown. /// /// ComputedHorizontalScrollBarVisibility contains the ScrollViewer's current calculation as to /// whether or not scrollbars should be displayed. /// public Visibility ComputedHorizontalScrollBarVisibility { get { return _scrollVisibilityX; } } ////// ComputedVerticalScrollBarVisibility contains the ScrollViewer's current calculation as to /// whether or not scrollbars should be displayed. /// public Visibility ComputedVerticalScrollBarVisibility { get { return _scrollVisibilityY; } } ////// Actual HorizontalOffset contains the ScrollViewer's current horizontal offset. /// This is a computed value, derived from viewport/content size and previous scroll commands /// public double HorizontalOffset { // _xPositionISI is a local cache of GetValue(HorizontalOffsetProperty) // In the future, it could be replaced with the GetValue call. get { return _xPositionISI; } private set { SetValue(HorizontalOffsetPropertyKey, value); } } ////// Actual VerticalOffset contains the ScrollViewer's current Vertical offset. /// This is a computed value, derived from viewport/content size and previous scroll commands /// public double VerticalOffset { // _yPositionISI is a local cache of GetValue(VerticalOffsetProperty) // In the future, it could be replaced with the GetValue call. get { return _yPositionISI; } private set { SetValue(VerticalOffsetPropertyKey, value); } } ////// ExtentWidth contains the horizontal size of the scrolled content element. /// ////// ExtentWidth is only an output property; it can effectively be set by specifying /// [Category("Layout")] public double ExtentWidth { get { return _xExtent; } } ///on the content element. /// /// ExtentHeight contains the vertical size of the scrolled content element. /// ////// ExtentHeight is only an output property; it can effectively be set by specifying /// [Category("Layout")] public double ExtentHeight { get { return _yExtent; } } ///on the content element. /// /// ScrollableWidth contains the horizontal size of the content element that can be scrolled. /// public double ScrollableWidth { get { return Math.Max(0.0, ExtentWidth - ViewportWidth); } } ////// ScrollableHeight contains the vertical size of the content element that can be scrolled. /// public double ScrollableHeight { get { return Math.Max(0.0, ExtentHeight - ViewportHeight); } } ////// ViewportWidth contains the horizontal size of the scrolling viewport. /// ////// ExtentWidth is only an output property; it can effectively be set by specifying /// [Category("Layout")] public double ViewportWidth { get { return _xSize; } } ///on this element. /// /// ViewportHeight contains the vertical size of the scrolling viewport. /// ////// ViewportHeight is only an output property; it can effectively be set by specifying /// [Category("Layout")] public double ViewportHeight { get { return _ySize; } } ///on this element. /// /// DependencyProperty for [CommonDependencyProperty] public static readonly DependencyProperty CanContentScrollProperty = DependencyProperty.RegisterAttached( "CanContentScroll", typeof(bool), typeof(ScrollViewer), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); ///property. /// /// Helper for setting CanContentScroll property. /// public static void SetCanContentScroll(DependencyObject element, bool canContentScroll) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(CanContentScrollProperty, canContentScroll); } ////// Helper for reading CanContentScroll property. /// public static bool GetCanContentScroll(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return ((bool)element.GetValue(CanContentScrollProperty)); } ////// DependencyProperty for [CommonDependencyProperty] public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty = DependencyProperty.RegisterAttached( "HorizontalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(ScrollViewer), new FrameworkPropertyMetadata( ScrollBarVisibility.Disabled, FrameworkPropertyMetadataOptions.AffectsMeasure), new ValidateValueCallback(IsValidScrollBarVisibility)); ///property. /// /// Helper for setting HorizontalScrollBarVisibility property. /// public static void SetHorizontalScrollBarVisibility(DependencyObject element, ScrollBarVisibility horizontalScrollBarVisibility) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(HorizontalScrollBarVisibilityProperty, horizontalScrollBarVisibility); } ////// Helper for reading HorizontalScrollBarVisibility property. /// public static ScrollBarVisibility GetHorizontalScrollBarVisibility(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return ((ScrollBarVisibility)element.GetValue(HorizontalScrollBarVisibilityProperty)); } ////// DependencyProperty for [CommonDependencyProperty] public static readonly DependencyProperty VerticalScrollBarVisibilityProperty = DependencyProperty.RegisterAttached( "VerticalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(ScrollViewer), new FrameworkPropertyMetadata( ScrollBarVisibility.Visible, FrameworkPropertyMetadataOptions.AffectsMeasure), new ValidateValueCallback(IsValidScrollBarVisibility)); ///property. /// /// Helper for setting VerticalScrollBarVisibility property. /// public static void SetVerticalScrollBarVisibility(DependencyObject element, ScrollBarVisibility verticalScrollBarVisibility) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(VerticalScrollBarVisibilityProperty, verticalScrollBarVisibility); } ////// Helper for reading VerticalScrollBarVisibility property. /// public static ScrollBarVisibility GetVerticalScrollBarVisibility(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return ((ScrollBarVisibility)element.GetValue(VerticalScrollBarVisibilityProperty)); } ////// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ComputedHorizontalScrollBarVisibilityPropertyKey = DependencyProperty.RegisterReadOnly( "ComputedHorizontalScrollBarVisibility", typeof(Visibility), typeof(ScrollViewer), new FrameworkPropertyMetadata(Visibility.Visible)); ////// Dependency property that indicates whether horizontal scrollbars should display. The /// value of this property is computed by ScrollViewer; it can be controlled via the /// public static readonly DependencyProperty ComputedHorizontalScrollBarVisibilityProperty = ComputedHorizontalScrollBarVisibilityPropertyKey.DependencyProperty; ////// /// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ComputedVerticalScrollBarVisibilityPropertyKey = DependencyProperty.RegisterReadOnly( "ComputedVerticalScrollBarVisibility", typeof(Visibility), typeof(ScrollViewer), new FrameworkPropertyMetadata(Visibility.Visible)); ////// Dependency property that indicates whether vertical scrollbars should display. The /// value of this property is computed by ScrollViewer; it can be controlled via the /// public static readonly DependencyProperty ComputedVerticalScrollBarVisibilityProperty = ComputedVerticalScrollBarVisibilityPropertyKey.DependencyProperty; ////// /// Actual VerticalOffset. /// private static readonly DependencyPropertyKey VerticalOffsetPropertyKey = DependencyProperty.RegisterReadOnly( "VerticalOffset", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty VerticalOffsetProperty = VerticalOffsetPropertyKey.DependencyProperty; ///property. /// /// HorizontalOffset. /// private static readonly DependencyPropertyKey HorizontalOffsetPropertyKey = DependencyProperty.RegisterReadOnly( "HorizontalOffset", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty HorizontalOffsetProperty = HorizontalOffsetPropertyKey.DependencyProperty; ///property. /// /// When not doing live scrolling, this is the offset value where the /// content is visually located. /// private static readonly DependencyPropertyKey ContentVerticalOffsetPropertyKey = DependencyProperty.RegisterReadOnly( "ContentVerticalOffset", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ContentVerticalOffsetProperty = ContentVerticalOffsetPropertyKey.DependencyProperty; ///property. /// /// When not doing live scrolling, this is the offset value where the /// content is visually located. /// public double ContentVerticalOffset { get { return (double)GetValue(ContentVerticalOffsetProperty); } private set { SetValue(ContentVerticalOffsetPropertyKey, value); } } ////// When not doing live scrolling, this is the offset value where the /// content is visually located. /// private static readonly DependencyPropertyKey ContentHorizontalOffsetPropertyKey = DependencyProperty.RegisterReadOnly( "ContentHorizontalOffset", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ContentHorizontalOffsetProperty = ContentHorizontalOffsetPropertyKey.DependencyProperty; ///property. /// /// When not doing live scrolling, this is the offset value where the /// content is visually located. /// public double ContentHorizontalOffset { get { return (double)GetValue(ContentHorizontalOffsetProperty); } private set { SetValue(ContentHorizontalOffsetPropertyKey, value); } } ////// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ExtentWidthPropertyKey = DependencyProperty.RegisterReadOnly( "ExtentWidth", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ExtentWidthProperty = ExtentWidthPropertyKey.DependencyProperty; ///property. /// /// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ExtentHeightPropertyKey = DependencyProperty.RegisterReadOnly( "ExtentHeight", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ExtentHeightProperty = ExtentHeightPropertyKey.DependencyProperty; ///property. /// /// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ScrollableWidthPropertyKey = DependencyProperty.RegisterReadOnly( "ScrollableWidth", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ScrollableWidthProperty = ScrollableWidthPropertyKey.DependencyProperty; ///property. /// /// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ScrollableHeightPropertyKey = DependencyProperty.RegisterReadOnly( "ScrollableHeight", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ScrollableHeightProperty = ScrollableHeightPropertyKey.DependencyProperty; ///property. /// /// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ViewportWidthPropertyKey = DependencyProperty.RegisterReadOnly( "ViewportWidth", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ViewportWidthProperty = ViewportWidthPropertyKey.DependencyProperty; ///property. /// /// The key needed set a read-only property. /// internal static readonly DependencyPropertyKey ViewportHeightPropertyKey = DependencyProperty.RegisterReadOnly( "ViewportHeight", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ViewportHeightProperty = ViewportHeightPropertyKey.DependencyProperty; ///property. /// /// DependencyProperty that indicates whether the ScrollViewer should /// scroll contents immediately during a thumb drag or defer until /// a drag completes. /// public static readonly DependencyProperty IsDeferredScrollingEnabledProperty = DependencyProperty.RegisterAttached("IsDeferredScrollingEnabled", typeof(bool), typeof(ScrollViewer), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); ////// Gets the value of IsDeferredScrollingEnabled. /// /// The element on which to query the property. ///The value of the property. public static bool GetIsDeferredScrollingEnabled(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (bool)element.GetValue(IsDeferredScrollingEnabledProperty); } ////// Sets the value of IsDeferredScrollingEnabled. /// /// The element on which to set the property. /// The new value of the property. public static void SetIsDeferredScrollingEnabled(DependencyObject element, bool value) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(IsDeferredScrollingEnabledProperty, BooleanBoxes.Box(value)); } ////// Indicates whether the ScrollViewer should scroll contents /// immediately during a thumb drag or defer until a drag completes. /// public bool IsDeferredScrollingEnabled { get { return (bool)GetValue(IsDeferredScrollingEnabledProperty); } set { SetValue(IsDeferredScrollingEnabledProperty, BooleanBoxes.Box(value)); } } #endregion //-------------------------------------------------------------------- // // Public Events (CLR + Avalon) // //-------------------------------------------------------------------- #region Public Events ////// Event ID that corresponds to a change in scrolling state. /// See ScrollChangeEvent for the corresponding event handler. /// public static readonly RoutedEvent ScrollChangedEvent = EventManager.RegisterRoutedEvent( "ScrollChanged", RoutingStrategy.Bubble, typeof(ScrollChangedEventHandler), typeof(ScrollViewer)); ////// Event handler registration for the event fired when scrolling state changes. /// [Category("Action")] public event ScrollChangedEventHandler ScrollChanged { add { AddHandler(ScrollChangedEvent, value); } remove { RemoveHandler(ScrollChangedEvent, value); } } #endregion //------------------------------------------------------------------- // // Protected Methods // //-------------------------------------------------------------------- #region Protected Methods ////// OnScrollChanged is an override called whenever scrolling state changes on this ScrollViewer. /// ////// OnScrollChanged fires the ScrollChangedEvent. Overriders of this method should call /// base.OnScrollChanged(args) if they want the event to be fired. /// /// ScrollChangedEventArgs containing information about the change in scrolling state. protected virtual void OnScrollChanged(ScrollChangedEventArgs e) { // Fire the event. RaiseEvent(e); } ////// ScrollViewer always wants to be hit even when transparent so that it gets input such as MouseWheel. /// protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) { // Assumptions: // 1. Input comes after layout, so Actual* are valid at this point // 2. The clipping part of scrolling is on the SCP, not SV. Thus, Actual* not taking clipping into // account is okay here, barring psychotic styles. Rect rc = new Rect(0, 0, this.ActualWidth, this.ActualHeight); if (rc.Contains(hitTestParameters.HitPoint)) { return new PointHitTestResult(this, hitTestParameters.HitPoint); } else { return null; } } ////// If control has a scrollviewer in its style and has a custom keyboard scrolling behavior when HandlesScrolling should return true. /// Then ScrollViewer will not handle keyboard input and leave it up to the control. /// protected internal override bool HandlesScrolling { get { return true; } } ////// ScrollArea handles keyboard scrolling events. /// ScrollArea handles: Left, Right, Up, Down, PageUp, PageDown, Home, End /// protected override void OnKeyDown(KeyEventArgs e) { if (e.Handled) return; Control templatedParentControl = TemplatedParent as Control; if (templatedParentControl != null && templatedParentControl.HandlesScrolling) return; // If the ScrollViewer has focus or other that arrow key is pressed // then it only scrolls if (e.OriginalSource == this) { ScrollInDirection(e); } // Focus is on the element within the ScrollViewer else { // If arrow key is pressed if (e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up || e.Key == Key.Down) { ScrollContentPresenter viewPort = GetTemplateChild(ScrollContentPresenterTemplateName) as ScrollContentPresenter; // If style changes and ConentSite cannot be found - just scroll and exit if (viewPort == null) { ScrollInDirection(e); return; } FocusNavigationDirection direction = KeyboardNavigation.KeyToTraversalDirection(e.Key); DependencyObject predictedFocus = null; DependencyObject focusedElement = Keyboard.FocusedElement as DependencyObject; bool isFocusWithinViewport = IsInViewport(viewPort, focusedElement); if (isFocusWithinViewport) { // Navigate from current focused element UIElement currentFocusUIElement = focusedElement as UIElement; if (currentFocusUIElement != null) { predictedFocus = currentFocusUIElement.PredictFocus(direction); } else { ContentElement currentFocusContentElement = focusedElement as ContentElement; if (currentFocusContentElement != null) { predictedFocus = currentFocusContentElement.PredictFocus(direction); } else { UIElement3D currentFocusUIElement3D = focusedElement as UIElement3D; if (currentFocusUIElement3D != null) { predictedFocus = currentFocusUIElement3D.PredictFocus(direction); } } } } else { // Navigate from current viewport predictedFocus = viewPort.PredictFocus(direction); } if (predictedFocus == null) { // predictedFocus is null - just scroll ScrollInDirection(e); } else { // Case 1: predictedFocus is entirely in current view port // Action: Set focus to predictedFocus, handle the event and exit if (IsInViewport(viewPort, predictedFocus)) { ((IInputElement)predictedFocus).Focus(); e.Handled = true; } // Case 2: else - predictedFocus is not entirely in the viewport // Scroll in the direction // If predictedFocus is in the new viewport - set focus // handle the event and exit else { ScrollInDirection(e); UpdateLayout(); if (IsInViewport(viewPort, predictedFocus)) { ((IInputElement)predictedFocus).Focus(); } } } } else // If other than arrow Key is down { ScrollInDirection(e); } } } // Returns true only if element is partly visible in the current viewport private bool IsInViewport(ScrollContentPresenter scp, DependencyObject element) { Rect viewPortRect = KeyboardNavigation.GetRectangle(scp); Rect elementRect = KeyboardNavigation.GetRectangle(element); return viewPortRect.IntersectsWith(elementRect); } internal void ScrollInDirection(KeyEventArgs e) { bool fControlDown = ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) != 0); bool fAltDown = ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) != 0); // We don't handle Alt + Key if (!fAltDown) { bool fInvertForRTL = (FlowDirection == FlowDirection.RightToLeft); switch (e.Key) { case Key.Left: if (fInvertForRTL) LineRight(); else LineLeft(); e.Handled = true; break; case Key.Right: if (fInvertForRTL) LineLeft(); else LineRight(); e.Handled = true; break; case Key.Up: LineUp(); e.Handled = true; break; case Key.Down: LineDown(); e.Handled = true; break; case Key.PageUp: PageUp(); e.Handled = true; break; case Key.PageDown: PageDown(); e.Handled = true; break; case Key.Home: if (fControlDown) ScrollToTop(); else ScrollToLeftEnd(); e.Handled = true; break; case Key.End: if (fControlDown) ScrollToBottom(); else ScrollToRightEnd(); e.Handled = true; break; } } } ////// This is the method that responds to the MouseWheel event. /// /// Event Arguments protected override void OnMouseWheel(MouseWheelEventArgs e) { if (e.Handled) { return; } if (!HandlesMouseWheelScrolling) { return; } if (ScrollInfo != null) { if (e.Delta < 0) { ScrollInfo.MouseWheelDown(); } else { ScrollInfo.MouseWheelUp(); } } e.Handled = true; } ////// This is the method that responds to the MouseButtonEvent event. /// /// protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { if (Focus()) e.Handled = true; base.OnMouseLeftButtonDown(e); } ////// Updates DesiredSize of the ScrollViewer. Called by parent UIElement. This is the first pass of layout. /// /// Constraint size is an "upper limit" that the return value should not exceed. ///The ScrollViewer's desired size. protected override Size MeasureOverride(Size constraint) { InChildInvalidateMeasure = false; IScrollInfo isi = this.ScrollInfo; int count = this.VisualChildrenCount; UIElement child = (count > 0) ? this.GetVisualChild(0) as UIElement : null; ScrollBarVisibility vsbv = VerticalScrollBarVisibility; ScrollBarVisibility hsbv = HorizontalScrollBarVisibility; if (child == null) { return new Size(); } bool etwTracingEnabled = EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal); if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.StartEvent, "SCROLLVIEWER:MeasureOverride"); } try { InMeasure = true; bool vsbAuto = (vsbv == ScrollBarVisibility.Auto); bool hsbAuto = (hsbv == ScrollBarVisibility.Auto); bool vDisableScroll = (vsbv == ScrollBarVisibility.Disabled); bool hDisableScroll = (hsbv == ScrollBarVisibility.Disabled); Visibility vv = (vsbv == ScrollBarVisibility.Visible) ? Visibility.Visible : Visibility.Collapsed; Visibility hv = (hsbv == ScrollBarVisibility.Visible) ? Visibility.Visible : Visibility.Collapsed; if (_scrollVisibilityY != vv) { _scrollVisibilityY = vv; SetValue(ComputedVerticalScrollBarVisibilityPropertyKey, _scrollVisibilityY); } if (_scrollVisibilityX != hv) { _scrollVisibilityX = hv; SetValue(ComputedHorizontalScrollBarVisibilityPropertyKey, _scrollVisibilityX); } if (isi != null) { isi.CanHorizontallyScroll = !hDisableScroll; isi.CanVerticallyScroll = !vDisableScroll; } // Measure our visual tree. child.Measure(constraint); //it could now be here as a result of visual template expansion that happens during Measure isi = this.ScrollInfo; if (isi != null && (hsbAuto || vsbAuto)) { bool makeHorizontalBarVisible = hsbAuto && DoubleUtil.GreaterThan(isi.ExtentWidth, isi.ViewportWidth); bool makeVerticalBarVisible = vsbAuto && DoubleUtil.GreaterThan(isi.ExtentHeight, isi.ViewportHeight); if (makeHorizontalBarVisible) { if (_scrollVisibilityX != Visibility.Visible) { _scrollVisibilityX = Visibility.Visible; SetValue(ComputedHorizontalScrollBarVisibilityPropertyKey, _scrollVisibilityX); } } if (makeVerticalBarVisible) { if (_scrollVisibilityY != Visibility.Visible) { _scrollVisibilityY = Visibility.Visible; SetValue(ComputedVerticalScrollBarVisibilityPropertyKey, _scrollVisibilityY); } } if (makeHorizontalBarVisible || makeVerticalBarVisible) { // Remeasure our visual tree. // Requires this extra invalidation because we need to remeasure Grid which is not neccessarily dirty now // since we only invlaidated scrollbars but we don't have LayoutUpdate loop at our disposal here InChildInvalidateMeasure = true; child.InvalidateMeasure(); child.Measure(constraint); } //if both are Auto, then appearance of one scrollbar may causes appearance of another. //If we don't re-check here, we get some part of content covered by auto scrollbar and can never reach to it since //another scrollbar may not appear (in cases when viewport==extent) - bug 1199443 if(hsbAuto && vsbAuto && (makeHorizontalBarVisible != makeVerticalBarVisible)) { bool makeHorizontalBarVisible2 = !makeHorizontalBarVisible && DoubleUtil.GreaterThan(isi.ExtentWidth, isi.ViewportWidth); bool makeVerticalBarVisible2 = !makeVerticalBarVisible && DoubleUtil.GreaterThan(isi.ExtentHeight, isi.ViewportHeight); if(makeHorizontalBarVisible2) { if (_scrollVisibilityX != Visibility.Visible) { _scrollVisibilityX = Visibility.Visible; SetValue(ComputedHorizontalScrollBarVisibilityPropertyKey, _scrollVisibilityX); } } else if (makeVerticalBarVisible2) //only one can be true { if (_scrollVisibilityY != Visibility.Visible) { _scrollVisibilityY = Visibility.Visible; SetValue(ComputedVerticalScrollBarVisibilityPropertyKey, _scrollVisibilityY); } } if (makeHorizontalBarVisible2 || makeVerticalBarVisible2) { // Remeasure our visual tree. // Requires this extra invalidation because we need to remeasure Grid which is not neccessarily dirty now // since we only invlaidated scrollbars but we don't have LayoutUpdate loop at our disposal here InChildInvalidateMeasure = true; child.InvalidateMeasure(); child.Measure(constraint); } } } } finally { InMeasure = false; } if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.EndEvent, "SCROLLVIEWER:MeasureOverride"); } return child.DesiredSize; } private void BindToTemplatedParent(DependencyProperty property) { if (!HasNonDefaultValue(property)) { Binding binding = new Binding(); binding.RelativeSource = RelativeSource.TemplatedParent; binding.Path = new PropertyPath(property); SetBinding(property, binding); } } ////// ScrollViewer binds to the TemplatedParent's attached properties /// if they are not set directly on the ScrollViewer /// internal override void OnPreApplyTemplate() { base.OnPreApplyTemplate(); if (TemplatedParent != null) { BindToTemplatedParent(HorizontalScrollBarVisibilityProperty); BindToTemplatedParent(VerticalScrollBarVisibilityProperty); BindToTemplatedParent(CanContentScrollProperty); BindToTemplatedParent(IsDeferredScrollingEnabledProperty); } } ////// Called when the Template's tree has been generated /// public override void OnApplyTemplate() { base.OnApplyTemplate(); ScrollBar scrollBar = GetTemplateChild(HorizontalScrollBarTemplateName) as ScrollBar; if (scrollBar != null) scrollBar.IsStandalone = false; scrollBar = GetTemplateChild(VerticalScrollBarTemplateName) as ScrollBar; if (scrollBar != null) scrollBar.IsStandalone = false; } #endregion //------------------------------------------------------------------- // // Protected Propeties // //------------------------------------------------------------------- #region Protected Properties ////// The ScrollInfo is the source of scrolling properties (Extent, Offset, and ViewportSize) /// for this ScrollViewer and any of its components like scrollbars. /// protected internal IScrollInfo ScrollInfo { get { return _scrollInfo; } set { _scrollInfo = value; if (_scrollInfo != null) { _scrollInfo.CanHorizontallyScroll = (HorizontalScrollBarVisibility != ScrollBarVisibility.Disabled); _scrollInfo.CanVerticallyScroll = (VerticalScrollBarVisibility != ScrollBarVisibility.Disabled); EnsureQueueProcessing(); } } } #endregion //------------------------------------------------------------------- // // Internal Propeties // //-------------------------------------------------------------------- #region Internal Properties ////// Whether or not the ScrollViewer should handle mouse wheel events. This property was /// specifically introduced for TextBoxBase, to prevent mouse wheel scrolling from "breaking" /// if the mouse pointer happens to land on a TextBoxBase with no more content in the direction /// of the scroll, as with a single-line TextBox. In that scenario, ScrollViewer would /// try to scroll the TextBoxBase and not allow the scroll event to bubble up to an outer /// control even though the TextBoxBase doesn't scroll. /// /// This property defaults to true. TextBoxBase sets it to false. /// internal bool HandlesMouseWheelScrolling { get { return _handlesMouseWheelScrolling; } set { _handlesMouseWheelScrolling = value; } } #endregion Internal Properties //------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------- #region Private Methods private enum Commands { Invalid, LineUp, LineDown, LineLeft, LineRight, PageUp, PageDown, PageLeft, PageRight, SetHorizontalOffset, SetVerticalOffset, MakeVisible, } private struct Command { internal Command(Commands code, double param, MakeVisibleParams mvp) { Code = code; Param = param; MakeVisibleParam = mvp; } internal Commands Code; internal double Param; internal MakeVisibleParams MakeVisibleParam; } private class MakeVisibleParams { internal MakeVisibleParams(Visual child, Rect targetRect) { Child = child; TargetRect = targetRect; } internal Visual Child; internal Rect TargetRect; } // implements ring buffer of commands private struct CommandQueue { private const int _capacity = 32; //returns false if capacity is used up and entry ignored internal void Enqueue(Command command) { if(_lastWritePosition == _lastReadPosition) //buffer is empty { _array = new Command[_capacity]; _lastWritePosition = _lastReadPosition = 0; } if(!OptimizeCommand(command)) //regular insertion, if optimization didn't happen { _lastWritePosition = (_lastWritePosition + 1) % _capacity; if(_lastWritePosition == _lastReadPosition) //buffer is full { // throw away the oldest entry and continue to accumulate fresh input _lastReadPosition = (_lastReadPosition + 1) % _capacity; } _array[_lastWritePosition] = command; } } // this tries to "merge" the incoming command with the accumulated queue // for example, if we get SetHorizontalOffset incoming, all "horizontal" // commands in the queue get removed and replaced with incoming one, // since horizontal position is going to end up at the specified offset anyways. private bool OptimizeCommand(Command command) { if (_lastWritePosition != _lastReadPosition) //buffer has something { if ((command.Code == Commands.SetHorizontalOffset && _array[_lastWritePosition].Code == Commands.SetHorizontalOffset) || (command.Code == Commands.SetVerticalOffset && _array[_lastWritePosition].Code == Commands.SetVerticalOffset) || (command.Code == Commands.MakeVisible && _array[_lastWritePosition].Code == Commands.MakeVisible)) { //if the last command was "set offset" or "make visible", simply replace it and //don't insert new command _array[_lastWritePosition].Param = command.Param; _array[_lastWritePosition].MakeVisibleParam = command.MakeVisibleParam; return true; } } return false; } // returns Invalid command if there is no more commands internal Command Fetch() { if(_lastWritePosition == _lastReadPosition) //buffer is empty { return new Command(Commands.Invalid, 0, null); } _lastReadPosition = (_lastReadPosition + 1) % _capacity; //array exists always if writePos != readPos Command command = _array[_lastReadPosition]; _array[_lastReadPosition].MakeVisibleParam = null; //to release the allocated object if(_lastWritePosition == _lastReadPosition) //it was the last command { _array = null; // make GC work. Hopefully the whole queue is processed in Gen0 } return command; } internal bool IsEmpty() { return (_lastWritePosition == _lastReadPosition); } private int _lastWritePosition; private int _lastReadPosition; private Command[] _array; } //returns true if there was a command sent to ISI private bool ExecuteNextCommand() { IScrollInfo isi = ScrollInfo; if(isi == null) return false; Command cmd = _queue.Fetch(); switch(cmd.Code) { case Commands.LineUp: isi.LineUp(); break; case Commands.LineDown: isi.LineDown(); break; case Commands.LineLeft: isi.LineLeft(); break; case Commands.LineRight: isi.LineRight(); break; case Commands.PageUp: isi.PageUp(); break; case Commands.PageDown: isi.PageDown(); break; case Commands.PageLeft: isi.PageLeft(); break; case Commands.PageRight: isi.PageRight(); break; case Commands.SetHorizontalOffset: isi.SetHorizontalOffset(cmd.Param); break; case Commands.SetVerticalOffset: isi.SetVerticalOffset(cmd.Param); break; case Commands.MakeVisible: { Visual child = cmd.MakeVisibleParam.Child; Visual visi = isi as Visual; if ( child != null && visi != null && (visi == child || visi.IsAncestorOf(child)) // bug 1616807. ISI could be removed from visual tree, // but ScrollViewer.ScrollInfo may not reflect this yet. && this.IsAncestorOf(visi) ) { Rect targetRect = cmd.MakeVisibleParam.TargetRect; if(targetRect.IsEmpty) { UIElement uie = child as UIElement; if(uie != null) targetRect = new Rect(uie.RenderSize); else targetRect = new Rect(); //not a good idea to invoke ISI with Empty rect } // Rect rcNew; if(isi.GetType() == typeof(System.Windows.Controls.ScrollContentPresenter)) { rcNew = ((System.Windows.Controls.ScrollContentPresenter)isi).MakeVisible(child, targetRect, false); } else { rcNew = isi.MakeVisible(child, targetRect); } if (!rcNew.IsEmpty) { GeneralTransform t = visi.TransformToAncestor(this); rcNew = t.TransformBounds(rcNew); } BringIntoView(rcNew); } } break; case Commands.Invalid: return false; } return true; } private void EnqueueCommand(Commands code, double param, MakeVisibleParams mvp) { _queue.Enqueue(new Command(code, param, mvp)); EnsureQueueProcessing(); } private void EnsureQueueProcessing() { if(!_queue.IsEmpty()) { EnsureLayoutUpdatedHandler(); } } // LayoutUpdated event handler. // 1. executes next queued command, if any // 2. If no commands to execute, updates properties and fires events private void OnLayoutUpdated(object sender, EventArgs e) { // if there was a command, execute it and leave the handler for the next pass if(ExecuteNextCommand()) { InvalidateArrange(); return; } double oldActualHorizontalOffset = HorizontalOffset; double oldActualVerticalOffset = VerticalOffset; double oldViewportWidth = ViewportWidth; double oldViewportHeight = ViewportHeight; double oldExtentWidth = ExtentWidth; double oldExtentHeight = ExtentHeight; double oldScrollableWidth = ScrollableWidth; double oldScrollableHeight = ScrollableHeight; bool changed = false; // // Go through scrolling properties updating values. // if (ScrollInfo != null && !DoubleUtil.AreClose(oldActualHorizontalOffset, ScrollInfo.HorizontalOffset)) { _xPositionISI = ScrollInfo.HorizontalOffset; HorizontalOffset = _xPositionISI; ContentHorizontalOffset = _xPositionISI; changed = true; } if (ScrollInfo != null && !DoubleUtil.AreClose(oldActualVerticalOffset, ScrollInfo.VerticalOffset)) { _yPositionISI = ScrollInfo.VerticalOffset; VerticalOffset = _yPositionISI; ContentVerticalOffset = _yPositionISI; changed = true; } if (ScrollInfo != null && !DoubleUtil.AreClose(oldViewportWidth, ScrollInfo.ViewportWidth)) { _xSize = ScrollInfo.ViewportWidth; SetValue(ViewportWidthPropertyKey, _xSize); changed = true; } if (ScrollInfo != null && !DoubleUtil.AreClose(oldViewportHeight, ScrollInfo.ViewportHeight)) { _ySize = ScrollInfo.ViewportHeight; SetValue(ViewportHeightPropertyKey, _ySize); changed = true; } if (ScrollInfo != null && !DoubleUtil.AreClose(oldExtentWidth, ScrollInfo.ExtentWidth)) { _xExtent = ScrollInfo.ExtentWidth; SetValue(ExtentWidthPropertyKey, _xExtent); changed = true; } if (ScrollInfo != null && !DoubleUtil.AreClose(oldExtentHeight, ScrollInfo.ExtentHeight)) { _yExtent = ScrollInfo.ExtentHeight; SetValue(ExtentHeightPropertyKey, _yExtent); changed = true; } // ScrollableWidth/Height are dependant on Viewport and Extent set above. This check must be done after those. double scrollableWidth = ScrollableWidth; if (!DoubleUtil.AreClose(oldScrollableWidth, ScrollableWidth)) { SetValue(ScrollableWidthPropertyKey, scrollableWidth); changed = true; } double scrollableHeight = ScrollableHeight; if (!DoubleUtil.AreClose(oldScrollableHeight, ScrollableHeight)) { SetValue(ScrollableHeightPropertyKey, scrollableHeight); changed = true; } Debug.Assert(DoubleUtil.GreaterThanOrClose(_xSize, 0.0) && DoubleUtil.GreaterThanOrClose(_ySize, 0.0), "Negative size for scrolling viewport. Bad IScrollInfo implementation."); // // Fire scrolling events. // if(changed) { // Fire ScrollChange event ScrollChangedEventArgs args = new ScrollChangedEventArgs( new Vector(HorizontalOffset, VerticalOffset), new Vector(HorizontalOffset - oldActualHorizontalOffset, VerticalOffset - oldActualVerticalOffset), new Size(ExtentWidth, ExtentHeight), new Vector(ExtentWidth - oldExtentWidth, ExtentHeight - oldExtentHeight), new Size(ViewportWidth, ViewportHeight), new Vector(ViewportWidth - oldViewportWidth, ViewportHeight - oldViewportHeight)); args.RoutedEvent = ScrollChangedEvent; args.Source = this; try { OnScrollChanged(args); // Fire automation events if automation is active. ScrollViewerAutomationPeer peer = UIElementAutomationPeer.FromElement(this) as ScrollViewerAutomationPeer; if(peer != null) { peer.RaiseAutomationEvents(oldExtentWidth, oldExtentHeight, oldViewportWidth, oldViewportHeight, oldActualHorizontalOffset, oldActualVerticalOffset); } } finally { // // Disconnect the layout listener. // ClearLayoutUpdatedHandler(); } } ClearLayoutUpdatedHandler(); } ////// Creates AutomationPeer ( protected override AutomationPeer OnCreateAutomationPeer() { return new ScrollViewerAutomationPeer(this); } ///) /// /// OnRequestBringIntoView is called from the event handler ScrollViewer registers for the event. /// The default implementation checks to make sure the visual is a child of the IScrollInfo, and then /// delegates to a method there /// /// The instance handling the event. /// RequestBringIntoViewEventArgs indicates the element and region to scroll into view. private static void OnRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e) { ScrollViewer sv = sender as ScrollViewer; Visual child = e.TargetObject as Visual; //the event starts from the elemetn itself, so if it is an SV.BringINtoView we would //get an SV trying to bring into view itself - this does not work obviously //so don't handle if the request is about ourselves, the event will bubble if(child != null && child != sv && child.IsDescendantOf(sv)) { e.Handled = true; sv.MakeVisible(child, e.TargetRect); } } private static void OnScrollCommand(object target, ExecutedRoutedEventArgs args) { if (args.Command == ScrollBar.DeferScrollToHorizontalOffsetCommand) { if (args.Parameter is double) { ((ScrollViewer)target).DeferScrollToHorizontalOffset((double)args.Parameter); } } else if (args.Command == ScrollBar.DeferScrollToVerticalOffsetCommand) { if (args.Parameter is double) { ((ScrollViewer)target).DeferScrollToVerticalOffset((double)args.Parameter); } } else if (args.Command == ScrollBar.LineLeftCommand) { ((ScrollViewer)target).LineLeft(); } else if (args.Command == ScrollBar.LineRightCommand) { ((ScrollViewer)target).LineRight(); } else if (args.Command == ScrollBar.PageLeftCommand) { ((ScrollViewer)target).PageLeft(); } else if (args.Command == ScrollBar.PageRightCommand) { ((ScrollViewer)target).PageRight(); } else if (args.Command == ScrollBar.LineUpCommand) { ((ScrollViewer)target).LineUp(); } else if (args.Command == ScrollBar.LineDownCommand) { ((ScrollViewer)target).LineDown(); } else if ( args.Command == ScrollBar.PageUpCommand || args.Command == ComponentCommands.ScrollPageUp ) { ((ScrollViewer)target).PageUp(); } else if ( args.Command == ScrollBar.PageDownCommand || args.Command == ComponentCommands.ScrollPageDown ) { ((ScrollViewer)target).PageDown(); } else if (args.Command == ScrollBar.ScrollToEndCommand) { ((ScrollViewer)target).ScrollToEnd(); } else if (args.Command == ScrollBar.ScrollToHomeCommand) { ((ScrollViewer)target).ScrollToHome(); } else if (args.Command == ScrollBar.ScrollToLeftEndCommand) { ((ScrollViewer)target).ScrollToLeftEnd(); } else if (args.Command == ScrollBar.ScrollToRightEndCommand) { ((ScrollViewer)target).ScrollToRightEnd(); } else if (args.Command == ScrollBar.ScrollToTopCommand) { ((ScrollViewer)target).ScrollToTop(); } else if (args.Command == ScrollBar.ScrollToBottomCommand) { ((ScrollViewer)target).ScrollToBottom(); } else if (args.Command == ScrollBar.ScrollToHorizontalOffsetCommand) { if (args.Parameter is double) { ((ScrollViewer)target).ScrollToHorizontalOffset((double)args.Parameter); } } else if (args.Command == ScrollBar.ScrollToVerticalOffsetCommand) { if (args.Parameter is double) { ((ScrollViewer)target).ScrollToVerticalOffset((double)args.Parameter); } } } private static void OnQueryScrollCommand(object target, CanExecuteRoutedEventArgs args) { args.CanExecute = true; // ScrollViewer is capable of execution of the majority of commands. // The only special case is the component commands below. // When scroll viewer is a primitive / part of another control // capable to handle scrolling - scroll viewer leaves it up // to the control to deal with component commands... if ( args.Command == ComponentCommands.ScrollPageUp || args.Command == ComponentCommands.ScrollPageDown ) { ScrollViewer scrollViewer = target as ScrollViewer; Control templatedParentControl = scrollViewer != null ? scrollViewer.TemplatedParent as Control : null; if ( templatedParentControl != null && templatedParentControl.HandlesScrolling ) { args.CanExecute = false; args.ContinueRouting = true; } } else if ((args.Command == ScrollBar.DeferScrollToHorizontalOffsetCommand) || (args.Command == ScrollBar.DeferScrollToVerticalOffsetCommand)) { // The scroll bar has indicated that a drag operation is in progress. // If deferred scrolling is disabled, then mark the command as // not executable so that the scroll bar will fire the regular scroll // command, and the scroll viewer will do live scrolling. ScrollViewer scrollViewer = target as ScrollViewer; if ((scrollViewer != null) && !scrollViewer.IsDeferredScrollingEnabled) { args.CanExecute = false; } } } private static void InitializeCommands() { ExecutedRoutedEventHandler executeScrollCommandEventHandler = new ExecutedRoutedEventHandler(OnScrollCommand); CanExecuteRoutedEventHandler canExecuteScrollCommandEventHandler = new CanExecuteRoutedEventHandler(OnQueryScrollCommand); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.LineLeftCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.LineRightCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.PageLeftCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.PageRightCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.LineUpCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.LineDownCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.PageUpCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.PageDownCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToLeftEndCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToRightEndCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToEndCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToHomeCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToTopCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToBottomCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToHorizontalOffsetCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToVerticalOffsetCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.DeferScrollToHorizontalOffsetCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.DeferScrollToVerticalOffsetCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ComponentCommands.ScrollPageUp, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ComponentCommands.ScrollPageDown, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); } // Creates the default control template for ScrollViewer. private static ControlTemplate CreateDefaultControlTemplate() { ControlTemplate template = null; // Our default style is a 2x2 grid: //// Grid // FrameworkElementFactory grid = new FrameworkElementFactory(typeof(Grid), "Grid"); FrameworkElementFactory gridColumn1 = new FrameworkElementFactory(typeof(ColumnDefinition), "ColumnDefinitionOne"); FrameworkElementFactory gridColumn2 = new FrameworkElementFactory(typeof(ColumnDefinition), "ColumnDefinitionTwo"); FrameworkElementFactory gridRow1 = new FrameworkElementFactory(typeof(RowDefinition), "RowDefinitionOne"); FrameworkElementFactory gridRow2 = new FrameworkElementFactory(typeof(RowDefinition), "RowDefinitionTwo"); FrameworkElementFactory vsb = new FrameworkElementFactory(typeof(ScrollBar), VerticalScrollBarTemplateName); FrameworkElementFactory hsb = new FrameworkElementFactory(typeof(ScrollBar), HorizontalScrollBarTemplateName); FrameworkElementFactory content = new FrameworkElementFactory(typeof(ScrollContentPresenter), ScrollContentPresenterTemplateName); FrameworkElementFactory corner = new FrameworkElementFactory(typeof(Rectangle), "Corner"); // Bind Actual HorizontalOffset to HorizontalScrollBar.Value // Bind Actual VerticalOffset to VerticalScrollbar.Value Binding bindingHorizontalOffset = new Binding("HorizontalOffset"); bindingHorizontalOffset.Mode = BindingMode.OneWay; bindingHorizontalOffset.RelativeSource = RelativeSource.TemplatedParent; Binding bindingVerticalOffset = new Binding("VerticalOffset"); bindingVerticalOffset.Mode = BindingMode.OneWay; bindingVerticalOffset.RelativeSource = RelativeSource.TemplatedParent; grid.SetValue(Grid.BackgroundProperty, new TemplateBindingExtension(BackgroundProperty)); grid.AppendChild(gridColumn1); grid.AppendChild(gridColumn2); grid.AppendChild(gridRow1); grid.AppendChild(gridRow2); grid.AppendChild(corner); grid.AppendChild(content); grid.AppendChild(vsb); grid.AppendChild(hsb); gridColumn1.SetValue(ColumnDefinition.WidthProperty, new GridLength(1.0, GridUnitType.Star)); gridColumn2.SetValue(ColumnDefinition.WidthProperty, new GridLength(1.0, GridUnitType.Auto)); gridRow1.SetValue(RowDefinition.HeightProperty, new GridLength(1.0, GridUnitType.Star)); gridRow2.SetValue(RowDefinition.HeightProperty, new GridLength(1.0, GridUnitType.Auto)); content.SetValue(Grid.ColumnProperty, 0); content.SetValue(Grid.RowProperty, 0); content.SetValue(ContentPresenter.MarginProperty, new TemplateBindingExtension(PaddingProperty)); content.SetValue(ContentProperty, new TemplateBindingExtension(ContentProperty)); content.SetValue(ContentTemplateProperty, new TemplateBindingExtension(ContentTemplateProperty)); content.SetValue(CanContentScrollProperty, new TemplateBindingExtension(CanContentScrollProperty)); hsb.SetValue(ScrollBar.OrientationProperty, Orientation.Horizontal); hsb.SetValue(Grid.ColumnProperty, 0); hsb.SetValue(Grid.RowProperty, 1); hsb.SetValue(RangeBase.MinimumProperty, 0.0); hsb.SetValue(RangeBase.MaximumProperty, new TemplateBindingExtension(ScrollableWidthProperty)); hsb.SetValue(ScrollBar.ViewportSizeProperty, new TemplateBindingExtension(ViewportWidthProperty)); hsb.SetBinding(RangeBase.ValueProperty, bindingHorizontalOffset); hsb.SetValue(UIElement.VisibilityProperty, new TemplateBindingExtension(ComputedHorizontalScrollBarVisibilityProperty)); hsb.SetValue(FrameworkElement.CursorProperty, Cursors.Arrow); hsb.SetValue(AutomationProperties.AutomationIdProperty, "HorizontalScrollBar"); vsb.SetValue(Grid.ColumnProperty, 1); vsb.SetValue(Grid.RowProperty, 0); vsb.SetValue(RangeBase.MinimumProperty, 0.0); vsb.SetValue(RangeBase.MaximumProperty, new TemplateBindingExtension(ScrollableHeightProperty)); vsb.SetValue(ScrollBar.ViewportSizeProperty, new TemplateBindingExtension(ViewportHeightProperty)); vsb.SetBinding(RangeBase.ValueProperty, bindingVerticalOffset); vsb.SetValue(UIElement.VisibilityProperty, new TemplateBindingExtension(ComputedVerticalScrollBarVisibilityProperty)); vsb.SetValue(FrameworkElement.CursorProperty, Cursors.Arrow); vsb.SetValue(AutomationProperties.AutomationIdProperty, "VerticalScrollBar"); corner.SetValue(Grid.ColumnProperty, 1); corner.SetValue(Grid.RowProperty, 1); corner.SetResourceReference(Rectangle.FillProperty, SystemColors.ControlBrushKey); template = new ControlTemplate(typeof(ScrollViewer)); template.VisualTree = grid; template.Seal(); return (template); } #endregion //-------------------------------------------------------------------- // // Private Fields // //------------------------------------------------------------------- #region Private Fields // Scrolling physical "line" metrics. internal const double _scrollLineDelta = 16.0; // Default physical amount to scroll with one Up/Down/Left/Right key internal const double _mouseWheelDelta = 48.0; // Default physical amount to scroll with one MouseWheel. private const string HorizontalScrollBarTemplateName = "PART_HorizontalScrollBar"; private const string VerticalScrollBarTemplateName = "PART_VerticalScrollBar"; internal const string ScrollContentPresenterTemplateName = "PART_ScrollContentPresenter"; // Property caching private Visibility _scrollVisibilityX; private Visibility _scrollVisibilityY; // Scroll property values - cache of what was computed by ISI private double _xPositionISI; private double _yPositionISI; private double _xExtent; private double _yExtent; private double _xSize; private double _ySize; // Event/infrastructure private EventHandler _layoutUpdatedHandler; private IScrollInfo _scrollInfo; private CommandQueue _queue; private bool InMeasure; internal bool InChildInvalidateMeasure = false; private bool _handlesMouseWheelScrolling = true; #endregion //-------------------------------------------------------------------- // // Static Constructors & Delegates // //------------------------------------------------------------------- #region Static Constructors & Delegates static ScrollViewer() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ScrollViewer), new FrameworkPropertyMetadata(typeof(ScrollViewer))); _dType = DependencyObjectType.FromSystemTypeInternal(typeof(ScrollViewer)); InitializeCommands(); ControlTemplate template = CreateDefaultControlTemplate(); Control.TemplateProperty.OverrideMetadata(typeof(ScrollViewer), new FrameworkPropertyMetadata(template)); IsTabStopProperty.OverrideMetadata(typeof(ScrollViewer), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(ScrollViewer), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local)); EventManager.RegisterClassHandler(typeof(ScrollViewer), RequestBringIntoViewEvent, new RequestBringIntoViewEventHandler(OnRequestBringIntoView)); } private static bool IsValidScrollBarVisibility(object o) { ScrollBarVisibility value = (ScrollBarVisibility)o; return (value == ScrollBarVisibility.Disabled || value == ScrollBarVisibility.Auto || value == ScrollBarVisibility.Hidden || value == ScrollBarVisibility.Visible); } // // 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 28; } } #endregion #region DTypeThemeStyleKey // Returns the DependencyObjectType for the registered ThemeStyleKey's default // value. Controls will override this method to return approriate types. internal override DependencyObjectType DTypeThemeStyleKey { get { return _dType; } } private static DependencyObjectType _dType; #endregion DTypeThemeStyleKey } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using MS.Internal; using MS.Internal.Commands; using MS.Internal.KnownBoxes; using MS.Internal.PresentationFramework; using MS.Utility; using System; using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Windows.Threading; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Windows.Automation.Provider; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Windows.Markup; using System.Windows.Shapes; namespace System.Windows.Controls { #region ScrollBarVisibility enum ///// // // // // Cell 1-2, 1-2 // //// // Cell 1, 2 // // Cell 2, 1 // /// ScrollBarVisibilty defines the visibility behavior of a scrollbar. /// public enum ScrollBarVisibility { ////// No scrollbars and no scrolling in this dimension. /// Disabled = 0, ////// The scrollbar should be visible only if there is more content than fits in the viewport. /// Auto, ////// The scrollbar should never be visible. No space should ever be reserved for the scrollbar. /// Hidden, ////// The scrollbar should always be visible. Space should always be reserved for the scrollbar. /// Visible, // NOTE: if you add or remove any values in this enum, be sure to update ScrollViewer.IsValidScrollBarVisibility() } #endregion ////// A ScrollViewer accepts content and provides the logic that allows it to scroll. /// [DefaultEvent("ScrollChangedEvent")] [Localizability(LocalizationCategory.Ignore)] [TemplatePart(Name = "PART_HorizontalScrollBar", Type = typeof(ScrollBar))] [TemplatePart(Name = "PART_VerticalScrollBar", Type = typeof(ScrollBar))] [TemplatePart(Name = "PART_ScrollContentPresenter", Type = typeof(ScrollContentPresenter))] public class ScrollViewer : ContentControl { //------------------------------------------------------------------- // // Public Methods // //------------------------------------------------------------------- #region Public Methods ////// Scroll content by one line to the top. /// public void LineUp() { EnqueueCommand(Commands.LineUp, 0, null); } ////// Scroll content by one line to the bottom. /// public void LineDown() { EnqueueCommand(Commands.LineDown, 0, null); } ////// Scroll content by one line to the left. /// public void LineLeft() { EnqueueCommand(Commands.LineLeft, 0, null); } ////// Scroll content by one line to the right. /// public void LineRight() { EnqueueCommand(Commands.LineRight, 0, null); } ////// Scroll content by one page to the top. /// public void PageUp() { EnqueueCommand(Commands.PageUp, 0, null); } ////// Scroll content by one page to the bottom. /// public void PageDown() { EnqueueCommand(Commands.PageDown, 0, null); } ////// Scroll content by one page to the left. /// public void PageLeft() { EnqueueCommand(Commands.PageLeft, 0, null); } ////// Scroll content by one page to the right. /// public void PageRight() { EnqueueCommand(Commands.PageRight, 0, null); } ////// Horizontally scroll to the beginning of the content. /// public void ScrollToLeftEnd() { EnqueueCommand(Commands.SetHorizontalOffset, Double.NegativeInfinity, null); } ////// Horizontally scroll to the end of the content. /// public void ScrollToRightEnd() { EnqueueCommand(Commands.SetHorizontalOffset, Double.PositiveInfinity, null); } ////// Scroll to Top-Left of the content. /// public void ScrollToHome() { EnqueueCommand(Commands.SetHorizontalOffset, Double.NegativeInfinity, null); EnqueueCommand(Commands.SetVerticalOffset, Double.NegativeInfinity, null); } ////// Scroll to Bottom-Left of the content. /// public void ScrollToEnd() { EnqueueCommand(Commands.SetHorizontalOffset, Double.NegativeInfinity, null); EnqueueCommand(Commands.SetVerticalOffset, Double.PositiveInfinity, null); } ////// Vertically scroll to the beginning of the content. /// public void ScrollToTop() { EnqueueCommand(Commands.SetVerticalOffset, Double.NegativeInfinity, null); } ////// Vertically scroll to the end of the content. /// public void ScrollToBottom() { EnqueueCommand(Commands.SetVerticalOffset, Double.PositiveInfinity, null); } ////// Scroll horizontally to specified offset. Not guaranteed to end up at the specified offset though. /// public void ScrollToHorizontalOffset(double offset) { double validatedOffset = ScrollContentPresenter.ValidateInputOffset(offset, "offset"); // Queue up the scroll command, which tells the content to scroll. // Will lead to an update of all offsets (both live and deferred). EnqueueCommand(Commands.SetHorizontalOffset, validatedOffset, null); } ////// Scroll vertically to specified offset. Not guaranteed to end up at the specified offset though. /// public void ScrollToVerticalOffset(double offset) { double validatedOffset = ScrollContentPresenter.ValidateInputOffset(offset, "offset"); // Queue up the scroll command, which tells the content to scroll. // Will lead to an update of all offsets (both live and deferred). EnqueueCommand(Commands.SetVerticalOffset, validatedOffset, null); } private void DeferScrollToHorizontalOffset(double offset) { double validatedOffset = ScrollContentPresenter.ValidateInputOffset(offset, "offset"); // Update the offset property but not the deferred (content offset) // property, which will be updated when the drag operation is complete. HorizontalOffset = validatedOffset; } private void DeferScrollToVerticalOffset(double offset) { double validatedOffset = ScrollContentPresenter.ValidateInputOffset(offset, "offset"); // Update the offset property but not the deferred (content offset) // property, which will be updated when the drag operation is complete. VerticalOffset = validatedOffset; } internal void MakeVisible(Visual child, Rect rect) { MakeVisibleParams p = new MakeVisibleParams(child, rect); EnqueueCommand(Commands.MakeVisible, 0, p); } private void EnsureLayoutUpdatedHandler() { if (_layoutUpdatedHandler == null) { _layoutUpdatedHandler = new EventHandler(OnLayoutUpdated); LayoutUpdated += _layoutUpdatedHandler; } InvalidateArrange(); //can be that there is no outstanding need to do layout - make sure it is. } private void ClearLayoutUpdatedHandler() { // If queue is not empty - then we still need that handler to make sure queue is being processed. if ((_layoutUpdatedHandler != null) && (_queue.IsEmpty())) { LayoutUpdated -= _layoutUpdatedHandler; _layoutUpdatedHandler = null; } } ////// This function is called by an IScrollInfo attached to this ScrollViewer when any values /// of scrolling properties (Offset, Extent, and ViewportSize) change. The function schedules /// invalidation of other elements like ScrollBars that are dependant on these properties. /// public void InvalidateScrollInfo() { IScrollInfo isi = this.ScrollInfo; //STRESS 1627654: anybody can call this method even if we don't have ISI... if(isi == null) return; //!InMeasure tells us that ScrollInfo has recomputed its extent/viewport //incrementally, not as a result of remeasuring by this ScrollViewer. //that means we should re-run the logic of determining visibility of //autoscrollbars, if we have them. if(!InMeasure) { // // Check if we should remove/add scrollbars. // double extent = ScrollInfo.ExtentWidth; double viewport = ScrollInfo.ViewportWidth; if ( HorizontalScrollBarVisibility == ScrollBarVisibility.Auto && ( ( _scrollVisibilityX == Visibility.Collapsed && DoubleUtil.GreaterThan(extent, viewport)) || ( _scrollVisibilityX == Visibility.Visible && DoubleUtil.LessThanOrClose(extent, viewport)))) { InvalidateMeasure(); } else { extent = ScrollInfo.ExtentHeight; viewport = ScrollInfo.ViewportHeight; if (VerticalScrollBarVisibility == ScrollBarVisibility.Auto && ((_scrollVisibilityY == Visibility.Collapsed && DoubleUtil.GreaterThan(extent, viewport)) || (_scrollVisibilityY == Visibility.Visible && DoubleUtil.LessThanOrClose(extent, viewport)))) { InvalidateMeasure(); } } } // If any scrolling properties have actually changed, fire public events post-layout if ( !DoubleUtil.AreClose(HorizontalOffset, ScrollInfo.HorizontalOffset) || !DoubleUtil.AreClose(VerticalOffset, ScrollInfo.VerticalOffset) || !DoubleUtil.AreClose(ViewportWidth, ScrollInfo.ViewportWidth) || !DoubleUtil.AreClose(ViewportHeight, ScrollInfo.ViewportHeight) || !DoubleUtil.AreClose(ExtentWidth, ScrollInfo.ExtentWidth) || !DoubleUtil.AreClose(ExtentHeight, ScrollInfo.ExtentHeight)) { EnsureLayoutUpdatedHandler(); } } #endregion //-------------------------------------------------------------------- // // Public Properties // //------------------------------------------------------------------- #region Public Properties ////// This property indicates whether the Content should handle scrolling if it can. /// A true value indicates Content should be allowed to scroll if it supports IScrollInfo. /// A false value will always use the default physically scrolling handler. /// public bool CanContentScroll { get { return (bool)GetValue(CanContentScrollProperty); } set { SetValue(CanContentScrollProperty, value); } } ////// HorizonalScollbarVisibility is a [Bindable(true), Category("Appearance")] public ScrollBarVisibility HorizontalScrollBarVisibility { get { return (ScrollBarVisibility) GetValue(HorizontalScrollBarVisibilityProperty); } set { SetValue(HorizontalScrollBarVisibilityProperty, value); } } ///that /// determines if a horizontal scrollbar is shown. /// /// VerticalScrollBarVisibility is a [Bindable(true), Category("Appearance")] public ScrollBarVisibility VerticalScrollBarVisibility { get { return (ScrollBarVisibility) GetValue(VerticalScrollBarVisibilityProperty); } set { SetValue(VerticalScrollBarVisibilityProperty, value); } } ///that /// determines if a vertical scrollbar is shown. /// /// ComputedHorizontalScrollBarVisibility contains the ScrollViewer's current calculation as to /// whether or not scrollbars should be displayed. /// public Visibility ComputedHorizontalScrollBarVisibility { get { return _scrollVisibilityX; } } ////// ComputedVerticalScrollBarVisibility contains the ScrollViewer's current calculation as to /// whether or not scrollbars should be displayed. /// public Visibility ComputedVerticalScrollBarVisibility { get { return _scrollVisibilityY; } } ////// Actual HorizontalOffset contains the ScrollViewer's current horizontal offset. /// This is a computed value, derived from viewport/content size and previous scroll commands /// public double HorizontalOffset { // _xPositionISI is a local cache of GetValue(HorizontalOffsetProperty) // In the future, it could be replaced with the GetValue call. get { return _xPositionISI; } private set { SetValue(HorizontalOffsetPropertyKey, value); } } ////// Actual VerticalOffset contains the ScrollViewer's current Vertical offset. /// This is a computed value, derived from viewport/content size and previous scroll commands /// public double VerticalOffset { // _yPositionISI is a local cache of GetValue(VerticalOffsetProperty) // In the future, it could be replaced with the GetValue call. get { return _yPositionISI; } private set { SetValue(VerticalOffsetPropertyKey, value); } } ////// ExtentWidth contains the horizontal size of the scrolled content element. /// ////// ExtentWidth is only an output property; it can effectively be set by specifying /// [Category("Layout")] public double ExtentWidth { get { return _xExtent; } } ///on the content element. /// /// ExtentHeight contains the vertical size of the scrolled content element. /// ////// ExtentHeight is only an output property; it can effectively be set by specifying /// [Category("Layout")] public double ExtentHeight { get { return _yExtent; } } ///on the content element. /// /// ScrollableWidth contains the horizontal size of the content element that can be scrolled. /// public double ScrollableWidth { get { return Math.Max(0.0, ExtentWidth - ViewportWidth); } } ////// ScrollableHeight contains the vertical size of the content element that can be scrolled. /// public double ScrollableHeight { get { return Math.Max(0.0, ExtentHeight - ViewportHeight); } } ////// ViewportWidth contains the horizontal size of the scrolling viewport. /// ////// ExtentWidth is only an output property; it can effectively be set by specifying /// [Category("Layout")] public double ViewportWidth { get { return _xSize; } } ///on this element. /// /// ViewportHeight contains the vertical size of the scrolling viewport. /// ////// ViewportHeight is only an output property; it can effectively be set by specifying /// [Category("Layout")] public double ViewportHeight { get { return _ySize; } } ///on this element. /// /// DependencyProperty for [CommonDependencyProperty] public static readonly DependencyProperty CanContentScrollProperty = DependencyProperty.RegisterAttached( "CanContentScroll", typeof(bool), typeof(ScrollViewer), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); ///property. /// /// Helper for setting CanContentScroll property. /// public static void SetCanContentScroll(DependencyObject element, bool canContentScroll) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(CanContentScrollProperty, canContentScroll); } ////// Helper for reading CanContentScroll property. /// public static bool GetCanContentScroll(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return ((bool)element.GetValue(CanContentScrollProperty)); } ////// DependencyProperty for [CommonDependencyProperty] public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty = DependencyProperty.RegisterAttached( "HorizontalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(ScrollViewer), new FrameworkPropertyMetadata( ScrollBarVisibility.Disabled, FrameworkPropertyMetadataOptions.AffectsMeasure), new ValidateValueCallback(IsValidScrollBarVisibility)); ///property. /// /// Helper for setting HorizontalScrollBarVisibility property. /// public static void SetHorizontalScrollBarVisibility(DependencyObject element, ScrollBarVisibility horizontalScrollBarVisibility) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(HorizontalScrollBarVisibilityProperty, horizontalScrollBarVisibility); } ////// Helper for reading HorizontalScrollBarVisibility property. /// public static ScrollBarVisibility GetHorizontalScrollBarVisibility(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return ((ScrollBarVisibility)element.GetValue(HorizontalScrollBarVisibilityProperty)); } ////// DependencyProperty for [CommonDependencyProperty] public static readonly DependencyProperty VerticalScrollBarVisibilityProperty = DependencyProperty.RegisterAttached( "VerticalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(ScrollViewer), new FrameworkPropertyMetadata( ScrollBarVisibility.Visible, FrameworkPropertyMetadataOptions.AffectsMeasure), new ValidateValueCallback(IsValidScrollBarVisibility)); ///property. /// /// Helper for setting VerticalScrollBarVisibility property. /// public static void SetVerticalScrollBarVisibility(DependencyObject element, ScrollBarVisibility verticalScrollBarVisibility) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(VerticalScrollBarVisibilityProperty, verticalScrollBarVisibility); } ////// Helper for reading VerticalScrollBarVisibility property. /// public static ScrollBarVisibility GetVerticalScrollBarVisibility(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return ((ScrollBarVisibility)element.GetValue(VerticalScrollBarVisibilityProperty)); } ////// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ComputedHorizontalScrollBarVisibilityPropertyKey = DependencyProperty.RegisterReadOnly( "ComputedHorizontalScrollBarVisibility", typeof(Visibility), typeof(ScrollViewer), new FrameworkPropertyMetadata(Visibility.Visible)); ////// Dependency property that indicates whether horizontal scrollbars should display. The /// value of this property is computed by ScrollViewer; it can be controlled via the /// public static readonly DependencyProperty ComputedHorizontalScrollBarVisibilityProperty = ComputedHorizontalScrollBarVisibilityPropertyKey.DependencyProperty; ////// /// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ComputedVerticalScrollBarVisibilityPropertyKey = DependencyProperty.RegisterReadOnly( "ComputedVerticalScrollBarVisibility", typeof(Visibility), typeof(ScrollViewer), new FrameworkPropertyMetadata(Visibility.Visible)); ////// Dependency property that indicates whether vertical scrollbars should display. The /// value of this property is computed by ScrollViewer; it can be controlled via the /// public static readonly DependencyProperty ComputedVerticalScrollBarVisibilityProperty = ComputedVerticalScrollBarVisibilityPropertyKey.DependencyProperty; ////// /// Actual VerticalOffset. /// private static readonly DependencyPropertyKey VerticalOffsetPropertyKey = DependencyProperty.RegisterReadOnly( "VerticalOffset", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty VerticalOffsetProperty = VerticalOffsetPropertyKey.DependencyProperty; ///property. /// /// HorizontalOffset. /// private static readonly DependencyPropertyKey HorizontalOffsetPropertyKey = DependencyProperty.RegisterReadOnly( "HorizontalOffset", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty HorizontalOffsetProperty = HorizontalOffsetPropertyKey.DependencyProperty; ///property. /// /// When not doing live scrolling, this is the offset value where the /// content is visually located. /// private static readonly DependencyPropertyKey ContentVerticalOffsetPropertyKey = DependencyProperty.RegisterReadOnly( "ContentVerticalOffset", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ContentVerticalOffsetProperty = ContentVerticalOffsetPropertyKey.DependencyProperty; ///property. /// /// When not doing live scrolling, this is the offset value where the /// content is visually located. /// public double ContentVerticalOffset { get { return (double)GetValue(ContentVerticalOffsetProperty); } private set { SetValue(ContentVerticalOffsetPropertyKey, value); } } ////// When not doing live scrolling, this is the offset value where the /// content is visually located. /// private static readonly DependencyPropertyKey ContentHorizontalOffsetPropertyKey = DependencyProperty.RegisterReadOnly( "ContentHorizontalOffset", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ContentHorizontalOffsetProperty = ContentHorizontalOffsetPropertyKey.DependencyProperty; ///property. /// /// When not doing live scrolling, this is the offset value where the /// content is visually located. /// public double ContentHorizontalOffset { get { return (double)GetValue(ContentHorizontalOffsetProperty); } private set { SetValue(ContentHorizontalOffsetPropertyKey, value); } } ////// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ExtentWidthPropertyKey = DependencyProperty.RegisterReadOnly( "ExtentWidth", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ExtentWidthProperty = ExtentWidthPropertyKey.DependencyProperty; ///property. /// /// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ExtentHeightPropertyKey = DependencyProperty.RegisterReadOnly( "ExtentHeight", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ExtentHeightProperty = ExtentHeightPropertyKey.DependencyProperty; ///property. /// /// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ScrollableWidthPropertyKey = DependencyProperty.RegisterReadOnly( "ScrollableWidth", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ScrollableWidthProperty = ScrollableWidthPropertyKey.DependencyProperty; ///property. /// /// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ScrollableHeightPropertyKey = DependencyProperty.RegisterReadOnly( "ScrollableHeight", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ScrollableHeightProperty = ScrollableHeightPropertyKey.DependencyProperty; ///property. /// /// The key needed set a read-only property. /// private static readonly DependencyPropertyKey ViewportWidthPropertyKey = DependencyProperty.RegisterReadOnly( "ViewportWidth", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ViewportWidthProperty = ViewportWidthPropertyKey.DependencyProperty; ///property. /// /// The key needed set a read-only property. /// internal static readonly DependencyPropertyKey ViewportHeightPropertyKey = DependencyProperty.RegisterReadOnly( "ViewportHeight", typeof(double), typeof(ScrollViewer), new FrameworkPropertyMetadata(0d)); ////// DependencyProperty for public static readonly DependencyProperty ViewportHeightProperty = ViewportHeightPropertyKey.DependencyProperty; ///property. /// /// DependencyProperty that indicates whether the ScrollViewer should /// scroll contents immediately during a thumb drag or defer until /// a drag completes. /// public static readonly DependencyProperty IsDeferredScrollingEnabledProperty = DependencyProperty.RegisterAttached("IsDeferredScrollingEnabled", typeof(bool), typeof(ScrollViewer), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); ////// Gets the value of IsDeferredScrollingEnabled. /// /// The element on which to query the property. ///The value of the property. public static bool GetIsDeferredScrollingEnabled(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (bool)element.GetValue(IsDeferredScrollingEnabledProperty); } ////// Sets the value of IsDeferredScrollingEnabled. /// /// The element on which to set the property. /// The new value of the property. public static void SetIsDeferredScrollingEnabled(DependencyObject element, bool value) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(IsDeferredScrollingEnabledProperty, BooleanBoxes.Box(value)); } ////// Indicates whether the ScrollViewer should scroll contents /// immediately during a thumb drag or defer until a drag completes. /// public bool IsDeferredScrollingEnabled { get { return (bool)GetValue(IsDeferredScrollingEnabledProperty); } set { SetValue(IsDeferredScrollingEnabledProperty, BooleanBoxes.Box(value)); } } #endregion //-------------------------------------------------------------------- // // Public Events (CLR + Avalon) // //-------------------------------------------------------------------- #region Public Events ////// Event ID that corresponds to a change in scrolling state. /// See ScrollChangeEvent for the corresponding event handler. /// public static readonly RoutedEvent ScrollChangedEvent = EventManager.RegisterRoutedEvent( "ScrollChanged", RoutingStrategy.Bubble, typeof(ScrollChangedEventHandler), typeof(ScrollViewer)); ////// Event handler registration for the event fired when scrolling state changes. /// [Category("Action")] public event ScrollChangedEventHandler ScrollChanged { add { AddHandler(ScrollChangedEvent, value); } remove { RemoveHandler(ScrollChangedEvent, value); } } #endregion //------------------------------------------------------------------- // // Protected Methods // //-------------------------------------------------------------------- #region Protected Methods ////// OnScrollChanged is an override called whenever scrolling state changes on this ScrollViewer. /// ////// OnScrollChanged fires the ScrollChangedEvent. Overriders of this method should call /// base.OnScrollChanged(args) if they want the event to be fired. /// /// ScrollChangedEventArgs containing information about the change in scrolling state. protected virtual void OnScrollChanged(ScrollChangedEventArgs e) { // Fire the event. RaiseEvent(e); } ////// ScrollViewer always wants to be hit even when transparent so that it gets input such as MouseWheel. /// protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) { // Assumptions: // 1. Input comes after layout, so Actual* are valid at this point // 2. The clipping part of scrolling is on the SCP, not SV. Thus, Actual* not taking clipping into // account is okay here, barring psychotic styles. Rect rc = new Rect(0, 0, this.ActualWidth, this.ActualHeight); if (rc.Contains(hitTestParameters.HitPoint)) { return new PointHitTestResult(this, hitTestParameters.HitPoint); } else { return null; } } ////// If control has a scrollviewer in its style and has a custom keyboard scrolling behavior when HandlesScrolling should return true. /// Then ScrollViewer will not handle keyboard input and leave it up to the control. /// protected internal override bool HandlesScrolling { get { return true; } } ////// ScrollArea handles keyboard scrolling events. /// ScrollArea handles: Left, Right, Up, Down, PageUp, PageDown, Home, End /// protected override void OnKeyDown(KeyEventArgs e) { if (e.Handled) return; Control templatedParentControl = TemplatedParent as Control; if (templatedParentControl != null && templatedParentControl.HandlesScrolling) return; // If the ScrollViewer has focus or other that arrow key is pressed // then it only scrolls if (e.OriginalSource == this) { ScrollInDirection(e); } // Focus is on the element within the ScrollViewer else { // If arrow key is pressed if (e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up || e.Key == Key.Down) { ScrollContentPresenter viewPort = GetTemplateChild(ScrollContentPresenterTemplateName) as ScrollContentPresenter; // If style changes and ConentSite cannot be found - just scroll and exit if (viewPort == null) { ScrollInDirection(e); return; } FocusNavigationDirection direction = KeyboardNavigation.KeyToTraversalDirection(e.Key); DependencyObject predictedFocus = null; DependencyObject focusedElement = Keyboard.FocusedElement as DependencyObject; bool isFocusWithinViewport = IsInViewport(viewPort, focusedElement); if (isFocusWithinViewport) { // Navigate from current focused element UIElement currentFocusUIElement = focusedElement as UIElement; if (currentFocusUIElement != null) { predictedFocus = currentFocusUIElement.PredictFocus(direction); } else { ContentElement currentFocusContentElement = focusedElement as ContentElement; if (currentFocusContentElement != null) { predictedFocus = currentFocusContentElement.PredictFocus(direction); } else { UIElement3D currentFocusUIElement3D = focusedElement as UIElement3D; if (currentFocusUIElement3D != null) { predictedFocus = currentFocusUIElement3D.PredictFocus(direction); } } } } else { // Navigate from current viewport predictedFocus = viewPort.PredictFocus(direction); } if (predictedFocus == null) { // predictedFocus is null - just scroll ScrollInDirection(e); } else { // Case 1: predictedFocus is entirely in current view port // Action: Set focus to predictedFocus, handle the event and exit if (IsInViewport(viewPort, predictedFocus)) { ((IInputElement)predictedFocus).Focus(); e.Handled = true; } // Case 2: else - predictedFocus is not entirely in the viewport // Scroll in the direction // If predictedFocus is in the new viewport - set focus // handle the event and exit else { ScrollInDirection(e); UpdateLayout(); if (IsInViewport(viewPort, predictedFocus)) { ((IInputElement)predictedFocus).Focus(); } } } } else // If other than arrow Key is down { ScrollInDirection(e); } } } // Returns true only if element is partly visible in the current viewport private bool IsInViewport(ScrollContentPresenter scp, DependencyObject element) { Rect viewPortRect = KeyboardNavigation.GetRectangle(scp); Rect elementRect = KeyboardNavigation.GetRectangle(element); return viewPortRect.IntersectsWith(elementRect); } internal void ScrollInDirection(KeyEventArgs e) { bool fControlDown = ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) != 0); bool fAltDown = ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) != 0); // We don't handle Alt + Key if (!fAltDown) { bool fInvertForRTL = (FlowDirection == FlowDirection.RightToLeft); switch (e.Key) { case Key.Left: if (fInvertForRTL) LineRight(); else LineLeft(); e.Handled = true; break; case Key.Right: if (fInvertForRTL) LineLeft(); else LineRight(); e.Handled = true; break; case Key.Up: LineUp(); e.Handled = true; break; case Key.Down: LineDown(); e.Handled = true; break; case Key.PageUp: PageUp(); e.Handled = true; break; case Key.PageDown: PageDown(); e.Handled = true; break; case Key.Home: if (fControlDown) ScrollToTop(); else ScrollToLeftEnd(); e.Handled = true; break; case Key.End: if (fControlDown) ScrollToBottom(); else ScrollToRightEnd(); e.Handled = true; break; } } } ////// This is the method that responds to the MouseWheel event. /// /// Event Arguments protected override void OnMouseWheel(MouseWheelEventArgs e) { if (e.Handled) { return; } if (!HandlesMouseWheelScrolling) { return; } if (ScrollInfo != null) { if (e.Delta < 0) { ScrollInfo.MouseWheelDown(); } else { ScrollInfo.MouseWheelUp(); } } e.Handled = true; } ////// This is the method that responds to the MouseButtonEvent event. /// /// protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { if (Focus()) e.Handled = true; base.OnMouseLeftButtonDown(e); } ////// Updates DesiredSize of the ScrollViewer. Called by parent UIElement. This is the first pass of layout. /// /// Constraint size is an "upper limit" that the return value should not exceed. ///The ScrollViewer's desired size. protected override Size MeasureOverride(Size constraint) { InChildInvalidateMeasure = false; IScrollInfo isi = this.ScrollInfo; int count = this.VisualChildrenCount; UIElement child = (count > 0) ? this.GetVisualChild(0) as UIElement : null; ScrollBarVisibility vsbv = VerticalScrollBarVisibility; ScrollBarVisibility hsbv = HorizontalScrollBarVisibility; if (child == null) { return new Size(); } bool etwTracingEnabled = EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal); if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.StartEvent, "SCROLLVIEWER:MeasureOverride"); } try { InMeasure = true; bool vsbAuto = (vsbv == ScrollBarVisibility.Auto); bool hsbAuto = (hsbv == ScrollBarVisibility.Auto); bool vDisableScroll = (vsbv == ScrollBarVisibility.Disabled); bool hDisableScroll = (hsbv == ScrollBarVisibility.Disabled); Visibility vv = (vsbv == ScrollBarVisibility.Visible) ? Visibility.Visible : Visibility.Collapsed; Visibility hv = (hsbv == ScrollBarVisibility.Visible) ? Visibility.Visible : Visibility.Collapsed; if (_scrollVisibilityY != vv) { _scrollVisibilityY = vv; SetValue(ComputedVerticalScrollBarVisibilityPropertyKey, _scrollVisibilityY); } if (_scrollVisibilityX != hv) { _scrollVisibilityX = hv; SetValue(ComputedHorizontalScrollBarVisibilityPropertyKey, _scrollVisibilityX); } if (isi != null) { isi.CanHorizontallyScroll = !hDisableScroll; isi.CanVerticallyScroll = !vDisableScroll; } // Measure our visual tree. child.Measure(constraint); //it could now be here as a result of visual template expansion that happens during Measure isi = this.ScrollInfo; if (isi != null && (hsbAuto || vsbAuto)) { bool makeHorizontalBarVisible = hsbAuto && DoubleUtil.GreaterThan(isi.ExtentWidth, isi.ViewportWidth); bool makeVerticalBarVisible = vsbAuto && DoubleUtil.GreaterThan(isi.ExtentHeight, isi.ViewportHeight); if (makeHorizontalBarVisible) { if (_scrollVisibilityX != Visibility.Visible) { _scrollVisibilityX = Visibility.Visible; SetValue(ComputedHorizontalScrollBarVisibilityPropertyKey, _scrollVisibilityX); } } if (makeVerticalBarVisible) { if (_scrollVisibilityY != Visibility.Visible) { _scrollVisibilityY = Visibility.Visible; SetValue(ComputedVerticalScrollBarVisibilityPropertyKey, _scrollVisibilityY); } } if (makeHorizontalBarVisible || makeVerticalBarVisible) { // Remeasure our visual tree. // Requires this extra invalidation because we need to remeasure Grid which is not neccessarily dirty now // since we only invlaidated scrollbars but we don't have LayoutUpdate loop at our disposal here InChildInvalidateMeasure = true; child.InvalidateMeasure(); child.Measure(constraint); } //if both are Auto, then appearance of one scrollbar may causes appearance of another. //If we don't re-check here, we get some part of content covered by auto scrollbar and can never reach to it since //another scrollbar may not appear (in cases when viewport==extent) - bug 1199443 if(hsbAuto && vsbAuto && (makeHorizontalBarVisible != makeVerticalBarVisible)) { bool makeHorizontalBarVisible2 = !makeHorizontalBarVisible && DoubleUtil.GreaterThan(isi.ExtentWidth, isi.ViewportWidth); bool makeVerticalBarVisible2 = !makeVerticalBarVisible && DoubleUtil.GreaterThan(isi.ExtentHeight, isi.ViewportHeight); if(makeHorizontalBarVisible2) { if (_scrollVisibilityX != Visibility.Visible) { _scrollVisibilityX = Visibility.Visible; SetValue(ComputedHorizontalScrollBarVisibilityPropertyKey, _scrollVisibilityX); } } else if (makeVerticalBarVisible2) //only one can be true { if (_scrollVisibilityY != Visibility.Visible) { _scrollVisibilityY = Visibility.Visible; SetValue(ComputedVerticalScrollBarVisibilityPropertyKey, _scrollVisibilityY); } } if (makeHorizontalBarVisible2 || makeVerticalBarVisible2) { // Remeasure our visual tree. // Requires this extra invalidation because we need to remeasure Grid which is not neccessarily dirty now // since we only invlaidated scrollbars but we don't have LayoutUpdate loop at our disposal here InChildInvalidateMeasure = true; child.InvalidateMeasure(); child.Measure(constraint); } } } } finally { InMeasure = false; } if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.GENERICSTRINGGUID), MS.Utility.EventType.EndEvent, "SCROLLVIEWER:MeasureOverride"); } return child.DesiredSize; } private void BindToTemplatedParent(DependencyProperty property) { if (!HasNonDefaultValue(property)) { Binding binding = new Binding(); binding.RelativeSource = RelativeSource.TemplatedParent; binding.Path = new PropertyPath(property); SetBinding(property, binding); } } ////// ScrollViewer binds to the TemplatedParent's attached properties /// if they are not set directly on the ScrollViewer /// internal override void OnPreApplyTemplate() { base.OnPreApplyTemplate(); if (TemplatedParent != null) { BindToTemplatedParent(HorizontalScrollBarVisibilityProperty); BindToTemplatedParent(VerticalScrollBarVisibilityProperty); BindToTemplatedParent(CanContentScrollProperty); BindToTemplatedParent(IsDeferredScrollingEnabledProperty); } } ////// Called when the Template's tree has been generated /// public override void OnApplyTemplate() { base.OnApplyTemplate(); ScrollBar scrollBar = GetTemplateChild(HorizontalScrollBarTemplateName) as ScrollBar; if (scrollBar != null) scrollBar.IsStandalone = false; scrollBar = GetTemplateChild(VerticalScrollBarTemplateName) as ScrollBar; if (scrollBar != null) scrollBar.IsStandalone = false; } #endregion //------------------------------------------------------------------- // // Protected Propeties // //------------------------------------------------------------------- #region Protected Properties ////// The ScrollInfo is the source of scrolling properties (Extent, Offset, and ViewportSize) /// for this ScrollViewer and any of its components like scrollbars. /// protected internal IScrollInfo ScrollInfo { get { return _scrollInfo; } set { _scrollInfo = value; if (_scrollInfo != null) { _scrollInfo.CanHorizontallyScroll = (HorizontalScrollBarVisibility != ScrollBarVisibility.Disabled); _scrollInfo.CanVerticallyScroll = (VerticalScrollBarVisibility != ScrollBarVisibility.Disabled); EnsureQueueProcessing(); } } } #endregion //------------------------------------------------------------------- // // Internal Propeties // //-------------------------------------------------------------------- #region Internal Properties ////// Whether or not the ScrollViewer should handle mouse wheel events. This property was /// specifically introduced for TextBoxBase, to prevent mouse wheel scrolling from "breaking" /// if the mouse pointer happens to land on a TextBoxBase with no more content in the direction /// of the scroll, as with a single-line TextBox. In that scenario, ScrollViewer would /// try to scroll the TextBoxBase and not allow the scroll event to bubble up to an outer /// control even though the TextBoxBase doesn't scroll. /// /// This property defaults to true. TextBoxBase sets it to false. /// internal bool HandlesMouseWheelScrolling { get { return _handlesMouseWheelScrolling; } set { _handlesMouseWheelScrolling = value; } } #endregion Internal Properties //------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------- #region Private Methods private enum Commands { Invalid, LineUp, LineDown, LineLeft, LineRight, PageUp, PageDown, PageLeft, PageRight, SetHorizontalOffset, SetVerticalOffset, MakeVisible, } private struct Command { internal Command(Commands code, double param, MakeVisibleParams mvp) { Code = code; Param = param; MakeVisibleParam = mvp; } internal Commands Code; internal double Param; internal MakeVisibleParams MakeVisibleParam; } private class MakeVisibleParams { internal MakeVisibleParams(Visual child, Rect targetRect) { Child = child; TargetRect = targetRect; } internal Visual Child; internal Rect TargetRect; } // implements ring buffer of commands private struct CommandQueue { private const int _capacity = 32; //returns false if capacity is used up and entry ignored internal void Enqueue(Command command) { if(_lastWritePosition == _lastReadPosition) //buffer is empty { _array = new Command[_capacity]; _lastWritePosition = _lastReadPosition = 0; } if(!OptimizeCommand(command)) //regular insertion, if optimization didn't happen { _lastWritePosition = (_lastWritePosition + 1) % _capacity; if(_lastWritePosition == _lastReadPosition) //buffer is full { // throw away the oldest entry and continue to accumulate fresh input _lastReadPosition = (_lastReadPosition + 1) % _capacity; } _array[_lastWritePosition] = command; } } // this tries to "merge" the incoming command with the accumulated queue // for example, if we get SetHorizontalOffset incoming, all "horizontal" // commands in the queue get removed and replaced with incoming one, // since horizontal position is going to end up at the specified offset anyways. private bool OptimizeCommand(Command command) { if (_lastWritePosition != _lastReadPosition) //buffer has something { if ((command.Code == Commands.SetHorizontalOffset && _array[_lastWritePosition].Code == Commands.SetHorizontalOffset) || (command.Code == Commands.SetVerticalOffset && _array[_lastWritePosition].Code == Commands.SetVerticalOffset) || (command.Code == Commands.MakeVisible && _array[_lastWritePosition].Code == Commands.MakeVisible)) { //if the last command was "set offset" or "make visible", simply replace it and //don't insert new command _array[_lastWritePosition].Param = command.Param; _array[_lastWritePosition].MakeVisibleParam = command.MakeVisibleParam; return true; } } return false; } // returns Invalid command if there is no more commands internal Command Fetch() { if(_lastWritePosition == _lastReadPosition) //buffer is empty { return new Command(Commands.Invalid, 0, null); } _lastReadPosition = (_lastReadPosition + 1) % _capacity; //array exists always if writePos != readPos Command command = _array[_lastReadPosition]; _array[_lastReadPosition].MakeVisibleParam = null; //to release the allocated object if(_lastWritePosition == _lastReadPosition) //it was the last command { _array = null; // make GC work. Hopefully the whole queue is processed in Gen0 } return command; } internal bool IsEmpty() { return (_lastWritePosition == _lastReadPosition); } private int _lastWritePosition; private int _lastReadPosition; private Command[] _array; } //returns true if there was a command sent to ISI private bool ExecuteNextCommand() { IScrollInfo isi = ScrollInfo; if(isi == null) return false; Command cmd = _queue.Fetch(); switch(cmd.Code) { case Commands.LineUp: isi.LineUp(); break; case Commands.LineDown: isi.LineDown(); break; case Commands.LineLeft: isi.LineLeft(); break; case Commands.LineRight: isi.LineRight(); break; case Commands.PageUp: isi.PageUp(); break; case Commands.PageDown: isi.PageDown(); break; case Commands.PageLeft: isi.PageLeft(); break; case Commands.PageRight: isi.PageRight(); break; case Commands.SetHorizontalOffset: isi.SetHorizontalOffset(cmd.Param); break; case Commands.SetVerticalOffset: isi.SetVerticalOffset(cmd.Param); break; case Commands.MakeVisible: { Visual child = cmd.MakeVisibleParam.Child; Visual visi = isi as Visual; if ( child != null && visi != null && (visi == child || visi.IsAncestorOf(child)) // bug 1616807. ISI could be removed from visual tree, // but ScrollViewer.ScrollInfo may not reflect this yet. && this.IsAncestorOf(visi) ) { Rect targetRect = cmd.MakeVisibleParam.TargetRect; if(targetRect.IsEmpty) { UIElement uie = child as UIElement; if(uie != null) targetRect = new Rect(uie.RenderSize); else targetRect = new Rect(); //not a good idea to invoke ISI with Empty rect } // Rect rcNew; if(isi.GetType() == typeof(System.Windows.Controls.ScrollContentPresenter)) { rcNew = ((System.Windows.Controls.ScrollContentPresenter)isi).MakeVisible(child, targetRect, false); } else { rcNew = isi.MakeVisible(child, targetRect); } if (!rcNew.IsEmpty) { GeneralTransform t = visi.TransformToAncestor(this); rcNew = t.TransformBounds(rcNew); } BringIntoView(rcNew); } } break; case Commands.Invalid: return false; } return true; } private void EnqueueCommand(Commands code, double param, MakeVisibleParams mvp) { _queue.Enqueue(new Command(code, param, mvp)); EnsureQueueProcessing(); } private void EnsureQueueProcessing() { if(!_queue.IsEmpty()) { EnsureLayoutUpdatedHandler(); } } // LayoutUpdated event handler. // 1. executes next queued command, if any // 2. If no commands to execute, updates properties and fires events private void OnLayoutUpdated(object sender, EventArgs e) { // if there was a command, execute it and leave the handler for the next pass if(ExecuteNextCommand()) { InvalidateArrange(); return; } double oldActualHorizontalOffset = HorizontalOffset; double oldActualVerticalOffset = VerticalOffset; double oldViewportWidth = ViewportWidth; double oldViewportHeight = ViewportHeight; double oldExtentWidth = ExtentWidth; double oldExtentHeight = ExtentHeight; double oldScrollableWidth = ScrollableWidth; double oldScrollableHeight = ScrollableHeight; bool changed = false; // // Go through scrolling properties updating values. // if (ScrollInfo != null && !DoubleUtil.AreClose(oldActualHorizontalOffset, ScrollInfo.HorizontalOffset)) { _xPositionISI = ScrollInfo.HorizontalOffset; HorizontalOffset = _xPositionISI; ContentHorizontalOffset = _xPositionISI; changed = true; } if (ScrollInfo != null && !DoubleUtil.AreClose(oldActualVerticalOffset, ScrollInfo.VerticalOffset)) { _yPositionISI = ScrollInfo.VerticalOffset; VerticalOffset = _yPositionISI; ContentVerticalOffset = _yPositionISI; changed = true; } if (ScrollInfo != null && !DoubleUtil.AreClose(oldViewportWidth, ScrollInfo.ViewportWidth)) { _xSize = ScrollInfo.ViewportWidth; SetValue(ViewportWidthPropertyKey, _xSize); changed = true; } if (ScrollInfo != null && !DoubleUtil.AreClose(oldViewportHeight, ScrollInfo.ViewportHeight)) { _ySize = ScrollInfo.ViewportHeight; SetValue(ViewportHeightPropertyKey, _ySize); changed = true; } if (ScrollInfo != null && !DoubleUtil.AreClose(oldExtentWidth, ScrollInfo.ExtentWidth)) { _xExtent = ScrollInfo.ExtentWidth; SetValue(ExtentWidthPropertyKey, _xExtent); changed = true; } if (ScrollInfo != null && !DoubleUtil.AreClose(oldExtentHeight, ScrollInfo.ExtentHeight)) { _yExtent = ScrollInfo.ExtentHeight; SetValue(ExtentHeightPropertyKey, _yExtent); changed = true; } // ScrollableWidth/Height are dependant on Viewport and Extent set above. This check must be done after those. double scrollableWidth = ScrollableWidth; if (!DoubleUtil.AreClose(oldScrollableWidth, ScrollableWidth)) { SetValue(ScrollableWidthPropertyKey, scrollableWidth); changed = true; } double scrollableHeight = ScrollableHeight; if (!DoubleUtil.AreClose(oldScrollableHeight, ScrollableHeight)) { SetValue(ScrollableHeightPropertyKey, scrollableHeight); changed = true; } Debug.Assert(DoubleUtil.GreaterThanOrClose(_xSize, 0.0) && DoubleUtil.GreaterThanOrClose(_ySize, 0.0), "Negative size for scrolling viewport. Bad IScrollInfo implementation."); // // Fire scrolling events. // if(changed) { // Fire ScrollChange event ScrollChangedEventArgs args = new ScrollChangedEventArgs( new Vector(HorizontalOffset, VerticalOffset), new Vector(HorizontalOffset - oldActualHorizontalOffset, VerticalOffset - oldActualVerticalOffset), new Size(ExtentWidth, ExtentHeight), new Vector(ExtentWidth - oldExtentWidth, ExtentHeight - oldExtentHeight), new Size(ViewportWidth, ViewportHeight), new Vector(ViewportWidth - oldViewportWidth, ViewportHeight - oldViewportHeight)); args.RoutedEvent = ScrollChangedEvent; args.Source = this; try { OnScrollChanged(args); // Fire automation events if automation is active. ScrollViewerAutomationPeer peer = UIElementAutomationPeer.FromElement(this) as ScrollViewerAutomationPeer; if(peer != null) { peer.RaiseAutomationEvents(oldExtentWidth, oldExtentHeight, oldViewportWidth, oldViewportHeight, oldActualHorizontalOffset, oldActualVerticalOffset); } } finally { // // Disconnect the layout listener. // ClearLayoutUpdatedHandler(); } } ClearLayoutUpdatedHandler(); } ////// Creates AutomationPeer ( protected override AutomationPeer OnCreateAutomationPeer() { return new ScrollViewerAutomationPeer(this); } ///) /// /// OnRequestBringIntoView is called from the event handler ScrollViewer registers for the event. /// The default implementation checks to make sure the visual is a child of the IScrollInfo, and then /// delegates to a method there /// /// The instance handling the event. /// RequestBringIntoViewEventArgs indicates the element and region to scroll into view. private static void OnRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e) { ScrollViewer sv = sender as ScrollViewer; Visual child = e.TargetObject as Visual; //the event starts from the elemetn itself, so if it is an SV.BringINtoView we would //get an SV trying to bring into view itself - this does not work obviously //so don't handle if the request is about ourselves, the event will bubble if(child != null && child != sv && child.IsDescendantOf(sv)) { e.Handled = true; sv.MakeVisible(child, e.TargetRect); } } private static void OnScrollCommand(object target, ExecutedRoutedEventArgs args) { if (args.Command == ScrollBar.DeferScrollToHorizontalOffsetCommand) { if (args.Parameter is double) { ((ScrollViewer)target).DeferScrollToHorizontalOffset((double)args.Parameter); } } else if (args.Command == ScrollBar.DeferScrollToVerticalOffsetCommand) { if (args.Parameter is double) { ((ScrollViewer)target).DeferScrollToVerticalOffset((double)args.Parameter); } } else if (args.Command == ScrollBar.LineLeftCommand) { ((ScrollViewer)target).LineLeft(); } else if (args.Command == ScrollBar.LineRightCommand) { ((ScrollViewer)target).LineRight(); } else if (args.Command == ScrollBar.PageLeftCommand) { ((ScrollViewer)target).PageLeft(); } else if (args.Command == ScrollBar.PageRightCommand) { ((ScrollViewer)target).PageRight(); } else if (args.Command == ScrollBar.LineUpCommand) { ((ScrollViewer)target).LineUp(); } else if (args.Command == ScrollBar.LineDownCommand) { ((ScrollViewer)target).LineDown(); } else if ( args.Command == ScrollBar.PageUpCommand || args.Command == ComponentCommands.ScrollPageUp ) { ((ScrollViewer)target).PageUp(); } else if ( args.Command == ScrollBar.PageDownCommand || args.Command == ComponentCommands.ScrollPageDown ) { ((ScrollViewer)target).PageDown(); } else if (args.Command == ScrollBar.ScrollToEndCommand) { ((ScrollViewer)target).ScrollToEnd(); } else if (args.Command == ScrollBar.ScrollToHomeCommand) { ((ScrollViewer)target).ScrollToHome(); } else if (args.Command == ScrollBar.ScrollToLeftEndCommand) { ((ScrollViewer)target).ScrollToLeftEnd(); } else if (args.Command == ScrollBar.ScrollToRightEndCommand) { ((ScrollViewer)target).ScrollToRightEnd(); } else if (args.Command == ScrollBar.ScrollToTopCommand) { ((ScrollViewer)target).ScrollToTop(); } else if (args.Command == ScrollBar.ScrollToBottomCommand) { ((ScrollViewer)target).ScrollToBottom(); } else if (args.Command == ScrollBar.ScrollToHorizontalOffsetCommand) { if (args.Parameter is double) { ((ScrollViewer)target).ScrollToHorizontalOffset((double)args.Parameter); } } else if (args.Command == ScrollBar.ScrollToVerticalOffsetCommand) { if (args.Parameter is double) { ((ScrollViewer)target).ScrollToVerticalOffset((double)args.Parameter); } } } private static void OnQueryScrollCommand(object target, CanExecuteRoutedEventArgs args) { args.CanExecute = true; // ScrollViewer is capable of execution of the majority of commands. // The only special case is the component commands below. // When scroll viewer is a primitive / part of another control // capable to handle scrolling - scroll viewer leaves it up // to the control to deal with component commands... if ( args.Command == ComponentCommands.ScrollPageUp || args.Command == ComponentCommands.ScrollPageDown ) { ScrollViewer scrollViewer = target as ScrollViewer; Control templatedParentControl = scrollViewer != null ? scrollViewer.TemplatedParent as Control : null; if ( templatedParentControl != null && templatedParentControl.HandlesScrolling ) { args.CanExecute = false; args.ContinueRouting = true; } } else if ((args.Command == ScrollBar.DeferScrollToHorizontalOffsetCommand) || (args.Command == ScrollBar.DeferScrollToVerticalOffsetCommand)) { // The scroll bar has indicated that a drag operation is in progress. // If deferred scrolling is disabled, then mark the command as // not executable so that the scroll bar will fire the regular scroll // command, and the scroll viewer will do live scrolling. ScrollViewer scrollViewer = target as ScrollViewer; if ((scrollViewer != null) && !scrollViewer.IsDeferredScrollingEnabled) { args.CanExecute = false; } } } private static void InitializeCommands() { ExecutedRoutedEventHandler executeScrollCommandEventHandler = new ExecutedRoutedEventHandler(OnScrollCommand); CanExecuteRoutedEventHandler canExecuteScrollCommandEventHandler = new CanExecuteRoutedEventHandler(OnQueryScrollCommand); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.LineLeftCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.LineRightCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.PageLeftCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.PageRightCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.LineUpCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.LineDownCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.PageUpCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.PageDownCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToLeftEndCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToRightEndCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToEndCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToHomeCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToTopCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToBottomCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToHorizontalOffsetCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.ScrollToVerticalOffsetCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.DeferScrollToHorizontalOffsetCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ScrollBar.DeferScrollToVerticalOffsetCommand, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ComponentCommands.ScrollPageUp, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); CommandHelpers.RegisterCommandHandler(typeof(ScrollViewer), ComponentCommands.ScrollPageDown, executeScrollCommandEventHandler, canExecuteScrollCommandEventHandler); } // Creates the default control template for ScrollViewer. private static ControlTemplate CreateDefaultControlTemplate() { ControlTemplate template = null; // Our default style is a 2x2 grid: //// Grid // FrameworkElementFactory grid = new FrameworkElementFactory(typeof(Grid), "Grid"); FrameworkElementFactory gridColumn1 = new FrameworkElementFactory(typeof(ColumnDefinition), "ColumnDefinitionOne"); FrameworkElementFactory gridColumn2 = new FrameworkElementFactory(typeof(ColumnDefinition), "ColumnDefinitionTwo"); FrameworkElementFactory gridRow1 = new FrameworkElementFactory(typeof(RowDefinition), "RowDefinitionOne"); FrameworkElementFactory gridRow2 = new FrameworkElementFactory(typeof(RowDefinition), "RowDefinitionTwo"); FrameworkElementFactory vsb = new FrameworkElementFactory(typeof(ScrollBar), VerticalScrollBarTemplateName); FrameworkElementFactory hsb = new FrameworkElementFactory(typeof(ScrollBar), HorizontalScrollBarTemplateName); FrameworkElementFactory content = new FrameworkElementFactory(typeof(ScrollContentPresenter), ScrollContentPresenterTemplateName); FrameworkElementFactory corner = new FrameworkElementFactory(typeof(Rectangle), "Corner"); // Bind Actual HorizontalOffset to HorizontalScrollBar.Value // Bind Actual VerticalOffset to VerticalScrollbar.Value Binding bindingHorizontalOffset = new Binding("HorizontalOffset"); bindingHorizontalOffset.Mode = BindingMode.OneWay; bindingHorizontalOffset.RelativeSource = RelativeSource.TemplatedParent; Binding bindingVerticalOffset = new Binding("VerticalOffset"); bindingVerticalOffset.Mode = BindingMode.OneWay; bindingVerticalOffset.RelativeSource = RelativeSource.TemplatedParent; grid.SetValue(Grid.BackgroundProperty, new TemplateBindingExtension(BackgroundProperty)); grid.AppendChild(gridColumn1); grid.AppendChild(gridColumn2); grid.AppendChild(gridRow1); grid.AppendChild(gridRow2); grid.AppendChild(corner); grid.AppendChild(content); grid.AppendChild(vsb); grid.AppendChild(hsb); gridColumn1.SetValue(ColumnDefinition.WidthProperty, new GridLength(1.0, GridUnitType.Star)); gridColumn2.SetValue(ColumnDefinition.WidthProperty, new GridLength(1.0, GridUnitType.Auto)); gridRow1.SetValue(RowDefinition.HeightProperty, new GridLength(1.0, GridUnitType.Star)); gridRow2.SetValue(RowDefinition.HeightProperty, new GridLength(1.0, GridUnitType.Auto)); content.SetValue(Grid.ColumnProperty, 0); content.SetValue(Grid.RowProperty, 0); content.SetValue(ContentPresenter.MarginProperty, new TemplateBindingExtension(PaddingProperty)); content.SetValue(ContentProperty, new TemplateBindingExtension(ContentProperty)); content.SetValue(ContentTemplateProperty, new TemplateBindingExtension(ContentTemplateProperty)); content.SetValue(CanContentScrollProperty, new TemplateBindingExtension(CanContentScrollProperty)); hsb.SetValue(ScrollBar.OrientationProperty, Orientation.Horizontal); hsb.SetValue(Grid.ColumnProperty, 0); hsb.SetValue(Grid.RowProperty, 1); hsb.SetValue(RangeBase.MinimumProperty, 0.0); hsb.SetValue(RangeBase.MaximumProperty, new TemplateBindingExtension(ScrollableWidthProperty)); hsb.SetValue(ScrollBar.ViewportSizeProperty, new TemplateBindingExtension(ViewportWidthProperty)); hsb.SetBinding(RangeBase.ValueProperty, bindingHorizontalOffset); hsb.SetValue(UIElement.VisibilityProperty, new TemplateBindingExtension(ComputedHorizontalScrollBarVisibilityProperty)); hsb.SetValue(FrameworkElement.CursorProperty, Cursors.Arrow); hsb.SetValue(AutomationProperties.AutomationIdProperty, "HorizontalScrollBar"); vsb.SetValue(Grid.ColumnProperty, 1); vsb.SetValue(Grid.RowProperty, 0); vsb.SetValue(RangeBase.MinimumProperty, 0.0); vsb.SetValue(RangeBase.MaximumProperty, new TemplateBindingExtension(ScrollableHeightProperty)); vsb.SetValue(ScrollBar.ViewportSizeProperty, new TemplateBindingExtension(ViewportHeightProperty)); vsb.SetBinding(RangeBase.ValueProperty, bindingVerticalOffset); vsb.SetValue(UIElement.VisibilityProperty, new TemplateBindingExtension(ComputedVerticalScrollBarVisibilityProperty)); vsb.SetValue(FrameworkElement.CursorProperty, Cursors.Arrow); vsb.SetValue(AutomationProperties.AutomationIdProperty, "VerticalScrollBar"); corner.SetValue(Grid.ColumnProperty, 1); corner.SetValue(Grid.RowProperty, 1); corner.SetResourceReference(Rectangle.FillProperty, SystemColors.ControlBrushKey); template = new ControlTemplate(typeof(ScrollViewer)); template.VisualTree = grid; template.Seal(); return (template); } #endregion //-------------------------------------------------------------------- // // Private Fields // //------------------------------------------------------------------- #region Private Fields // Scrolling physical "line" metrics. internal const double _scrollLineDelta = 16.0; // Default physical amount to scroll with one Up/Down/Left/Right key internal const double _mouseWheelDelta = 48.0; // Default physical amount to scroll with one MouseWheel. private const string HorizontalScrollBarTemplateName = "PART_HorizontalScrollBar"; private const string VerticalScrollBarTemplateName = "PART_VerticalScrollBar"; internal const string ScrollContentPresenterTemplateName = "PART_ScrollContentPresenter"; // Property caching private Visibility _scrollVisibilityX; private Visibility _scrollVisibilityY; // Scroll property values - cache of what was computed by ISI private double _xPositionISI; private double _yPositionISI; private double _xExtent; private double _yExtent; private double _xSize; private double _ySize; // Event/infrastructure private EventHandler _layoutUpdatedHandler; private IScrollInfo _scrollInfo; private CommandQueue _queue; private bool InMeasure; internal bool InChildInvalidateMeasure = false; private bool _handlesMouseWheelScrolling = true; #endregion //-------------------------------------------------------------------- // // Static Constructors & Delegates // //------------------------------------------------------------------- #region Static Constructors & Delegates static ScrollViewer() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ScrollViewer), new FrameworkPropertyMetadata(typeof(ScrollViewer))); _dType = DependencyObjectType.FromSystemTypeInternal(typeof(ScrollViewer)); InitializeCommands(); ControlTemplate template = CreateDefaultControlTemplate(); Control.TemplateProperty.OverrideMetadata(typeof(ScrollViewer), new FrameworkPropertyMetadata(template)); IsTabStopProperty.OverrideMetadata(typeof(ScrollViewer), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(ScrollViewer), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local)); EventManager.RegisterClassHandler(typeof(ScrollViewer), RequestBringIntoViewEvent, new RequestBringIntoViewEventHandler(OnRequestBringIntoView)); } private static bool IsValidScrollBarVisibility(object o) { ScrollBarVisibility value = (ScrollBarVisibility)o; return (value == ScrollBarVisibility.Disabled || value == ScrollBarVisibility.Auto || value == ScrollBarVisibility.Hidden || value == ScrollBarVisibility.Visible); } // // 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 28; } } #endregion #region DTypeThemeStyleKey // Returns the DependencyObjectType for the registered ThemeStyleKey's default // value. Controls will override this method to return approriate types. internal override DependencyObjectType DTypeThemeStyleKey { get { return _dType; } } private static DependencyObjectType _dType; #endregion DTypeThemeStyleKey } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.// // // // // Cell 1-2, 1-2 // //// // Cell 1, 2 // // Cell 2, 1 //
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SystemNetHelpers.cs
- ColorConvertedBitmap.cs
- DateTimePicker.cs
- CompilerScope.cs
- StateWorkerRequest.cs
- CngAlgorithm.cs
- OptionalColumn.cs
- infer.cs
- Int32.cs
- DesignerUtils.cs
- DbModificationCommandTree.cs
- IDispatchConstantAttribute.cs
- RequestCacheValidator.cs
- DebugInfoExpression.cs
- NonPrimarySelectionGlyph.cs
- ProviderUtil.cs
- X509CertificateClaimSet.cs
- StringResourceManager.cs
- DataServiceRequestArgs.cs
- ActivityStatusChangeEventArgs.cs
- CompModHelpers.cs
- ParameterCollection.cs
- RtfToXamlReader.cs
- QueryOptionExpression.cs
- ComponentChangingEvent.cs
- ZipIOExtraFieldZip64Element.cs
- SubMenuStyle.cs
- CopyOfAction.cs
- XmlLanguageConverter.cs
- VectorAnimation.cs
- CompilerCollection.cs
- AttributeExtensions.cs
- GlyphRunDrawing.cs
- WebPartZone.cs
- DesignBindingPicker.cs
- SwitchElementsCollection.cs
- Column.cs
- SqlTriggerContext.cs
- TypeSystem.cs
- TriggerActionCollection.cs
- SchemaImporterExtensionElementCollection.cs
- QilParameter.cs
- ConfigXmlText.cs
- DragDrop.cs
- CheckBoxList.cs
- StringArrayConverter.cs
- ControlValuePropertyAttribute.cs
- SymLanguageType.cs
- CodeExpressionCollection.cs
- ToolStripArrowRenderEventArgs.cs
- Propagator.cs
- AsymmetricSignatureFormatter.cs
- PackWebRequest.cs
- ConditionalAttribute.cs
- Attribute.cs
- TryLoadRunnableWorkflowCommand.cs
- StylusPointProperty.cs
- ScriptDescriptor.cs
- StatusBarItemAutomationPeer.cs
- QueryCacheManager.cs
- NewArrayExpression.cs
- CompositeScriptReference.cs
- OleDbReferenceCollection.cs
- TextChange.cs
- OracleBoolean.cs
- DesigntimeLicenseContextSerializer.cs
- SwitchDesigner.xaml.cs
- PeerNameRegistration.cs
- ListCommandEventArgs.cs
- RadioButtonList.cs
- WebPartHeaderCloseVerb.cs
- Compiler.cs
- TreeViewImageIndexConverter.cs
- DbDataReader.cs
- PrintEvent.cs
- XPathBinder.cs
- WebBaseEventKeyComparer.cs
- SocketInformation.cs
- SimpleNameService.cs
- SessionPageStatePersister.cs
- HwndSourceParameters.cs
- Splitter.cs
- ContravarianceAdapter.cs
- hresults.cs
- Int32CAMarshaler.cs
- SqlExpander.cs
- MDIClient.cs
- ConfigViewGenerator.cs
- Ref.cs
- DbDataAdapter.cs
- TimeIntervalCollection.cs
- GZipDecoder.cs
- DispatcherSynchronizationContext.cs
- XmlCollation.cs
- CrossAppDomainChannel.cs
- MenuItem.cs
- NotifyCollectionChangedEventArgs.cs
- securitycriticaldata.cs
- RijndaelManagedTransform.cs
- TreeViewAutomationPeer.cs