Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / Controls / ScrollViewer.cs / 1 / 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 that
/// determines if a horizontal scrollbar is shown.
///
[Bindable(true), Category("Appearance")]
public ScrollBarVisibility HorizontalScrollBarVisibility
{
get { return (ScrollBarVisibility) GetValue(HorizontalScrollBarVisibilityProperty); }
set { SetValue(HorizontalScrollBarVisibilityProperty, value); }
}
///
/// VerticalScrollBarVisibility is a that
/// determines if a vertical scrollbar is shown.
///
[Bindable(true), Category("Appearance")]
public ScrollBarVisibility VerticalScrollBarVisibility
{
get { return (ScrollBarVisibility) GetValue(VerticalScrollBarVisibilityProperty); }
set { SetValue(VerticalScrollBarVisibilityProperty, value); }
}
///
/// 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
/// on the content element.
///
[Category("Layout")]
public double ExtentWidth
{
get { return _xExtent; }
}
///
/// ExtentHeight contains the vertical size of the scrolled content element.
///
///
/// ExtentHeight is only an output property; it can effectively be set by specifying
/// on the content element.
///
[Category("Layout")]
public double ExtentHeight
{
get { return _yExtent; }
}
///
/// 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
/// on this element.
///
[Category("Layout")]
public double ViewportWidth
{
get { return _xSize; }
}
///
/// ViewportHeight contains the vertical size of the scrolling viewport.
///
///
/// ViewportHeight is only an output property; it can effectively be set by specifying
/// on this element.
///
[Category("Layout")]
public double ViewportHeight
{
get { return _ySize; }
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty CanContentScrollProperty =
DependencyProperty.RegisterAttached(
"CanContentScroll",
typeof(bool),
typeof(ScrollViewer),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
///
/// 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 property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty =
DependencyProperty.RegisterAttached(
"HorizontalScrollBarVisibility",
typeof(ScrollBarVisibility),
typeof(ScrollViewer),
new FrameworkPropertyMetadata(
ScrollBarVisibility.Disabled,
FrameworkPropertyMetadataOptions.AffectsMeasure),
new ValidateValueCallback(IsValidScrollBarVisibility));
///
/// 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 property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty VerticalScrollBarVisibilityProperty =
DependencyProperty.RegisterAttached(
"VerticalScrollBarVisibility",
typeof(ScrollBarVisibility),
typeof(ScrollViewer),
new FrameworkPropertyMetadata(
ScrollBarVisibility.Visible,
FrameworkPropertyMetadataOptions.AffectsMeasure),
new ValidateValueCallback(IsValidScrollBarVisibility));
///
/// 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 property.
///
public static readonly DependencyProperty VerticalOffsetProperty =
VerticalOffsetPropertyKey.DependencyProperty;
///
/// HorizontalOffset.
///
private static readonly DependencyPropertyKey HorizontalOffsetPropertyKey =
DependencyProperty.RegisterReadOnly(
"HorizontalOffset",
typeof(double),
typeof(ScrollViewer),
new FrameworkPropertyMetadata(0d));
///
/// DependencyProperty for property.
///
public static readonly DependencyProperty HorizontalOffsetProperty =
HorizontalOffsetPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ContentVerticalOffsetProperty =
ContentVerticalOffsetPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ContentHorizontalOffsetProperty =
ContentHorizontalOffsetPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ExtentWidthProperty =
ExtentWidthPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ExtentHeightProperty =
ExtentHeightPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ScrollableWidthProperty =
ScrollableWidthPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ScrollableHeightProperty =
ScrollableHeightPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ViewportWidthProperty =
ViewportWidthPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ViewportHeightProperty =
ViewportHeightPropertyKey.DependencyProperty;
///
/// 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))
{
//if the last command was "set offset", simply replace offset and
//don't insert new command
_array[_lastWritePosition].Param = command.Param;
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
//
//
//
//
// // Cell 1-2, 1-2
//
//
// // Cell 1, 2
// // Cell 2, 1
//
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
///
/// 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 that
/// determines if a horizontal scrollbar is shown.
///
[Bindable(true), Category("Appearance")]
public ScrollBarVisibility HorizontalScrollBarVisibility
{
get { return (ScrollBarVisibility) GetValue(HorizontalScrollBarVisibilityProperty); }
set { SetValue(HorizontalScrollBarVisibilityProperty, value); }
}
///
/// VerticalScrollBarVisibility is a that
/// determines if a vertical scrollbar is shown.
///
[Bindable(true), Category("Appearance")]
public ScrollBarVisibility VerticalScrollBarVisibility
{
get { return (ScrollBarVisibility) GetValue(VerticalScrollBarVisibilityProperty); }
set { SetValue(VerticalScrollBarVisibilityProperty, value); }
}
///
/// 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
/// on the content element.
///
[Category("Layout")]
public double ExtentWidth
{
get { return _xExtent; }
}
///
/// ExtentHeight contains the vertical size of the scrolled content element.
///
///
/// ExtentHeight is only an output property; it can effectively be set by specifying
/// on the content element.
///
[Category("Layout")]
public double ExtentHeight
{
get { return _yExtent; }
}
///
/// 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
/// on this element.
///
[Category("Layout")]
public double ViewportWidth
{
get { return _xSize; }
}
///
/// ViewportHeight contains the vertical size of the scrolling viewport.
///
///
/// ViewportHeight is only an output property; it can effectively be set by specifying
/// on this element.
///
[Category("Layout")]
public double ViewportHeight
{
get { return _ySize; }
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty CanContentScrollProperty =
DependencyProperty.RegisterAttached(
"CanContentScroll",
typeof(bool),
typeof(ScrollViewer),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
///
/// 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 property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty =
DependencyProperty.RegisterAttached(
"HorizontalScrollBarVisibility",
typeof(ScrollBarVisibility),
typeof(ScrollViewer),
new FrameworkPropertyMetadata(
ScrollBarVisibility.Disabled,
FrameworkPropertyMetadataOptions.AffectsMeasure),
new ValidateValueCallback(IsValidScrollBarVisibility));
///
/// 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 property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty VerticalScrollBarVisibilityProperty =
DependencyProperty.RegisterAttached(
"VerticalScrollBarVisibility",
typeof(ScrollBarVisibility),
typeof(ScrollViewer),
new FrameworkPropertyMetadata(
ScrollBarVisibility.Visible,
FrameworkPropertyMetadataOptions.AffectsMeasure),
new ValidateValueCallback(IsValidScrollBarVisibility));
///
/// 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 property.
///
public static readonly DependencyProperty VerticalOffsetProperty =
VerticalOffsetPropertyKey.DependencyProperty;
///
/// HorizontalOffset.
///
private static readonly DependencyPropertyKey HorizontalOffsetPropertyKey =
DependencyProperty.RegisterReadOnly(
"HorizontalOffset",
typeof(double),
typeof(ScrollViewer),
new FrameworkPropertyMetadata(0d));
///
/// DependencyProperty for property.
///
public static readonly DependencyProperty HorizontalOffsetProperty =
HorizontalOffsetPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ContentVerticalOffsetProperty =
ContentVerticalOffsetPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ContentHorizontalOffsetProperty =
ContentHorizontalOffsetPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ExtentWidthProperty =
ExtentWidthPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ExtentHeightProperty =
ExtentHeightPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ScrollableWidthProperty =
ScrollableWidthPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ScrollableHeightProperty =
ScrollableHeightPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ViewportWidthProperty =
ViewportWidthPropertyKey.DependencyProperty;
///
/// 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 property.
///
public static readonly DependencyProperty ViewportHeightProperty =
ViewportHeightPropertyKey.DependencyProperty;
///
/// 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))
{
//if the last command was "set offset", simply replace offset and
//don't insert new command
_array[_lastWritePosition].Param = command.Param;
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
//
//
//
//
// // Cell 1-2, 1-2
//
//
// // Cell 1, 2
// // Cell 2, 1
//
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.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- GenericsInstances.cs
- TemplateField.cs
- ClientSideQueueItem.cs
- FileSystemWatcher.cs
- ProcessHostConfigUtils.cs
- DataListItem.cs
- Util.cs
- HitTestResult.cs
- GrammarBuilderRuleRef.cs
- ZoomPercentageConverter.cs
- Rotation3D.cs
- RoamingStoreFileUtility.cs
- PageTheme.cs
- HealthMonitoringSection.cs
- StorageEntitySetMapping.cs
- DataGridViewComboBoxColumn.cs
- ProtectedConfiguration.cs
- pingexception.cs
- InvalidateEvent.cs
- DataGridViewRowEventArgs.cs
- OverrideMode.cs
- XmlSerializationWriter.cs
- IndexExpression.cs
- ProgressBarBrushConverter.cs
- ValidationHelper.cs
- CompilerGeneratedAttribute.cs
- SelectingProviderEventArgs.cs
- AuthenticateEventArgs.cs
- MetadataCache.cs
- TagPrefixCollection.cs
- BulletedList.cs
- Pkcs7Signer.cs
- CoTaskMemUnicodeSafeHandle.cs
- VideoDrawing.cs
- AspNetSynchronizationContext.cs
- QilChoice.cs
- FixedSOMTableCell.cs
- SmtpAuthenticationManager.cs
- xamlnodes.cs
- ViewManager.cs
- DbgCompiler.cs
- PipeStream.cs
- HasCopySemanticsAttribute.cs
- SevenBitStream.cs
- RayMeshGeometry3DHitTestResult.cs
- XmlSchemaRedefine.cs
- WindowsUserNameSecurityTokenAuthenticator.cs
- Rotation3D.cs
- EntityAdapter.cs
- XamlContextStack.cs
- KeyBinding.cs
- TemplateControlBuildProvider.cs
- DataGridClipboardHelper.cs
- ToolBar.cs
- MultiDataTrigger.cs
- Transactions.cs
- CaretElement.cs
- Choices.cs
- ToolStripLocationCancelEventArgs.cs
- KerberosRequestorSecurityToken.cs
- _emptywebproxy.cs
- TextEffect.cs
- SafePEFileHandle.cs
- DataSetUtil.cs
- SourceFileInfo.cs
- TransformGroup.cs
- TemplateNameScope.cs
- ToolStripItemGlyph.cs
- SmiConnection.cs
- PrtCap_Public_Simple.cs
- BasicBrowserDialog.designer.cs
- StylusPlugin.cs
- Brush.cs
- CrossAppDomainChannel.cs
- ReferencedType.cs
- UiaCoreApi.cs
- MetadataCache.cs
- UserMapPath.cs
- CallbackDebugElement.cs
- SqlMethodCallConverter.cs
- StylusTouchDevice.cs
- ExpressionEditorSheet.cs
- RelatedEnd.cs
- StylusPointPropertyUnit.cs
- SqlFunctionAttribute.cs
- TailPinnedEventArgs.cs
- EnterpriseServicesHelper.cs
- SqlUnionizer.cs
- StringFunctions.cs
- IconConverter.cs
- XmlDigitalSignatureProcessor.cs
- _NTAuthentication.cs
- DataBoundControlHelper.cs
- ToolStripItemEventArgs.cs
- VirtualizingPanel.cs
- WebPartCatalogAddVerb.cs
- Directory.cs
- CheckBox.cs
- HandleCollector.cs
- AddInStore.cs