Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / Controls / Primitives / Track.cs / 1 / Track.cs
//----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// File: Track.cs
//
// Description: Contains the Track class.
//
//---------------------------------------------------------------------------
using MS.Internal;
using MS.Internal.KnownBoxes;
using MS.Internal.PresentationFramework;
using MS.Utility;
using System.Collections;
using System.ComponentModel;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Automation.Provider;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Markup;
using System.Windows.Input;
using System;
using System.Diagnostics;
namespace System.Windows.Controls.Primitives
{
///
/// Track handles layout of the parts of a ScrollBar and Slider.
///
[Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)] // cannot be read & localized as string
public class Track : FrameworkElement
{
//-------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------
#region Constructors
static Track()
{
}
///
/// Default DependencyObject constructor
///
public Track() : base()
{
}
#endregion
//--------------------------------------------------------------------
//
// Public Methods
//
//-------------------------------------------------------------------
#region Public Methods
///
/// Calculate the value from given Point. The input point is relative to TopLeft conner of Track.
///
/// Point (in Track's co-ordinate).
public virtual double ValueFromPoint(Point pt)
{
double val;
// Find distance from center of thumb to given point.
if (Orientation == Orientation.Horizontal)
{
val = Value + ValueFromDistance(pt.X - ThumbCenterOffset, pt.Y - (RenderSize.Height * 0.5));
}
else
{
val = Value + ValueFromDistance(pt.X - (RenderSize.Width * 0.5), pt.Y - ThumbCenterOffset);
}
return Math.Max(Minimum, Math.Min(Maximum, val));
}
///
/// This function returns the delta in value that would be caused by moving the thumb the given pixel distances.
/// The returned delta value is not guaranteed to be inside the valid Value range.
///
/// Total horizontal distance that the Thumb has moved.
/// Total vertical distance that the Thumb has moved.
public virtual double ValueFromDistance(double horizontal, double vertical)
{
double scale = IsDirectionReversed ? -1 : 1;
//
// Note: To implement 'Snap-Back' feature, we could check whether the point is far away from center of the track.
// If so, just return current value (this should move the Thumb back to its original localtion).
//
if (Orientation == Orientation.Horizontal)
{
return scale * horizontal * Density;
}
else
{
// Increases in y cause decreases in Sliders value
return -1 * scale * vertical * Density;
}
}
#endregion
//--------------------------------------------------------------------
//
// Public Properties
//
//--------------------------------------------------------------------
#region Public Properties
private void UpdateComponent(Control oldValue, Control newValue)
{
if (oldValue != newValue)
{
if (_visualChildren == null)
{
_visualChildren = new Visual[3];
}
if (oldValue != null)
{
// notify the visual layer that the old component has been removed.
RemoveVisualChild(oldValue);
}
// Remove the old value from our z index list and add new value to end
int i = 0;
while (i < 3)
{
// Array isn't full, break
if (_visualChildren[i] == null)
break;
// found the old value
if (_visualChildren[i] == oldValue)
{
// Move values down until end of array or a null element
while (i < 2 && _visualChildren[i + 1] != null)
{
_visualChildren[i] = _visualChildren[i + 1];
i++;
}
}
else
{
i++;
}
}
// Add newValue at end of z-order
_visualChildren[i] = newValue;
AddVisualChild(newValue);
InvalidateMeasure();
InvalidateArrange();
}
}
///
/// The RepeatButton used to decrease the Value
///
public RepeatButton DecreaseRepeatButton
{
get
{
return _decreaseButton;
}
set
{
if (_increaseButton == value)
{
throw new NotSupportedException(SR.Get(SRID.Track_SameButtons));
}
UpdateComponent(_decreaseButton, value);
_decreaseButton = value;
if (_decreaseButton != null)
{
CommandManager.InvalidateRequerySuggested(); // Should post an idle queue item to update IsEnabled on button
}
}
}
///
/// The Thumb in the Track
///
public Thumb Thumb
{
get
{
return _thumb;
}
set
{
UpdateComponent(_thumb, value);
_thumb = value;
}
}
///
/// The RepeatButton used to increase the Value
///
public RepeatButton IncreaseRepeatButton
{
get
{
return _increaseButton;
}
set
{
if (_decreaseButton == value)
{
throw new NotSupportedException(SR.Get(SRID.Track_SameButtons));
}
UpdateComponent(_increaseButton, value);
_increaseButton = value;
if (_increaseButton != null)
{
CommandManager.InvalidateRequerySuggested(); // Should post an idle queue item to update IsEnabled on button
}
}
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(Track),
new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure),
new ValidateValueCallback(ScrollBar.IsValidOrientation));
///
/// This property represents the Track layout orientation: Vertical or Horizontal.
/// On vertical ScrollBars, the thumb moves up and down. On horizontal bars, the thumb moves left to right.
///
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty MinimumProperty =
RangeBase.MinimumProperty.AddOwner(typeof(Track),
new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsArrange));
///
/// The Minimum value of the Slider or ScrollBar
///
public double Minimum
{
get { return (double)GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); }
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty MaximumProperty =
RangeBase.MaximumProperty.AddOwner(typeof(Track),
new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsArrange));
///
/// The Maximum value of the Slider or ScrollBar
///
public double Maximum
{
get { return (double)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty ValueProperty =
RangeBase.ValueProperty.AddOwner(typeof(Track),
new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.AffectsArrange));
///
/// The current value of the Slider or ScrollBar
///
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty ViewportSizeProperty =
DependencyProperty.Register("ViewportSize",
typeof(double),
typeof(Track),
new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsArrange),
new ValidateValueCallback(IsValidViewport));
///
/// ViewportSize is the amount of the scrolled extent currently visible. For most scrolled content, this value
/// will be bound to one of 's ViewportSize properties.
/// This property is in logical scrolling units.
///
/// Setting this value to NaN will turn off automatic sizing of the thumb
///
public double ViewportSize
{
get { return (double)GetValue(ViewportSizeProperty); }
set { SetValue(ViewportSizeProperty, value); }
}
private static bool IsValidViewport(object o)
{
double d = (double)o;
return d >= 0.0 || double.IsNaN(d);
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty IsDirectionReversedProperty =
DependencyProperty.Register("IsDirectionReversed",
typeof(bool),
typeof(Track),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
///
/// Indicates if the location of the DecreaseRepeatButton and IncreaseRepeatButton
/// should be swapped.
///
public bool IsDirectionReversed
{
get { return (bool)GetValue(IsDirectionReversedProperty); }
set { SetValue(IsDirectionReversedProperty, value); }
}
#endregion
//-------------------------------------------------------------------
//
// Protected Methods
//
//--------------------------------------------------------------------
#region Protected Methods
///
/// Derived class must implement to support Visual children. The method must return
/// the child at the specified index. Index must be between 0 and GetVisualChildrenCount-1.
///
/// By default a Visual does not have any children.
///
/// Remark:
/// During this virtual call it is not valid to modify the Visual tree.
///
protected override Visual GetVisualChild(int index)
{
if (_visualChildren == null || _visualChildren[index] == null)
{
throw new ArgumentOutOfRangeException("index", index, SR.Get(SRID.Visual_ArgumentOutOfRange));
}
return _visualChildren[index];
}
///
/// Derived classes override this property to enable the Visual code to enumerate
/// the Visual children. Derived classes need to return the number of children
/// from this method.
///
/// By default a Visual does not have any children.
///
/// Remark: During this virtual method the Visual tree must not be modified.
///
protected override int VisualChildrenCount
{
get
{
if (_visualChildren == null || _visualChildren[0] == null)
{
Debug.Assert(_visualChildren == null || _visualChildren[1] == null, "Child[1] should be null if Child[0] == null)");
Debug.Assert(_visualChildren == null || _visualChildren[2] == null, "Child[2] should be null if Child[0] == null)");
return 0;
}
else if (_visualChildren[1] == null)
{
Debug.Assert(_visualChildren[2] == null, "Child[2] should be null if Child[1] == null)");
return 1;
}
else
{
return _visualChildren[2] == null ? 2 : 3;
}
}
}
///
/// The desired size of a Track is the width (if vertically oriented) or height (if horizontally
/// oriented) of the Thumb.
///
/// When ViewportSize is NaN:
/// The thumb is measured to find the other dimension.
/// Otherwise:
/// Zero size is returned; Track can scale to any size along its children.
/// This means that it will occupy no space (and not display) unless made larger by a parent or specified size.
///
///
protected override Size MeasureOverride(Size availableSize)
{
Size desiredSize = new Size(0.0, 0.0);
// Only measure thumb
// Repeat buttons will be sized based on thumb
if (Thumb != null)
{
Thumb.Measure(availableSize);
desiredSize = Thumb.DesiredSize;
}
if (!double.IsNaN(ViewportSize))
{
// ScrollBar can shrink to 0 in the direction of scrolling
if (Orientation == Orientation.Vertical)
desiredSize.Height = 0.0;
else
desiredSize.Width = 0.0;
}
return desiredSize;
}
// Force length of one of track's pieces to be > 0 and less than tracklength
private static void CoerceLength(ref double componentLength, double trackLength)
{
if (componentLength < 0)
{
componentLength = 0.0;
}
else if (componentLength > trackLength || double.IsNaN(componentLength))
{
componentLength = trackLength;
}
}
///
/// Children will be stretched to fit horizontally (if vertically oriented) or vertically (if horizontally
/// oriented).
///
/// There are essentially three possible layout states:
/// 1. The track is enabled and the thumb is proportionally sizing.
/// 2. The track is enabled and the thumb has reached its minimum size.
/// 3. The track is disabled or there is not enough room for the thumb.
/// Track elements are not displayed, and will not be arranged.
///
///
protected override Size ArrangeOverride(Size arrangeSize)
{
double decreaseButtonLength, thumbLength, increaseButtonLength;
bool isVertical = (Orientation == Orientation.Vertical);
double viewportSize = Math.Max(0.0, ViewportSize);
// If viewport is NaN, compute thumb's size based on its desired size,
// otherwise compute the thumb base on the viewport and extent properties
if (double.IsNaN(viewportSize))
{
ComputeSliderLengths(arrangeSize, isVertical, out decreaseButtonLength, out thumbLength, out increaseButtonLength);
}
else
{
// Don't arrange if there's not enough content or the track is too small
if (!ComputeScrollBarLengths(arrangeSize, viewportSize, isVertical, out decreaseButtonLength, out thumbLength, out increaseButtonLength))
{
return arrangeSize;
}
}
// Layout the pieces of track
Point offset = new Point();
Size pieceSize = arrangeSize;
bool isDirectionReversed = IsDirectionReversed;
if (isVertical)
{
// Vertical Normal : |Inc Button |
// |Thumb |
// |Dec Button |
// Vertical Reversed : |Dec Button |
// |Thumb |
// |Inc Button |
CoerceLength(ref decreaseButtonLength, arrangeSize.Height);
CoerceLength(ref increaseButtonLength, arrangeSize.Height);
CoerceLength(ref thumbLength, arrangeSize.Height);
offset.Y = isDirectionReversed ? decreaseButtonLength + thumbLength : 0.0;
pieceSize.Height = increaseButtonLength;
if (IncreaseRepeatButton != null)
IncreaseRepeatButton.Arrange(new Rect(offset, pieceSize));
offset.Y = isDirectionReversed ? 0.0 : increaseButtonLength + thumbLength;
pieceSize.Height = decreaseButtonLength;
if (DecreaseRepeatButton != null)
DecreaseRepeatButton.Arrange(new Rect(offset, pieceSize));
offset.Y = isDirectionReversed ? decreaseButtonLength : increaseButtonLength;
pieceSize.Height = thumbLength;
if (Thumb != null)
Thumb.Arrange(new Rect(offset, pieceSize));
ThumbCenterOffset = offset.Y + (thumbLength * 0.5);
}
else
{
// Horizontal Normal : |Dec Button |Thumb| Inc Button|
// Horizontal Reversed : |Inc Button |Thumb| Dec Button|
CoerceLength(ref decreaseButtonLength, arrangeSize.Width);
CoerceLength(ref increaseButtonLength, arrangeSize.Width);
CoerceLength(ref thumbLength, arrangeSize.Width);
offset.X = isDirectionReversed ? increaseButtonLength + thumbLength : 0.0;
pieceSize.Width = decreaseButtonLength;
if (DecreaseRepeatButton != null)
DecreaseRepeatButton.Arrange(new Rect(offset, pieceSize));
offset.X = isDirectionReversed ? 0.0 : decreaseButtonLength + thumbLength;
pieceSize.Width = increaseButtonLength;
if (IncreaseRepeatButton != null)
IncreaseRepeatButton.Arrange(new Rect(offset, pieceSize));
offset.X = isDirectionReversed ? increaseButtonLength : decreaseButtonLength;
pieceSize.Width = thumbLength;
if (Thumb != null)
Thumb.Arrange(new Rect(offset, pieceSize));
ThumbCenterOffset = offset.X + (thumbLength * 0.5);
}
return arrangeSize;
}
// Computes the length of the decrease button, thumb and increase button
// Thumb's size is based on it's desired size
private void ComputeSliderLengths(Size arrangeSize, bool isVertical, out double decreaseButtonLength, out double thumbLength, out double increaseButtonLength)
{
double min = Minimum;
double range = Math.Max(0.0, Maximum - min);
double offset = Math.Min(range, Value - min);
double trackLength;
// Compute thumb size
if (isVertical)
{
trackLength = arrangeSize.Height;
thumbLength = Thumb == null ? 0 : Thumb.DesiredSize.Height;
}
else
{
trackLength = arrangeSize.Width;
thumbLength = Thumb == null ? 0 : Thumb.DesiredSize.Width;
}
CoerceLength(ref thumbLength, trackLength);
double remainingTrackLength = trackLength - thumbLength;
decreaseButtonLength = remainingTrackLength * offset / range;
CoerceLength(ref decreaseButtonLength, remainingTrackLength);
increaseButtonLength = remainingTrackLength - decreaseButtonLength;
CoerceLength(ref increaseButtonLength, remainingTrackLength);
Debug.Assert(decreaseButtonLength >= 0.0 && decreaseButtonLength <= remainingTrackLength, "decreaseButtonLength is outside bounds");
Debug.Assert(increaseButtonLength >= 0.0 && increaseButtonLength <= remainingTrackLength, "increaseButtonLength is outside bounds");
Density = range / remainingTrackLength;
}
// Computes the length of the decrease button, thumb and increase button
// Thumb's size is based on viewport and extent
// returns false if the track should be hidden
private bool ComputeScrollBarLengths(Size arrangeSize, double viewportSize, bool isVertical, out double decreaseButtonLength, out double thumbLength, out double increaseButtonLength)
{
double min = Minimum;
double range = Math.Max(0.0, Maximum - min);
double offset = Math.Min(range, Value - min);
Debug.Assert(DoubleUtil.GreaterThanOrClose(offset, 0.0), "Invalid offest (negative value).");
double extent = Math.Max(0.0, range) + viewportSize;
double trackLength;
// Compute thumb size
double thumbMinLength;
if (isVertical)
{
trackLength = arrangeSize.Height;
// Try to use the apps resource if it exists, fall back to SystemParameters if it doesn't
object buttonHeightResource = TryFindResource(SystemParameters.VerticalScrollBarButtonHeightKey);
double buttonHeight = buttonHeightResource is double ? (double)buttonHeightResource : SystemParameters.VerticalScrollBarButtonHeight;
thumbMinLength = Math.Floor(buttonHeight * 0.5);
}
else
{
trackLength = arrangeSize.Width;
// Try to use the apps resource if it exists, fall back to SystemParameters if it doesn't
object buttonWidthResource = TryFindResource(SystemParameters.HorizontalScrollBarButtonWidthKey);
double buttonWidth = buttonWidthResource is double ? (double)buttonWidthResource : SystemParameters.HorizontalScrollBarButtonWidth;
thumbMinLength = Math.Floor(buttonWidth * 0.5);
}
thumbLength = trackLength * viewportSize / extent;
CoerceLength(ref thumbLength, trackLength);
thumbLength = Math.Max(thumbMinLength, thumbLength);
// If we don't have enough content to scroll, disable the track.
bool notEnoughContentToScroll = DoubleUtil.LessThanOrClose(range, 0.0);
bool thumbLongerThanTrack = thumbLength > trackLength;
// if there's not enough content or the thumb is longer than the track,
// hide the track and don't arrange the pieces
if (notEnoughContentToScroll || thumbLongerThanTrack)
{
if (Visibility != Visibility.Hidden)
{
Visibility = Visibility.Hidden;
}
ThumbCenterOffset = Double.NaN;
Density = Double.NaN;
decreaseButtonLength = 0.0;
increaseButtonLength = 0.0;
return false; // don't arrange
}
else if (Visibility != Visibility.Visible)
{
Visibility = Visibility.Visible;
}
// Compute lengths of increase and decrease button
double remainingTrackLength = trackLength - thumbLength;
decreaseButtonLength = remainingTrackLength * offset / range;
CoerceLength(ref decreaseButtonLength, remainingTrackLength);
increaseButtonLength = remainingTrackLength - decreaseButtonLength;
CoerceLength(ref increaseButtonLength, remainingTrackLength);
Density = range / remainingTrackLength;
return true;
}
// Bind track to templated parent
private void BindToTemplatedParent(DependencyProperty target, DependencyProperty source)
{
if (!HasNonDefaultValue(target))
{
Binding binding = new Binding();
binding.RelativeSource = RelativeSource.TemplatedParent;
binding.Path = new PropertyPath(source);
SetBinding(target, binding);
}
}
// Bind thumb or repeat button to templated parent
private void BindChildToTemplatedParent(FrameworkElement element, DependencyProperty target, DependencyProperty source)
{
if (element != null && !element.HasNonDefaultValue(target))
{
Binding binding = new Binding();
binding.Source = this.TemplatedParent;
binding.Path = new PropertyPath(source);
element.SetBinding(target, binding);
}
}
///
/// Track automatically sets bindings to its templated parent
/// to aid styling
///
internal override void OnPreApplyTemplate()
{
base.OnPreApplyTemplate();
RangeBase rangeBase = TemplatedParent as RangeBase;
if (rangeBase != null)
{
BindToTemplatedParent(MinimumProperty, RangeBase.MinimumProperty);
BindToTemplatedParent(MaximumProperty, RangeBase.MaximumProperty);
BindToTemplatedParent(ValueProperty, RangeBase.ValueProperty);
// Setup ScrollBar specific bindings
ScrollBar scrollBar = rangeBase as ScrollBar;
if (scrollBar != null)
{
BindToTemplatedParent(ViewportSizeProperty, ScrollBar.ViewportSizeProperty);
BindToTemplatedParent(OrientationProperty, ScrollBar.OrientationProperty);
}
else
{
// Setup Slider specific bindings
Slider slider = rangeBase as Slider;
if (slider != null)
{
BindToTemplatedParent(OrientationProperty, Slider.OrientationProperty);
BindToTemplatedParent(IsDirectionReversedProperty, Slider.IsDirectionReversedProperty);
BindChildToTemplatedParent(DecreaseRepeatButton, RepeatButton.DelayProperty, Slider.DelayProperty);
BindChildToTemplatedParent(DecreaseRepeatButton, RepeatButton.IntervalProperty, Slider.IntervalProperty);
BindChildToTemplatedParent(IncreaseRepeatButton, RepeatButton.DelayProperty, Slider.DelayProperty);
BindChildToTemplatedParent(IncreaseRepeatButton, RepeatButton.IntervalProperty, Slider.IntervalProperty);
}
}
}
}
#endregion
//-------------------------------------------------------------------
//
// Private Methods
//
//-------------------------------------------------------------------
#region Private Methods
#endregion
//-------------------------------------------------------------------
//
// Private Properties
//
//--------------------------------------------------------------------
#region Private Properties
private double ThumbCenterOffset
{
get { return _thumbCenterOffset; }
set { _thumbCenterOffset = value; }
}
private double Density
{
get { return _density; }
set { _density = value; }
}
//
// 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
//-------------------------------------------------------------------
//
// Private Fields
//
//--------------------------------------------------------------------
#region Private Fields
private RepeatButton _increaseButton;
private RepeatButton _decreaseButton;
private Thumb _thumb;
private Visual[] _visualChildren;
// Density of scrolling units present in 1/96" of track (not thumb). Computed during ArrangeOverride.
// Note that density default really *is* NaN. This corresponds to no track having been computed/displayed.
private double _density = Double.NaN;
private double _thumbCenterOffset = Double.NaN;
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// File: Track.cs
//
// Description: Contains the Track class.
//
//---------------------------------------------------------------------------
using MS.Internal;
using MS.Internal.KnownBoxes;
using MS.Internal.PresentationFramework;
using MS.Utility;
using System.Collections;
using System.ComponentModel;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Automation.Provider;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Markup;
using System.Windows.Input;
using System;
using System.Diagnostics;
namespace System.Windows.Controls.Primitives
{
///
/// Track handles layout of the parts of a ScrollBar and Slider.
///
[Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)] // cannot be read & localized as string
public class Track : FrameworkElement
{
//-------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------
#region Constructors
static Track()
{
}
///
/// Default DependencyObject constructor
///
public Track() : base()
{
}
#endregion
//--------------------------------------------------------------------
//
// Public Methods
//
//-------------------------------------------------------------------
#region Public Methods
///
/// Calculate the value from given Point. The input point is relative to TopLeft conner of Track.
///
/// Point (in Track's co-ordinate).
public virtual double ValueFromPoint(Point pt)
{
double val;
// Find distance from center of thumb to given point.
if (Orientation == Orientation.Horizontal)
{
val = Value + ValueFromDistance(pt.X - ThumbCenterOffset, pt.Y - (RenderSize.Height * 0.5));
}
else
{
val = Value + ValueFromDistance(pt.X - (RenderSize.Width * 0.5), pt.Y - ThumbCenterOffset);
}
return Math.Max(Minimum, Math.Min(Maximum, val));
}
///
/// This function returns the delta in value that would be caused by moving the thumb the given pixel distances.
/// The returned delta value is not guaranteed to be inside the valid Value range.
///
/// Total horizontal distance that the Thumb has moved.
/// Total vertical distance that the Thumb has moved.
public virtual double ValueFromDistance(double horizontal, double vertical)
{
double scale = IsDirectionReversed ? -1 : 1;
//
// Note: To implement 'Snap-Back' feature, we could check whether the point is far away from center of the track.
// If so, just return current value (this should move the Thumb back to its original localtion).
//
if (Orientation == Orientation.Horizontal)
{
return scale * horizontal * Density;
}
else
{
// Increases in y cause decreases in Sliders value
return -1 * scale * vertical * Density;
}
}
#endregion
//--------------------------------------------------------------------
//
// Public Properties
//
//--------------------------------------------------------------------
#region Public Properties
private void UpdateComponent(Control oldValue, Control newValue)
{
if (oldValue != newValue)
{
if (_visualChildren == null)
{
_visualChildren = new Visual[3];
}
if (oldValue != null)
{
// notify the visual layer that the old component has been removed.
RemoveVisualChild(oldValue);
}
// Remove the old value from our z index list and add new value to end
int i = 0;
while (i < 3)
{
// Array isn't full, break
if (_visualChildren[i] == null)
break;
// found the old value
if (_visualChildren[i] == oldValue)
{
// Move values down until end of array or a null element
while (i < 2 && _visualChildren[i + 1] != null)
{
_visualChildren[i] = _visualChildren[i + 1];
i++;
}
}
else
{
i++;
}
}
// Add newValue at end of z-order
_visualChildren[i] = newValue;
AddVisualChild(newValue);
InvalidateMeasure();
InvalidateArrange();
}
}
///
/// The RepeatButton used to decrease the Value
///
public RepeatButton DecreaseRepeatButton
{
get
{
return _decreaseButton;
}
set
{
if (_increaseButton == value)
{
throw new NotSupportedException(SR.Get(SRID.Track_SameButtons));
}
UpdateComponent(_decreaseButton, value);
_decreaseButton = value;
if (_decreaseButton != null)
{
CommandManager.InvalidateRequerySuggested(); // Should post an idle queue item to update IsEnabled on button
}
}
}
///
/// The Thumb in the Track
///
public Thumb Thumb
{
get
{
return _thumb;
}
set
{
UpdateComponent(_thumb, value);
_thumb = value;
}
}
///
/// The RepeatButton used to increase the Value
///
public RepeatButton IncreaseRepeatButton
{
get
{
return _increaseButton;
}
set
{
if (_decreaseButton == value)
{
throw new NotSupportedException(SR.Get(SRID.Track_SameButtons));
}
UpdateComponent(_increaseButton, value);
_increaseButton = value;
if (_increaseButton != null)
{
CommandManager.InvalidateRequerySuggested(); // Should post an idle queue item to update IsEnabled on button
}
}
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(Track),
new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure),
new ValidateValueCallback(ScrollBar.IsValidOrientation));
///
/// This property represents the Track layout orientation: Vertical or Horizontal.
/// On vertical ScrollBars, the thumb moves up and down. On horizontal bars, the thumb moves left to right.
///
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty MinimumProperty =
RangeBase.MinimumProperty.AddOwner(typeof(Track),
new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsArrange));
///
/// The Minimum value of the Slider or ScrollBar
///
public double Minimum
{
get { return (double)GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); }
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty MaximumProperty =
RangeBase.MaximumProperty.AddOwner(typeof(Track),
new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsArrange));
///
/// The Maximum value of the Slider or ScrollBar
///
public double Maximum
{
get { return (double)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty ValueProperty =
RangeBase.ValueProperty.AddOwner(typeof(Track),
new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.AffectsArrange));
///
/// The current value of the Slider or ScrollBar
///
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty ViewportSizeProperty =
DependencyProperty.Register("ViewportSize",
typeof(double),
typeof(Track),
new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsArrange),
new ValidateValueCallback(IsValidViewport));
///
/// ViewportSize is the amount of the scrolled extent currently visible. For most scrolled content, this value
/// will be bound to one of 's ViewportSize properties.
/// This property is in logical scrolling units.
///
/// Setting this value to NaN will turn off automatic sizing of the thumb
///
public double ViewportSize
{
get { return (double)GetValue(ViewportSizeProperty); }
set { SetValue(ViewportSizeProperty, value); }
}
private static bool IsValidViewport(object o)
{
double d = (double)o;
return d >= 0.0 || double.IsNaN(d);
}
///
/// DependencyProperty for property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty IsDirectionReversedProperty =
DependencyProperty.Register("IsDirectionReversed",
typeof(bool),
typeof(Track),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
///
/// Indicates if the location of the DecreaseRepeatButton and IncreaseRepeatButton
/// should be swapped.
///
public bool IsDirectionReversed
{
get { return (bool)GetValue(IsDirectionReversedProperty); }
set { SetValue(IsDirectionReversedProperty, value); }
}
#endregion
//-------------------------------------------------------------------
//
// Protected Methods
//
//--------------------------------------------------------------------
#region Protected Methods
///
/// Derived class must implement to support Visual children. The method must return
/// the child at the specified index. Index must be between 0 and GetVisualChildrenCount-1.
///
/// By default a Visual does not have any children.
///
/// Remark:
/// During this virtual call it is not valid to modify the Visual tree.
///
protected override Visual GetVisualChild(int index)
{
if (_visualChildren == null || _visualChildren[index] == null)
{
throw new ArgumentOutOfRangeException("index", index, SR.Get(SRID.Visual_ArgumentOutOfRange));
}
return _visualChildren[index];
}
///
/// Derived classes override this property to enable the Visual code to enumerate
/// the Visual children. Derived classes need to return the number of children
/// from this method.
///
/// By default a Visual does not have any children.
///
/// Remark: During this virtual method the Visual tree must not be modified.
///
protected override int VisualChildrenCount
{
get
{
if (_visualChildren == null || _visualChildren[0] == null)
{
Debug.Assert(_visualChildren == null || _visualChildren[1] == null, "Child[1] should be null if Child[0] == null)");
Debug.Assert(_visualChildren == null || _visualChildren[2] == null, "Child[2] should be null if Child[0] == null)");
return 0;
}
else if (_visualChildren[1] == null)
{
Debug.Assert(_visualChildren[2] == null, "Child[2] should be null if Child[1] == null)");
return 1;
}
else
{
return _visualChildren[2] == null ? 2 : 3;
}
}
}
///
/// The desired size of a Track is the width (if vertically oriented) or height (if horizontally
/// oriented) of the Thumb.
///
/// When ViewportSize is NaN:
/// The thumb is measured to find the other dimension.
/// Otherwise:
/// Zero size is returned; Track can scale to any size along its children.
/// This means that it will occupy no space (and not display) unless made larger by a parent or specified size.
///
///
protected override Size MeasureOverride(Size availableSize)
{
Size desiredSize = new Size(0.0, 0.0);
// Only measure thumb
// Repeat buttons will be sized based on thumb
if (Thumb != null)
{
Thumb.Measure(availableSize);
desiredSize = Thumb.DesiredSize;
}
if (!double.IsNaN(ViewportSize))
{
// ScrollBar can shrink to 0 in the direction of scrolling
if (Orientation == Orientation.Vertical)
desiredSize.Height = 0.0;
else
desiredSize.Width = 0.0;
}
return desiredSize;
}
// Force length of one of track's pieces to be > 0 and less than tracklength
private static void CoerceLength(ref double componentLength, double trackLength)
{
if (componentLength < 0)
{
componentLength = 0.0;
}
else if (componentLength > trackLength || double.IsNaN(componentLength))
{
componentLength = trackLength;
}
}
///
/// Children will be stretched to fit horizontally (if vertically oriented) or vertically (if horizontally
/// oriented).
///
/// There are essentially three possible layout states:
/// 1. The track is enabled and the thumb is proportionally sizing.
/// 2. The track is enabled and the thumb has reached its minimum size.
/// 3. The track is disabled or there is not enough room for the thumb.
/// Track elements are not displayed, and will not be arranged.
///
///
protected override Size ArrangeOverride(Size arrangeSize)
{
double decreaseButtonLength, thumbLength, increaseButtonLength;
bool isVertical = (Orientation == Orientation.Vertical);
double viewportSize = Math.Max(0.0, ViewportSize);
// If viewport is NaN, compute thumb's size based on its desired size,
// otherwise compute the thumb base on the viewport and extent properties
if (double.IsNaN(viewportSize))
{
ComputeSliderLengths(arrangeSize, isVertical, out decreaseButtonLength, out thumbLength, out increaseButtonLength);
}
else
{
// Don't arrange if there's not enough content or the track is too small
if (!ComputeScrollBarLengths(arrangeSize, viewportSize, isVertical, out decreaseButtonLength, out thumbLength, out increaseButtonLength))
{
return arrangeSize;
}
}
// Layout the pieces of track
Point offset = new Point();
Size pieceSize = arrangeSize;
bool isDirectionReversed = IsDirectionReversed;
if (isVertical)
{
// Vertical Normal : |Inc Button |
// |Thumb |
// |Dec Button |
// Vertical Reversed : |Dec Button |
// |Thumb |
// |Inc Button |
CoerceLength(ref decreaseButtonLength, arrangeSize.Height);
CoerceLength(ref increaseButtonLength, arrangeSize.Height);
CoerceLength(ref thumbLength, arrangeSize.Height);
offset.Y = isDirectionReversed ? decreaseButtonLength + thumbLength : 0.0;
pieceSize.Height = increaseButtonLength;
if (IncreaseRepeatButton != null)
IncreaseRepeatButton.Arrange(new Rect(offset, pieceSize));
offset.Y = isDirectionReversed ? 0.0 : increaseButtonLength + thumbLength;
pieceSize.Height = decreaseButtonLength;
if (DecreaseRepeatButton != null)
DecreaseRepeatButton.Arrange(new Rect(offset, pieceSize));
offset.Y = isDirectionReversed ? decreaseButtonLength : increaseButtonLength;
pieceSize.Height = thumbLength;
if (Thumb != null)
Thumb.Arrange(new Rect(offset, pieceSize));
ThumbCenterOffset = offset.Y + (thumbLength * 0.5);
}
else
{
// Horizontal Normal : |Dec Button |Thumb| Inc Button|
// Horizontal Reversed : |Inc Button |Thumb| Dec Button|
CoerceLength(ref decreaseButtonLength, arrangeSize.Width);
CoerceLength(ref increaseButtonLength, arrangeSize.Width);
CoerceLength(ref thumbLength, arrangeSize.Width);
offset.X = isDirectionReversed ? increaseButtonLength + thumbLength : 0.0;
pieceSize.Width = decreaseButtonLength;
if (DecreaseRepeatButton != null)
DecreaseRepeatButton.Arrange(new Rect(offset, pieceSize));
offset.X = isDirectionReversed ? 0.0 : decreaseButtonLength + thumbLength;
pieceSize.Width = increaseButtonLength;
if (IncreaseRepeatButton != null)
IncreaseRepeatButton.Arrange(new Rect(offset, pieceSize));
offset.X = isDirectionReversed ? increaseButtonLength : decreaseButtonLength;
pieceSize.Width = thumbLength;
if (Thumb != null)
Thumb.Arrange(new Rect(offset, pieceSize));
ThumbCenterOffset = offset.X + (thumbLength * 0.5);
}
return arrangeSize;
}
// Computes the length of the decrease button, thumb and increase button
// Thumb's size is based on it's desired size
private void ComputeSliderLengths(Size arrangeSize, bool isVertical, out double decreaseButtonLength, out double thumbLength, out double increaseButtonLength)
{
double min = Minimum;
double range = Math.Max(0.0, Maximum - min);
double offset = Math.Min(range, Value - min);
double trackLength;
// Compute thumb size
if (isVertical)
{
trackLength = arrangeSize.Height;
thumbLength = Thumb == null ? 0 : Thumb.DesiredSize.Height;
}
else
{
trackLength = arrangeSize.Width;
thumbLength = Thumb == null ? 0 : Thumb.DesiredSize.Width;
}
CoerceLength(ref thumbLength, trackLength);
double remainingTrackLength = trackLength - thumbLength;
decreaseButtonLength = remainingTrackLength * offset / range;
CoerceLength(ref decreaseButtonLength, remainingTrackLength);
increaseButtonLength = remainingTrackLength - decreaseButtonLength;
CoerceLength(ref increaseButtonLength, remainingTrackLength);
Debug.Assert(decreaseButtonLength >= 0.0 && decreaseButtonLength <= remainingTrackLength, "decreaseButtonLength is outside bounds");
Debug.Assert(increaseButtonLength >= 0.0 && increaseButtonLength <= remainingTrackLength, "increaseButtonLength is outside bounds");
Density = range / remainingTrackLength;
}
// Computes the length of the decrease button, thumb and increase button
// Thumb's size is based on viewport and extent
// returns false if the track should be hidden
private bool ComputeScrollBarLengths(Size arrangeSize, double viewportSize, bool isVertical, out double decreaseButtonLength, out double thumbLength, out double increaseButtonLength)
{
double min = Minimum;
double range = Math.Max(0.0, Maximum - min);
double offset = Math.Min(range, Value - min);
Debug.Assert(DoubleUtil.GreaterThanOrClose(offset, 0.0), "Invalid offest (negative value).");
double extent = Math.Max(0.0, range) + viewportSize;
double trackLength;
// Compute thumb size
double thumbMinLength;
if (isVertical)
{
trackLength = arrangeSize.Height;
// Try to use the apps resource if it exists, fall back to SystemParameters if it doesn't
object buttonHeightResource = TryFindResource(SystemParameters.VerticalScrollBarButtonHeightKey);
double buttonHeight = buttonHeightResource is double ? (double)buttonHeightResource : SystemParameters.VerticalScrollBarButtonHeight;
thumbMinLength = Math.Floor(buttonHeight * 0.5);
}
else
{
trackLength = arrangeSize.Width;
// Try to use the apps resource if it exists, fall back to SystemParameters if it doesn't
object buttonWidthResource = TryFindResource(SystemParameters.HorizontalScrollBarButtonWidthKey);
double buttonWidth = buttonWidthResource is double ? (double)buttonWidthResource : SystemParameters.HorizontalScrollBarButtonWidth;
thumbMinLength = Math.Floor(buttonWidth * 0.5);
}
thumbLength = trackLength * viewportSize / extent;
CoerceLength(ref thumbLength, trackLength);
thumbLength = Math.Max(thumbMinLength, thumbLength);
// If we don't have enough content to scroll, disable the track.
bool notEnoughContentToScroll = DoubleUtil.LessThanOrClose(range, 0.0);
bool thumbLongerThanTrack = thumbLength > trackLength;
// if there's not enough content or the thumb is longer than the track,
// hide the track and don't arrange the pieces
if (notEnoughContentToScroll || thumbLongerThanTrack)
{
if (Visibility != Visibility.Hidden)
{
Visibility = Visibility.Hidden;
}
ThumbCenterOffset = Double.NaN;
Density = Double.NaN;
decreaseButtonLength = 0.0;
increaseButtonLength = 0.0;
return false; // don't arrange
}
else if (Visibility != Visibility.Visible)
{
Visibility = Visibility.Visible;
}
// Compute lengths of increase and decrease button
double remainingTrackLength = trackLength - thumbLength;
decreaseButtonLength = remainingTrackLength * offset / range;
CoerceLength(ref decreaseButtonLength, remainingTrackLength);
increaseButtonLength = remainingTrackLength - decreaseButtonLength;
CoerceLength(ref increaseButtonLength, remainingTrackLength);
Density = range / remainingTrackLength;
return true;
}
// Bind track to templated parent
private void BindToTemplatedParent(DependencyProperty target, DependencyProperty source)
{
if (!HasNonDefaultValue(target))
{
Binding binding = new Binding();
binding.RelativeSource = RelativeSource.TemplatedParent;
binding.Path = new PropertyPath(source);
SetBinding(target, binding);
}
}
// Bind thumb or repeat button to templated parent
private void BindChildToTemplatedParent(FrameworkElement element, DependencyProperty target, DependencyProperty source)
{
if (element != null && !element.HasNonDefaultValue(target))
{
Binding binding = new Binding();
binding.Source = this.TemplatedParent;
binding.Path = new PropertyPath(source);
element.SetBinding(target, binding);
}
}
///
/// Track automatically sets bindings to its templated parent
/// to aid styling
///
internal override void OnPreApplyTemplate()
{
base.OnPreApplyTemplate();
RangeBase rangeBase = TemplatedParent as RangeBase;
if (rangeBase != null)
{
BindToTemplatedParent(MinimumProperty, RangeBase.MinimumProperty);
BindToTemplatedParent(MaximumProperty, RangeBase.MaximumProperty);
BindToTemplatedParent(ValueProperty, RangeBase.ValueProperty);
// Setup ScrollBar specific bindings
ScrollBar scrollBar = rangeBase as ScrollBar;
if (scrollBar != null)
{
BindToTemplatedParent(ViewportSizeProperty, ScrollBar.ViewportSizeProperty);
BindToTemplatedParent(OrientationProperty, ScrollBar.OrientationProperty);
}
else
{
// Setup Slider specific bindings
Slider slider = rangeBase as Slider;
if (slider != null)
{
BindToTemplatedParent(OrientationProperty, Slider.OrientationProperty);
BindToTemplatedParent(IsDirectionReversedProperty, Slider.IsDirectionReversedProperty);
BindChildToTemplatedParent(DecreaseRepeatButton, RepeatButton.DelayProperty, Slider.DelayProperty);
BindChildToTemplatedParent(DecreaseRepeatButton, RepeatButton.IntervalProperty, Slider.IntervalProperty);
BindChildToTemplatedParent(IncreaseRepeatButton, RepeatButton.DelayProperty, Slider.DelayProperty);
BindChildToTemplatedParent(IncreaseRepeatButton, RepeatButton.IntervalProperty, Slider.IntervalProperty);
}
}
}
}
#endregion
//-------------------------------------------------------------------
//
// Private Methods
//
//-------------------------------------------------------------------
#region Private Methods
#endregion
//-------------------------------------------------------------------
//
// Private Properties
//
//--------------------------------------------------------------------
#region Private Properties
private double ThumbCenterOffset
{
get { return _thumbCenterOffset; }
set { _thumbCenterOffset = value; }
}
private double Density
{
get { return _density; }
set { _density = value; }
}
//
// 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
//-------------------------------------------------------------------
//
// Private Fields
//
//--------------------------------------------------------------------
#region Private Fields
private RepeatButton _increaseButton;
private RepeatButton _decreaseButton;
private Thumb _thumb;
private Visual[] _visualChildren;
// Density of scrolling units present in 1/96" of track (not thumb). Computed during ArrangeOverride.
// Note that density default really *is* NaN. This corresponds to no track having been computed/displayed.
private double _density = Double.NaN;
private double _thumbCenterOffset = Double.NaN;
#endregion
}
}
// 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
- AlternateViewCollection.cs
- ConnectionManagementElement.cs
- WorkflowOperationBehavior.cs
- HtmlHead.cs
- MetadataProperty.cs
- ImageAnimator.cs
- CorrelationExtension.cs
- HotSpot.cs
- LocationReference.cs
- TextEditorCopyPaste.cs
- StaticContext.cs
- ProxyAttribute.cs
- sitestring.cs
- AbsoluteQuery.cs
- TextFormatter.cs
- Block.cs
- IsolatedStorageException.cs
- GenericRootAutomationPeer.cs
- ToolboxDataAttribute.cs
- ListViewCancelEventArgs.cs
- ThousandthOfEmRealDoubles.cs
- xmlformatgeneratorstatics.cs
- HostExecutionContextManager.cs
- ToolStripDropDownMenu.cs
- PeerTransportListenAddressConverter.cs
- RsaSecurityToken.cs
- SafeFindHandle.cs
- PngBitmapDecoder.cs
- Int64.cs
- NonParentingControl.cs
- FileVersionInfo.cs
- SqlDataSourceSelectingEventArgs.cs
- ExceptionUtil.cs
- ByteArrayHelperWithString.cs
- Application.cs
- SecurityRuntime.cs
- SequentialUshortCollection.cs
- DataGridViewCellStyleContentChangedEventArgs.cs
- ObjectAnimationBase.cs
- ProtocolsConfiguration.cs
- StyleTypedPropertyAttribute.cs
- externdll.cs
- SqlRetyper.cs
- XmlSchemaComplexContentRestriction.cs
- TextLineResult.cs
- ReferencedAssembly.cs
- AttachedPropertyBrowsableAttribute.cs
- HttpListenerPrefixCollection.cs
- ReadOnlyState.cs
- SQLByteStorage.cs
- DynamicValidatorEventArgs.cs
- ReadonlyMessageFilter.cs
- MaskInputRejectedEventArgs.cs
- Freezable.cs
- RelatedPropertyManager.cs
- MailMessageEventArgs.cs
- ScrollProperties.cs
- ClientConfigurationSystem.cs
- ListViewGroupItemCollection.cs
- UserPersonalizationStateInfo.cs
- IncrementalCompileAnalyzer.cs
- SynchronizationContext.cs
- XmlNullResolver.cs
- SecUtil.cs
- DiagnosticsConfiguration.cs
- RegexCaptureCollection.cs
- ShaderRenderModeValidation.cs
- Config.cs
- PauseStoryboard.cs
- Dictionary.cs
- RootBuilder.cs
- XmlSchemaComplexContentExtension.cs
- ExpressionBuilderContext.cs
- FloatUtil.cs
- _NetRes.cs
- BindingExpressionUncommonField.cs
- BooleanConverter.cs
- DynamicILGenerator.cs
- SerializationInfoEnumerator.cs
- ChainOfResponsibility.cs
- SchemaType.cs
- LeaseManager.cs
- ProfileService.cs
- HttpCachePolicy.cs
- LeaseManager.cs
- Win32PrintDialog.cs
- ServiceModelConfiguration.cs
- ActionFrame.cs
- FragmentQuery.cs
- UserControlCodeDomTreeGenerator.cs
- UpdatePanelControlTrigger.cs
- DataGridViewLinkCell.cs
- ColorConverter.cs
- BuilderPropertyEntry.cs
- OdbcUtils.cs
- CodeSubDirectory.cs
- ReadOnlyDictionary.cs
- VSWCFServiceContractGenerator.cs
- UniqueEventHelper.cs
- CodePropertyReferenceExpression.cs