Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Framework / System / Windows / Controls / Slider.cs / 1 / Slider.cs
//---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Collections; using System.Windows.Threading; using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using MS.Win32; using MS.Internal; using MS.Internal.Commands; // For typeconverter using System.ComponentModel.Design.Serialization; using System.Reflection; namespace System.Windows.Controls { ////// Slider control lets the user select from a range of values by moving a slider. /// Slider is used to enable to user to gradually modify a value (range selection). /// Slider is an easy and natural interface for users, because it provides good visual feedback. /// ///[Localizability(LocalizationCategory.Ignore)] [DefaultEvent("ValueChanged"), DefaultProperty("Value")] [TemplatePart(Name = "PART_Track", Type = typeof(Track))] [TemplatePart(Name = "PART_SelectionRange", Type = typeof(FrameworkElement))] public class Slider : RangeBase { #region Constructors /// /// Instantiates a new instance of a Slider with out Dispatcher. /// ///public Slider() : base() { } /// /// This is the static constructor for the Slider class. It /// simply registers the appropriate class handlers for the input /// devices, and defines a default style sheet. /// static Slider() { // Initialize CommandCollection & CommandLink(s) InitializeCommands(); // Register all PropertyTypeMetadata MinimumProperty.OverrideMetadata(typeof(Slider), new FrameworkPropertyMetadata(0.0d, FrameworkPropertyMetadataOptions.AffectsMeasure)); MaximumProperty.OverrideMetadata(typeof(Slider), new FrameworkPropertyMetadata(10.0d, FrameworkPropertyMetadataOptions.AffectsMeasure)); ValueProperty.OverrideMetadata(typeof(Slider), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsMeasure)); // Register Event Handler for the Thumb EventManager.RegisterClassHandler(typeof(Slider), Thumb.DragStartedEvent, new DragStartedEventHandler(Slider.OnThumbDragStarted)); EventManager.RegisterClassHandler(typeof(Slider), Thumb.DragDeltaEvent, new DragDeltaEventHandler(Slider.OnThumbDragDelta)); EventManager.RegisterClassHandler(typeof(Slider), Thumb.DragCompletedEvent, new DragCompletedEventHandler(Slider.OnThumbDragCompleted)); // Listen to MouseLeftButtonDown event to determine if slide should move focus to itself EventManager.RegisterClassHandler(typeof(Slider), Mouse.MouseDownEvent, new MouseButtonEventHandler(Slider._OnMouseLeftButtonDown),true); DefaultStyleKeyProperty.OverrideMetadata(typeof(Slider), new FrameworkPropertyMetadata(typeof(Slider))); _dType = DependencyObjectType.FromSystemTypeInternal(typeof(Slider)); } #endregion Constructors #region Commands private static RoutedCommand _increaseLargeCommand = null; private static RoutedCommand _increaseSmallCommand = null; private static RoutedCommand _decreaseLargeCommand = null; private static RoutedCommand _decreaseSmallCommand = null; private static RoutedCommand _minimizeValueCommand = null; private static RoutedCommand _maximizeValueCommand = null; ////// Increase Slider value /// public static RoutedCommand IncreaseLarge { get { return _increaseLargeCommand; } } ////// Decrease Slider value /// public static RoutedCommand DecreaseLarge { get { return _decreaseLargeCommand; } } ////// Increase Slider value /// public static RoutedCommand IncreaseSmall { get { return _increaseSmallCommand; } } ////// Decrease Slider value /// public static RoutedCommand DecreaseSmall { get { return _decreaseSmallCommand; } } ////// Set Slider value to mininum /// public static RoutedCommand MinimizeValue { get { return _minimizeValueCommand; } } ////// Set Slider value to maximum /// public static RoutedCommand MaximizeValue { get { return _maximizeValueCommand; } } static void InitializeCommands() { _increaseLargeCommand = new RoutedCommand("IncreaseLarge", typeof(Slider)); _decreaseLargeCommand = new RoutedCommand("DecreaseLarge", typeof(Slider)); _increaseSmallCommand = new RoutedCommand("IncreaseSmall", typeof(Slider)); _decreaseSmallCommand = new RoutedCommand("DecreaseSmall", typeof(Slider)); _minimizeValueCommand = new RoutedCommand("MinimizeValue", typeof(Slider)); _maximizeValueCommand = new RoutedCommand("MaximizeValue", typeof(Slider)); CommandHelpers.RegisterCommandHandler(typeof(Slider), _increaseLargeCommand, new ExecutedRoutedEventHandler(OnIncreaseLargeCommand), new SliderGesture(Key.PageUp, Key.PageDown, false)); CommandHelpers.RegisterCommandHandler(typeof(Slider), _decreaseLargeCommand, new ExecutedRoutedEventHandler(OnDecreaseLargeCommand), new SliderGesture(Key.PageDown, Key.PageUp, false)); CommandHelpers.RegisterCommandHandler(typeof(Slider), _increaseSmallCommand, new ExecutedRoutedEventHandler(OnIncreaseSmallCommand), new SliderGesture(Key.Up, Key.Down, false), new SliderGesture(Key.Right, Key.Left, true)); CommandHelpers.RegisterCommandHandler(typeof(Slider), _decreaseSmallCommand, new ExecutedRoutedEventHandler(OnDecreaseSmallCommand), new SliderGesture(Key.Down, Key.Up, false), new SliderGesture(Key.Left, Key.Right, true)); CommandHelpers.RegisterCommandHandler(typeof(Slider), _minimizeValueCommand, new ExecutedRoutedEventHandler(OnMinimizeValueCommand), Key.Home); CommandHelpers.RegisterCommandHandler(typeof(Slider), _maximizeValueCommand, new ExecutedRoutedEventHandler(OnMaximizeValueCommand), Key.End); } private class SliderGesture : InputGesture { public SliderGesture(Key normal, Key inverted, bool forHorizontal) { _normal = normal; _inverted = inverted; _forHorizontal = forHorizontal; } ////// Sees if the InputGesture matches the input associated with the inputEventArgs /// public override bool Matches(object targetElement, InputEventArgs inputEventArgs) { KeyEventArgs keyEventArgs = inputEventArgs as KeyEventArgs; Slider slider = targetElement as Slider; if (keyEventArgs != null && slider != null && Keyboard.Modifiers == ModifierKeys.None) { if((int)_normal == (int)keyEventArgs.RealKey) { return !IsInverted(slider); } if ((int)_inverted == (int)keyEventArgs.RealKey) { return IsInverted(slider); } } return false; } private bool IsInverted(Slider slider) { if (_forHorizontal) { return slider.IsDirectionReversed != (slider.FlowDirection == FlowDirection.RightToLeft); } else { return slider.IsDirectionReversed; } } private Key _normal, _inverted; private bool _forHorizontal; } private static void OnIncreaseSmallCommand(object sender, ExecutedRoutedEventArgs e) { Slider slider = sender as Slider; if (slider != null) { slider.OnIncreaseSmall(); } } private static void OnDecreaseSmallCommand(object sender, ExecutedRoutedEventArgs e) { Slider slider = sender as Slider; if (slider != null) { slider.OnDecreaseSmall(); } } private static void OnMaximizeValueCommand(object sender, ExecutedRoutedEventArgs e) { Slider slider = sender as Slider; if (slider != null) { slider.OnMaximizeValue(); } } private static void OnMinimizeValueCommand(object sender, ExecutedRoutedEventArgs e) { Slider slider = sender as Slider; if (slider != null) { slider.OnMinimizeValue(); } } private static void OnIncreaseLargeCommand(object sender, ExecutedRoutedEventArgs e) { Slider slider = sender as Slider; if (slider != null) { slider.OnIncreaseLarge(); } } private static void OnDecreaseLargeCommand(object sender, ExecutedRoutedEventArgs e) { Slider slider = sender as Slider; if (slider != null) { slider.OnDecreaseLarge(); } } #endregion Commands #region Properties #region Orientation Property ////// DependencyProperty for public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(Slider), new FrameworkPropertyMetadata(Orientation.Horizontal), new ValidateValueCallback(ScrollBar.IsValidOrientation)); ///property. /// /// Get/Set Orientation property /// public Orientation Orientation { get { return (Orientation)GetValue(OrientationProperty); } set { SetValue(OrientationProperty, value); } } #endregion #region IsDirectionReversed Property ////// Slider ThumbProportion property /// public static readonly DependencyProperty IsDirectionReversedProperty = DependencyProperty.Register("IsDirectionReversed", typeof(bool), typeof(Slider), new FrameworkPropertyMetadata(false)); ////// Get/Set IsDirectionReversed property /// [Bindable(true), Category("Appearance")] public bool IsDirectionReversed { get { return (bool)GetValue(IsDirectionReversedProperty); } set { SetValue(IsDirectionReversedProperty, value); } } #endregion #region Delay Property ////// The Property for the Delay property. /// public static readonly DependencyProperty DelayProperty = RepeatButton.DelayProperty.AddOwner(typeof(Slider), new FrameworkPropertyMetadata(RepeatButton.GetKeyboardDelay())); ////// Specifies the amount of time, in milliseconds, to wait before repeating begins. /// Must be non-negative. /// [Bindable(true), Category("Behavior")] public int Delay { get { return (int)GetValue(DelayProperty); } set { SetValue(DelayProperty, value); } } #endregion Delay Property #region Interval Property ////// The Property for the Interval property. /// public static readonly DependencyProperty IntervalProperty = RepeatButton.IntervalProperty.AddOwner(typeof(Slider), new FrameworkPropertyMetadata(RepeatButton.GetKeyboardSpeed())); ////// Specifies the amount of time, in milliseconds, between repeats once repeating starts. /// Must be non-negative /// [Bindable(true), Category("Behavior")] public int Interval { get { return (int)GetValue(IntervalProperty); } set { SetValue(IntervalProperty, value); } } #endregion Interval Property #region AutoToolTipPlacement Property ////// The DependencyProperty for the AutoToolTipPlacement property. /// public static readonly DependencyProperty AutoToolTipPlacementProperty = DependencyProperty.Register("AutoToolTipPlacement", typeof(AutoToolTipPlacement), typeof(Slider), new FrameworkPropertyMetadata(Primitives.AutoToolTipPlacement.None), new ValidateValueCallback(IsValidAutoToolTipPlacement)); ////// AutoToolTipPlacement property specifies the placement of the AutoToolTip /// [Bindable(true), Category("Behavior")] public Primitives.AutoToolTipPlacement AutoToolTipPlacement { get { return (Primitives.AutoToolTipPlacement)GetValue(AutoToolTipPlacementProperty); } set { SetValue(AutoToolTipPlacementProperty, value); } } private static bool IsValidAutoToolTipPlacement(object o) { AutoToolTipPlacement placement = (AutoToolTipPlacement)o; return placement == AutoToolTipPlacement.None || placement == AutoToolTipPlacement.TopLeft || placement == AutoToolTipPlacement.BottomRight; } #endregion #region AutoToolTipPrecision Property ////// The DependencyProperty for the AutoToolTipPrecision property. /// Flags: None /// Default Value: 0 /// public static readonly DependencyProperty AutoToolTipPrecisionProperty = DependencyProperty.Register("AutoToolTipPrecision", typeof(int), typeof(Slider), new FrameworkPropertyMetadata(0), new ValidateValueCallback(IsValidAutoToolTipPrecision)); ////// Get or set number of decimal digits of Slider's Value shown in AutoToolTip /// [Bindable(true), Category("Appearance")] public int AutoToolTipPrecision { get { return (int)GetValue(AutoToolTipPrecisionProperty); } set { SetValue(AutoToolTipPrecisionProperty, value); } } ////// Validates AutoToolTipPrecision value /// /// ///private static bool IsValidAutoToolTipPrecision(object o) { return (((int)o) >= 0); } #endregion /* * TickMark support * * - double TickFrequency * - bool IsSnapToTickEnabled * - Enum TickPlacement * - DoubleCollection Ticks */ #region TickMark support /// /// The DependencyProperty for the IsSnapToTickEnabled property. /// public static readonly DependencyProperty IsSnapToTickEnabledProperty = DependencyProperty.Register("IsSnapToTickEnabled", typeof(bool), typeof(Slider), new FrameworkPropertyMetadata(false)); ////// When 'true', Slider will automatically move the Thumb (and/or change current value) to the closest TickMark. /// [Bindable(true), Category("Behavior")] public bool IsSnapToTickEnabled { get { return (bool)GetValue(IsSnapToTickEnabledProperty); } set { SetValue(IsSnapToTickEnabledProperty, value); } } ////// The DependencyProperty for the TickPlacement property. /// public static readonly DependencyProperty TickPlacementProperty = DependencyProperty.Register("TickPlacement", typeof(Primitives.TickPlacement), typeof(Slider), new FrameworkPropertyMetadata(Primitives.TickPlacement.None), new ValidateValueCallback(IsValidTickPlacement)); ////// Slider uses this value to determine where to show the Ticks. /// When Ticks is not 'null', Slider will ignore 'TickFrequency', and draw only TickMarks /// that specified in Ticks collection. /// [Bindable(true), Category("Appearance")] public Primitives.TickPlacement TickPlacement { get { return (Primitives.TickPlacement)GetValue(TickPlacementProperty); } set { SetValue(TickPlacementProperty, value); } } private static bool IsValidTickPlacement(object o) { TickPlacement value = (TickPlacement)o; return value == TickPlacement.None || value == TickPlacement.TopLeft || value == TickPlacement.BottomRight || value == TickPlacement.Both; } ////// The DependencyProperty for the TickFrequency property. /// Default Value is 1.0 /// public static readonly DependencyProperty TickFrequencyProperty = DependencyProperty.Register("TickFrequency", typeof(double), typeof(Slider), new FrameworkPropertyMetadata(1.0), new ValidateValueCallback(IsValidDoubleValue)); ////// Slider uses this value to determine where to show the Ticks. /// When Ticks is not 'null', Slider will ignore 'TickFrequency', and draw only TickMarks /// that specified in Ticks collection. /// [Bindable(true), Category("Appearance")] public double TickFrequency { get { return (double)GetValue(TickFrequencyProperty); } set { SetValue(TickFrequencyProperty, value); } } // ////// The DependencyProperty for the Ticks property. /// public static readonly DependencyProperty TicksProperty = DependencyProperty.Register("Ticks", typeof(DoubleCollection), typeof(Slider), new FrameworkPropertyMetadata(new FreezableDefaultValueFactory(DoubleCollection.Empty))); ////// Slider uses this value to determine where to show the Ticks. /// When Ticks is not 'null', Slider will ignore 'TickFrequency', and draw only TickMarks /// that specified in Ticks collection. /// [Bindable(true), Category("Appearance")] public DoubleCollection Ticks { get { return (DoubleCollection)GetValue(TicksProperty); } set { SetValue(TicksProperty, value); } } #endregion TickMark support /* * Selection support * * - bool IsSelectionRangeEnabled * - double SelectionStart * - double SelectionEnd */ #region Selection supports ////// The DependencyProperty for the IsSelectionRangeEnabled property. /// public static readonly DependencyProperty IsSelectionRangeEnabledProperty = DependencyProperty.Register("IsSelectionRangeEnabled", typeof(bool), typeof(Slider), new FrameworkPropertyMetadata(false)); ////// Enable or disable selection support on Slider /// [Bindable(true), Category("Appearance")] public bool IsSelectionRangeEnabled { get { return (bool)GetValue(IsSelectionRangeEnabledProperty); } set { SetValue(IsSelectionRangeEnabledProperty, value); } } ////// The DependencyProperty for the SelectionStart property. /// public static readonly DependencyProperty SelectionStartProperty = DependencyProperty.Register("SelectionStart", typeof(double), typeof(Slider), new FrameworkPropertyMetadata(0.0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnSelectionStartChanged), new CoerceValueCallback(CoerceSelectionStart)), new ValidateValueCallback(IsValidDoubleValue)); ////// Get or set starting value of selection. /// [Bindable(true), Category("Appearance")] public double SelectionStart { get { return (double) GetValue(SelectionStartProperty); } set { SetValue(SelectionStartProperty, value); } } private static void OnSelectionStartChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Slider ctrl = (Slider)d; double oldValue = (double)e.OldValue; double newValue = (double)e.NewValue; ctrl.CoerceValue(SelectionEndProperty); ctrl.UpdateSelectionRangeElementPositionAndSize(); } private static object CoerceSelectionStart(DependencyObject d, object value) { Slider slider = (Slider)d; double selection = (double)value; double min = slider.Minimum; double max = slider.Maximum; if (selection < min) { return min; } if (selection > max) { return max; } return value; } ////// The DependencyProperty for the SelectionEnd property. /// public static readonly DependencyProperty SelectionEndProperty = DependencyProperty.Register("SelectionEnd", typeof(double), typeof(Slider), new FrameworkPropertyMetadata(0.0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnSelectionEndChanged), new CoerceValueCallback(CoerceSelectionEnd)), new ValidateValueCallback(IsValidDoubleValue)); ////// Get or set starting value of selection. /// [Bindable(true), Category("Appearance")] public double SelectionEnd { get { return (double) GetValue(SelectionEndProperty); } set { SetValue(SelectionEndProperty, value); } } private static void OnSelectionEndChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Slider ctrl = (Slider)d; ctrl.UpdateSelectionRangeElementPositionAndSize(); } private static object CoerceSelectionEnd(DependencyObject d, object value) { Slider slider = (Slider)d; double selection = (double)value; double min = slider.SelectionStart; double max = slider.Maximum; if (selection < min) { return min; } if (selection > max) { return max; } return value; } ////// Called when the value of SelectionEnd is required by the property system. /// /// The object on which the property was queried. ///The value of the SelectionEnd property on "d." private static object OnGetSelectionEnd(DependencyObject d) { return ((Slider)d).SelectionEnd; } ////// This method is invoked when the Minimum property changes. /// /// The old value of the Minimum property. /// The new value of the Minimum property. protected override void OnMinimumChanged(double oldMinimum, double newMinimum) { CoerceValue(SelectionStartProperty); } ////// This method is invoked when the Maximum property changes. /// /// The old value of the Maximum property. /// The new value of the Maximum property. protected override void OnMaximumChanged(double oldMaximum, double newMaximum) { CoerceValue(SelectionStartProperty); CoerceValue(SelectionEndProperty); } #endregion Selection supports /* * Move-To-Point support * * Property * - bool IsMoveToPointEnabled * * Event Handlers * - OnPreviewMouseLeftButtonDown * - double SelectionEnd */ #region Move-To-Point support ////// The DependencyProperty for the IsMoveToPointEnabled property. /// public static readonly DependencyProperty IsMoveToPointEnabledProperty = DependencyProperty.Register("IsMoveToPointEnabled", typeof(bool), typeof(Slider), new FrameworkPropertyMetadata(false)); ////// Enable or disable Move-To-Point support on Slider. /// Move-To-Point feature, enables Slider to immediately move the Thumb directly to the location where user /// clicked the Mouse. /// [Bindable(true), Category("Behavior")] public bool IsMoveToPointEnabled { get { return (bool)GetValue(IsMoveToPointEnabledProperty); } set { SetValue(IsMoveToPointEnabledProperty, value); } } ////// When IsMoveToPointEneabled is 'true', Slider needs to preview MouseLeftButtonDown event, in order prevent its RepeatButtons /// from handle Left-Click. /// /// protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) { if (IsMoveToPointEnabled && Track != null && Track.Thumb != null && !Track.Thumb.IsMouseOver) { // Move Thumb to the Mouse location Point pt = e.MouseDevice.GetPosition(Track); double newValue = Track.ValueFromPoint(pt); if (System.Windows.Shapes.Shape.IsDoubleFinite(newValue)) { UpdateValue(newValue); } e.Handled = true; } base.OnPreviewMouseLeftButtonDown(e); } #endregion Move-To-Point support #endregion // Properties #region Event Handlers ////// Listen to Thumb DragStarted event. /// /// /// private static void OnThumbDragStarted(object sender, DragStartedEventArgs e) { Slider slider = sender as Slider; slider.OnThumbDragStarted(e); } ////// Listen to Thumb DragDelta event. /// /// /// private static void OnThumbDragDelta(object sender, DragDeltaEventArgs e) { Slider slider = sender as Slider; slider.OnThumbDragDelta(e); } ////// Listen to Thumb DragCompleted event. /// /// /// private static void OnThumbDragCompleted(object sender, DragCompletedEventArgs e) { Slider slider = sender as Slider; slider.OnThumbDragCompleted(e); } ////// Called when user start dragging the Thumb. /// This function can be override to customize the way Slider handles Thumb movement. /// /// protected virtual void OnThumbDragStarted(DragStartedEventArgs e) { // Show AutoToolTip if needed. Thumb thumb = e.OriginalSource as Thumb; if ((thumb == null) || (this.AutoToolTipPlacement == Primitives.AutoToolTipPlacement.None)) { return; } // Save original tooltip _thumbOriginalToolTip = thumb.ToolTip; if (_autoToolTip == null) { _autoToolTip = new ToolTip(); _autoToolTip.Placement = PlacementMode.Custom; _autoToolTip.PlacementTarget = thumb; _autoToolTip.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(this.AutoToolTipCustomPlacementCallback); } thumb.ToolTip = _autoToolTip; _autoToolTip.Content = GetAutoToolTipNumber(); _autoToolTip.IsOpen = true; ((Popup)_autoToolTip.Parent).Reposition(); } ////// Called when user dragging the Thumb. /// This function can be override to customize the way Slider handles Thumb movement. /// /// protected virtual void OnThumbDragDelta(DragDeltaEventArgs e) { Thumb thumb = e.OriginalSource as Thumb; // Convert to Track's co-ordinate if (Track != null && thumb == Track.Thumb) { double newValue = Value + Track.ValueFromDistance(e.HorizontalChange, e.VerticalChange); if (System.Windows.Shapes.Shape.IsDoubleFinite(newValue)) { UpdateValue(newValue); } // Show AutoToolTip if needed if (this.AutoToolTipPlacement != Primitives.AutoToolTipPlacement.None) { if (_autoToolTip == null) { _autoToolTip = new ToolTip(); } _autoToolTip.Content = GetAutoToolTipNumber(); if (thumb.ToolTip != _autoToolTip) { thumb.ToolTip = _autoToolTip; } if (!_autoToolTip.IsOpen) { _autoToolTip.IsOpen = true; } ((Popup)_autoToolTip.Parent).Reposition(); } } } private string GetAutoToolTipNumber() { NumberFormatInfo format = (NumberFormatInfo)(NumberFormatInfo.CurrentInfo.Clone()); format.NumberDecimalDigits = this.AutoToolTipPrecision; return this.Value.ToString("N", format); } ////// Called when user stop dragging the Thumb. /// This function can be override to customize the way Slider handles Thumb movement. /// /// protected virtual void OnThumbDragCompleted(DragCompletedEventArgs e) { // Show AutoToolTip if needed. Thumb thumb = e.OriginalSource as Thumb; if ((thumb == null) || (this.AutoToolTipPlacement == Primitives.AutoToolTipPlacement.None)) { return; } if (_autoToolTip != null) { _autoToolTip.IsOpen = false; } thumb.ToolTip = _thumbOriginalToolTip; } private CustomPopupPlacement[] AutoToolTipCustomPlacementCallback(Size popupSize, Size targetSize, Point offset) { switch (this.AutoToolTipPlacement) { case Primitives.AutoToolTipPlacement.TopLeft: if (Orientation == Orientation.Horizontal) { // Place popup at top of thumb return new CustomPopupPlacement[]{new CustomPopupPlacement( new Point((targetSize.Width - popupSize.Width) * 0.5, -popupSize.Height), PopupPrimaryAxis.Horizontal) }; } else { // Place popup at left of thumb return new CustomPopupPlacement[] { new CustomPopupPlacement( new Point(-popupSize.Width, (targetSize.Height - popupSize.Height) * 0.5), PopupPrimaryAxis.Vertical) }; } case Primitives.AutoToolTipPlacement.BottomRight: if (Orientation == Orientation.Horizontal) { // Place popup at bottom of thumb return new CustomPopupPlacement[] { new CustomPopupPlacement( new Point((targetSize.Width - popupSize.Width) * 0.5, targetSize.Height) , PopupPrimaryAxis.Horizontal) }; } else { // Place popup at right of thumb return new CustomPopupPlacement[] { new CustomPopupPlacement( new Point(targetSize.Width, (targetSize.Height - popupSize.Height) * 0.5), PopupPrimaryAxis.Vertical) }; } default: return new CustomPopupPlacement[]{}; } } ////// Resize and resposition the SelectionRangeElement. /// private void UpdateSelectionRangeElementPositionAndSize() { Size trackSize = new Size(0d, 0d); Size thumbSize = new Size(0d, 0d); if (Track == null || DoubleUtil.LessThan(SelectionEnd,SelectionStart)) { return; } trackSize = Track.RenderSize; thumbSize = (Track.Thumb != null) ? Track.Thumb.RenderSize : new Size(0d, 0d); double range = Maximum - Minimum; double valueToSize; FrameworkElement rangeElement = this.SelectionRangeElement as FrameworkElement; if (rangeElement == null) { return; } if (Orientation == Orientation.Horizontal) { // Calculate part size for HorizontalSlider if (DoubleUtil.AreClose(range, 0d) || (DoubleUtil.AreClose(trackSize.Width, thumbSize.Width))) { valueToSize = 0d; } else { valueToSize = Math.Max(0.0, (trackSize.Width - thumbSize.Width) / range); } rangeElement.Width = ((SelectionEnd - SelectionStart) * valueToSize); if (IsDirectionReversed) { Canvas.SetLeft(rangeElement, (thumbSize.Width * 0.5) + Math.Max(Maximum - SelectionEnd, 0) * valueToSize); } else { Canvas.SetLeft(rangeElement, (thumbSize.Width * 0.5) + Math.Max(SelectionStart - Minimum, 0) * valueToSize); } } else { // Calculate part size for VerticalSlider if (DoubleUtil.AreClose(range, 0d) || (DoubleUtil.AreClose(trackSize.Height, thumbSize.Height))) { valueToSize = 0d; } else { valueToSize = Math.Max(0.0, (trackSize.Height - thumbSize.Height) / range); } rangeElement.Height = ((SelectionEnd - SelectionStart) * valueToSize); if (IsDirectionReversed) { Canvas.SetTop(rangeElement, (thumbSize.Height * 0.5) + Math.Max(SelectionStart - Minimum, 0) * valueToSize); } else { Canvas.SetTop(rangeElement, (thumbSize.Height * 0.5) + Math.Max(Maximum - SelectionEnd,0) * valueToSize); } } } ////// Gets or sets reference to Slider's Track element. /// internal Track Track { get { return _track; } set { _track = value; } } ////// Gets or sets reference to Slider's SelectionRange element. /// internal FrameworkElement SelectionRangeElement { get { return _selectionRangeElement; } set { _selectionRangeElement = value; } } ////// Snap the input 'value' to the closest tick. /// If input value is exactly in the middle of 2 surrounding ticks, it will be snapped to the tick that has greater value. /// /// Value that want to snap to closest Tick. ///Snapped value if IsSnapToTickEnabled is 'true'. Otherwise, returns un-snaped value. private double SnapToTick(double value) { if (IsSnapToTickEnabled) { double previous = Minimum; double next = Maximum; // This property is rarely set so let's try to avoid the GetValue // caching of the mutable default value DoubleCollection ticks = null; bool hasModifiers; if (GetValueSource(TicksProperty, null, out hasModifiers) != BaseValueSourceInternal.Default || hasModifiers) { ticks = Ticks; } // If ticks collection is available, use it. // Note that ticks may be unsorted. if ((ticks != null) && (ticks.Count > 0)) { for (int i = 0; i < ticks.Count; i++) { double tick = ticks[i]; if (DoubleUtil.AreClose(tick, value)) { return value; } if (DoubleUtil.LessThan(tick, value) && DoubleUtil.GreaterThan(tick, previous)) { previous = tick; } else if (DoubleUtil.GreaterThan(tick ,value) && DoubleUtil.LessThan(tick, next)) { next = tick; } } } else if (DoubleUtil.GreaterThan(TickFrequency, 0.0)) { previous = Minimum + (Math.Round(((value - Minimum) / TickFrequency)) * TickFrequency); next = Math.Min(Maximum, previous + TickFrequency); } // Choose the closest value between previous and next. If tie, snap to 'next'. value = DoubleUtil.GreaterThanOrClose(value, (previous + next) * 0.5) ? next : previous; } return value; } // Sets Value = SnapToTick(value+direction), unless the result of SnapToTick is Value, // then it searches for the next tick greater(if direction is positive) than value // and sets Value to that tick private void MoveToNextTick(double direction) { if (direction != 0.0) { double value = this.Value; // Find the next value by snapping double next = SnapToTick(Math.Max(this.Minimum, Math.Min(this.Maximum, value + direction))); bool greaterThan = direction > 0; //search for the next tick greater than value? // If the snapping brought us back to value, find the next tick point if (next == value && !( greaterThan && value == Maximum) // Stop if searching up if already at Max && !(!greaterThan && value == Minimum)) // Stop if searching down if already at Min { // This property is rarely set so let's try to avoid the GetValue // caching of the mutable default value DoubleCollection ticks = null; bool hasModifiers; if (GetValueSource(TicksProperty, null, out hasModifiers) != BaseValueSourceInternal.Default || hasModifiers) { ticks = Ticks; } // If ticks collection is available, use it. // Note that ticks may be unsorted. if ((ticks != null) && (ticks.Count > 0)) { for (int i = 0; i < ticks.Count; i++) { double tick = ticks[i]; // Find the smallest tick greater than value or the largest tick less than value if ((greaterThan && DoubleUtil.GreaterThan(tick, value) && (DoubleUtil.LessThan(tick, next) || next == value)) ||(!greaterThan && DoubleUtil.LessThan(tick, value) && (DoubleUtil.GreaterThan(tick, next) || next == value))) { next = tick; } } } else if (DoubleUtil.GreaterThan(TickFrequency, 0.0)) { // Find the current tick we are at double tickNumber = Math.Round((value - Minimum) / TickFrequency); if (greaterThan) tickNumber += 1.0; else tickNumber -= 1.0; next = Minimum + tickNumber * TickFrequency; } } // Update if we've found a better value if (next != value) { this.Value = next; } } } #endregion Event Handlers #region Override Functions ////// Creates AutomationPeer ( protected override AutomationPeer OnCreateAutomationPeer() { return new SliderAutomationPeer(this); } ///) /// /// This is a class handler for MouseLeftButtonDown event. /// The purpose of this handle is to move input focus to Slider when user pressed /// mouse left button on any part of slider that is not focusable. /// /// /// private static void _OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if(e.ChangedButton != MouseButton.Left) return; Slider slider = (Slider)sender; // When someone click on the Slider's part, and it's not focusable // Slider need to take the focus in order to process keyboard correctly if (!slider.IsKeyboardFocusWithin) { e.Handled = slider.Focus() || e.Handled; } } ////// Perform arrangement of slider's children /// /// protected override Size ArrangeOverride(Size finalSize) { Size size = base.ArrangeOverride(finalSize); UpdateSelectionRangeElementPositionAndSize(); return size; } ////// Update SelectionRange Length. /// /// /// protected override void OnValueChanged(double oldValue, double newValue) { base.OnValueChanged(oldValue, newValue); UpdateSelectionRangeElementPositionAndSize(); } ////// Slider locates the SelectionRangeElement when its visual tree is created /// public override void OnApplyTemplate() { base.OnApplyTemplate(); SelectionRangeElement = GetTemplateChild(SelectionRangeElementName) as FrameworkElement; Track = GetTemplateChild(TrackName) as Track; if (_autoToolTip != null) { _autoToolTip.PlacementTarget = Track != null ? Track.Thumb : null; } } #endregion Override Functions #region Virtual Functions ////// Call when Slider.IncreaseLarge command is invoked. /// protected virtual void OnIncreaseLarge() { MoveToNextTick(this.LargeChange); } ////// Call when Slider.DecreaseLarge command is invoked. /// protected virtual void OnDecreaseLarge() { MoveToNextTick(-this.LargeChange); } ////// Call when Slider.IncreaseSmall command is invoked. /// protected virtual void OnIncreaseSmall() { MoveToNextTick(this.SmallChange); } ////// Call when Slider.DecreaseSmall command is invoked. /// protected virtual void OnDecreaseSmall() { MoveToNextTick(-this.SmallChange); } ////// Call when Slider.MaximizeValue command is invoked. /// protected virtual void OnMaximizeValue() { this.Value = this.Maximum; } ////// Call when Slider.MinimizeValue command is invoked. /// protected virtual void OnMinimizeValue() { this.Value = this.Minimum; } #endregion Virtual Functions #region Helper Functions ////// Helper function for value update. /// This function will also snap the value to tick, if IsSnapToTickEnabled is true. /// /// private void UpdateValue(double value) { Double snappedValue = SnapToTick(value); if (snappedValue != Value) { Value = Math.Max(this.Minimum, Math.Min(this.Maximum, snappedValue)); } } ////// Validate input value in Slider (LargeChange, SmallChange, SelectionStart, SelectionEnd, and TickFrequency). /// /// ///Returns False if value is NaN or NegativeInfinity or PositiveInfinity. Otherwise, returns True. private static bool IsValidDoubleValue(object value) { double d = (double)value; return !(DoubleUtil.IsNaN(d) || double.IsInfinity(d)); } #endregion Helper Functions #region Private Fields private const string TrackName = "PART_Track"; private const string SelectionRangeElementName = "PART_SelectionRange"; // Slider required parts private FrameworkElement _selectionRangeElement; private Track _track; private ToolTip _autoToolTip = null; private object _thumbOriginalToolTip = null; #endregion Private Fields #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 System; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Collections; using System.Windows.Threading; using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using MS.Win32; using MS.Internal; using MS.Internal.Commands; // For typeconverter using System.ComponentModel.Design.Serialization; using System.Reflection; namespace System.Windows.Controls { ////// Slider control lets the user select from a range of values by moving a slider. /// Slider is used to enable to user to gradually modify a value (range selection). /// Slider is an easy and natural interface for users, because it provides good visual feedback. /// ///[Localizability(LocalizationCategory.Ignore)] [DefaultEvent("ValueChanged"), DefaultProperty("Value")] [TemplatePart(Name = "PART_Track", Type = typeof(Track))] [TemplatePart(Name = "PART_SelectionRange", Type = typeof(FrameworkElement))] public class Slider : RangeBase { #region Constructors /// /// Instantiates a new instance of a Slider with out Dispatcher. /// ///public Slider() : base() { } /// /// This is the static constructor for the Slider class. It /// simply registers the appropriate class handlers for the input /// devices, and defines a default style sheet. /// static Slider() { // Initialize CommandCollection & CommandLink(s) InitializeCommands(); // Register all PropertyTypeMetadata MinimumProperty.OverrideMetadata(typeof(Slider), new FrameworkPropertyMetadata(0.0d, FrameworkPropertyMetadataOptions.AffectsMeasure)); MaximumProperty.OverrideMetadata(typeof(Slider), new FrameworkPropertyMetadata(10.0d, FrameworkPropertyMetadataOptions.AffectsMeasure)); ValueProperty.OverrideMetadata(typeof(Slider), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsMeasure)); // Register Event Handler for the Thumb EventManager.RegisterClassHandler(typeof(Slider), Thumb.DragStartedEvent, new DragStartedEventHandler(Slider.OnThumbDragStarted)); EventManager.RegisterClassHandler(typeof(Slider), Thumb.DragDeltaEvent, new DragDeltaEventHandler(Slider.OnThumbDragDelta)); EventManager.RegisterClassHandler(typeof(Slider), Thumb.DragCompletedEvent, new DragCompletedEventHandler(Slider.OnThumbDragCompleted)); // Listen to MouseLeftButtonDown event to determine if slide should move focus to itself EventManager.RegisterClassHandler(typeof(Slider), Mouse.MouseDownEvent, new MouseButtonEventHandler(Slider._OnMouseLeftButtonDown),true); DefaultStyleKeyProperty.OverrideMetadata(typeof(Slider), new FrameworkPropertyMetadata(typeof(Slider))); _dType = DependencyObjectType.FromSystemTypeInternal(typeof(Slider)); } #endregion Constructors #region Commands private static RoutedCommand _increaseLargeCommand = null; private static RoutedCommand _increaseSmallCommand = null; private static RoutedCommand _decreaseLargeCommand = null; private static RoutedCommand _decreaseSmallCommand = null; private static RoutedCommand _minimizeValueCommand = null; private static RoutedCommand _maximizeValueCommand = null; ////// Increase Slider value /// public static RoutedCommand IncreaseLarge { get { return _increaseLargeCommand; } } ////// Decrease Slider value /// public static RoutedCommand DecreaseLarge { get { return _decreaseLargeCommand; } } ////// Increase Slider value /// public static RoutedCommand IncreaseSmall { get { return _increaseSmallCommand; } } ////// Decrease Slider value /// public static RoutedCommand DecreaseSmall { get { return _decreaseSmallCommand; } } ////// Set Slider value to mininum /// public static RoutedCommand MinimizeValue { get { return _minimizeValueCommand; } } ////// Set Slider value to maximum /// public static RoutedCommand MaximizeValue { get { return _maximizeValueCommand; } } static void InitializeCommands() { _increaseLargeCommand = new RoutedCommand("IncreaseLarge", typeof(Slider)); _decreaseLargeCommand = new RoutedCommand("DecreaseLarge", typeof(Slider)); _increaseSmallCommand = new RoutedCommand("IncreaseSmall", typeof(Slider)); _decreaseSmallCommand = new RoutedCommand("DecreaseSmall", typeof(Slider)); _minimizeValueCommand = new RoutedCommand("MinimizeValue", typeof(Slider)); _maximizeValueCommand = new RoutedCommand("MaximizeValue", typeof(Slider)); CommandHelpers.RegisterCommandHandler(typeof(Slider), _increaseLargeCommand, new ExecutedRoutedEventHandler(OnIncreaseLargeCommand), new SliderGesture(Key.PageUp, Key.PageDown, false)); CommandHelpers.RegisterCommandHandler(typeof(Slider), _decreaseLargeCommand, new ExecutedRoutedEventHandler(OnDecreaseLargeCommand), new SliderGesture(Key.PageDown, Key.PageUp, false)); CommandHelpers.RegisterCommandHandler(typeof(Slider), _increaseSmallCommand, new ExecutedRoutedEventHandler(OnIncreaseSmallCommand), new SliderGesture(Key.Up, Key.Down, false), new SliderGesture(Key.Right, Key.Left, true)); CommandHelpers.RegisterCommandHandler(typeof(Slider), _decreaseSmallCommand, new ExecutedRoutedEventHandler(OnDecreaseSmallCommand), new SliderGesture(Key.Down, Key.Up, false), new SliderGesture(Key.Left, Key.Right, true)); CommandHelpers.RegisterCommandHandler(typeof(Slider), _minimizeValueCommand, new ExecutedRoutedEventHandler(OnMinimizeValueCommand), Key.Home); CommandHelpers.RegisterCommandHandler(typeof(Slider), _maximizeValueCommand, new ExecutedRoutedEventHandler(OnMaximizeValueCommand), Key.End); } private class SliderGesture : InputGesture { public SliderGesture(Key normal, Key inverted, bool forHorizontal) { _normal = normal; _inverted = inverted; _forHorizontal = forHorizontal; } ////// Sees if the InputGesture matches the input associated with the inputEventArgs /// public override bool Matches(object targetElement, InputEventArgs inputEventArgs) { KeyEventArgs keyEventArgs = inputEventArgs as KeyEventArgs; Slider slider = targetElement as Slider; if (keyEventArgs != null && slider != null && Keyboard.Modifiers == ModifierKeys.None) { if((int)_normal == (int)keyEventArgs.RealKey) { return !IsInverted(slider); } if ((int)_inverted == (int)keyEventArgs.RealKey) { return IsInverted(slider); } } return false; } private bool IsInverted(Slider slider) { if (_forHorizontal) { return slider.IsDirectionReversed != (slider.FlowDirection == FlowDirection.RightToLeft); } else { return slider.IsDirectionReversed; } } private Key _normal, _inverted; private bool _forHorizontal; } private static void OnIncreaseSmallCommand(object sender, ExecutedRoutedEventArgs e) { Slider slider = sender as Slider; if (slider != null) { slider.OnIncreaseSmall(); } } private static void OnDecreaseSmallCommand(object sender, ExecutedRoutedEventArgs e) { Slider slider = sender as Slider; if (slider != null) { slider.OnDecreaseSmall(); } } private static void OnMaximizeValueCommand(object sender, ExecutedRoutedEventArgs e) { Slider slider = sender as Slider; if (slider != null) { slider.OnMaximizeValue(); } } private static void OnMinimizeValueCommand(object sender, ExecutedRoutedEventArgs e) { Slider slider = sender as Slider; if (slider != null) { slider.OnMinimizeValue(); } } private static void OnIncreaseLargeCommand(object sender, ExecutedRoutedEventArgs e) { Slider slider = sender as Slider; if (slider != null) { slider.OnIncreaseLarge(); } } private static void OnDecreaseLargeCommand(object sender, ExecutedRoutedEventArgs e) { Slider slider = sender as Slider; if (slider != null) { slider.OnDecreaseLarge(); } } #endregion Commands #region Properties #region Orientation Property ////// DependencyProperty for public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(Slider), new FrameworkPropertyMetadata(Orientation.Horizontal), new ValidateValueCallback(ScrollBar.IsValidOrientation)); ///property. /// /// Get/Set Orientation property /// public Orientation Orientation { get { return (Orientation)GetValue(OrientationProperty); } set { SetValue(OrientationProperty, value); } } #endregion #region IsDirectionReversed Property ////// Slider ThumbProportion property /// public static readonly DependencyProperty IsDirectionReversedProperty = DependencyProperty.Register("IsDirectionReversed", typeof(bool), typeof(Slider), new FrameworkPropertyMetadata(false)); ////// Get/Set IsDirectionReversed property /// [Bindable(true), Category("Appearance")] public bool IsDirectionReversed { get { return (bool)GetValue(IsDirectionReversedProperty); } set { SetValue(IsDirectionReversedProperty, value); } } #endregion #region Delay Property ////// The Property for the Delay property. /// public static readonly DependencyProperty DelayProperty = RepeatButton.DelayProperty.AddOwner(typeof(Slider), new FrameworkPropertyMetadata(RepeatButton.GetKeyboardDelay())); ////// Specifies the amount of time, in milliseconds, to wait before repeating begins. /// Must be non-negative. /// [Bindable(true), Category("Behavior")] public int Delay { get { return (int)GetValue(DelayProperty); } set { SetValue(DelayProperty, value); } } #endregion Delay Property #region Interval Property ////// The Property for the Interval property. /// public static readonly DependencyProperty IntervalProperty = RepeatButton.IntervalProperty.AddOwner(typeof(Slider), new FrameworkPropertyMetadata(RepeatButton.GetKeyboardSpeed())); ////// Specifies the amount of time, in milliseconds, between repeats once repeating starts. /// Must be non-negative /// [Bindable(true), Category("Behavior")] public int Interval { get { return (int)GetValue(IntervalProperty); } set { SetValue(IntervalProperty, value); } } #endregion Interval Property #region AutoToolTipPlacement Property ////// The DependencyProperty for the AutoToolTipPlacement property. /// public static readonly DependencyProperty AutoToolTipPlacementProperty = DependencyProperty.Register("AutoToolTipPlacement", typeof(AutoToolTipPlacement), typeof(Slider), new FrameworkPropertyMetadata(Primitives.AutoToolTipPlacement.None), new ValidateValueCallback(IsValidAutoToolTipPlacement)); ////// AutoToolTipPlacement property specifies the placement of the AutoToolTip /// [Bindable(true), Category("Behavior")] public Primitives.AutoToolTipPlacement AutoToolTipPlacement { get { return (Primitives.AutoToolTipPlacement)GetValue(AutoToolTipPlacementProperty); } set { SetValue(AutoToolTipPlacementProperty, value); } } private static bool IsValidAutoToolTipPlacement(object o) { AutoToolTipPlacement placement = (AutoToolTipPlacement)o; return placement == AutoToolTipPlacement.None || placement == AutoToolTipPlacement.TopLeft || placement == AutoToolTipPlacement.BottomRight; } #endregion #region AutoToolTipPrecision Property ////// The DependencyProperty for the AutoToolTipPrecision property. /// Flags: None /// Default Value: 0 /// public static readonly DependencyProperty AutoToolTipPrecisionProperty = DependencyProperty.Register("AutoToolTipPrecision", typeof(int), typeof(Slider), new FrameworkPropertyMetadata(0), new ValidateValueCallback(IsValidAutoToolTipPrecision)); ////// Get or set number of decimal digits of Slider's Value shown in AutoToolTip /// [Bindable(true), Category("Appearance")] public int AutoToolTipPrecision { get { return (int)GetValue(AutoToolTipPrecisionProperty); } set { SetValue(AutoToolTipPrecisionProperty, value); } } ////// Validates AutoToolTipPrecision value /// /// ///private static bool IsValidAutoToolTipPrecision(object o) { return (((int)o) >= 0); } #endregion /* * TickMark support * * - double TickFrequency * - bool IsSnapToTickEnabled * - Enum TickPlacement * - DoubleCollection Ticks */ #region TickMark support /// /// The DependencyProperty for the IsSnapToTickEnabled property. /// public static readonly DependencyProperty IsSnapToTickEnabledProperty = DependencyProperty.Register("IsSnapToTickEnabled", typeof(bool), typeof(Slider), new FrameworkPropertyMetadata(false)); ////// When 'true', Slider will automatically move the Thumb (and/or change current value) to the closest TickMark. /// [Bindable(true), Category("Behavior")] public bool IsSnapToTickEnabled { get { return (bool)GetValue(IsSnapToTickEnabledProperty); } set { SetValue(IsSnapToTickEnabledProperty, value); } } ////// The DependencyProperty for the TickPlacement property. /// public static readonly DependencyProperty TickPlacementProperty = DependencyProperty.Register("TickPlacement", typeof(Primitives.TickPlacement), typeof(Slider), new FrameworkPropertyMetadata(Primitives.TickPlacement.None), new ValidateValueCallback(IsValidTickPlacement)); ////// Slider uses this value to determine where to show the Ticks. /// When Ticks is not 'null', Slider will ignore 'TickFrequency', and draw only TickMarks /// that specified in Ticks collection. /// [Bindable(true), Category("Appearance")] public Primitives.TickPlacement TickPlacement { get { return (Primitives.TickPlacement)GetValue(TickPlacementProperty); } set { SetValue(TickPlacementProperty, value); } } private static bool IsValidTickPlacement(object o) { TickPlacement value = (TickPlacement)o; return value == TickPlacement.None || value == TickPlacement.TopLeft || value == TickPlacement.BottomRight || value == TickPlacement.Both; } ////// The DependencyProperty for the TickFrequency property. /// Default Value is 1.0 /// public static readonly DependencyProperty TickFrequencyProperty = DependencyProperty.Register("TickFrequency", typeof(double), typeof(Slider), new FrameworkPropertyMetadata(1.0), new ValidateValueCallback(IsValidDoubleValue)); ////// Slider uses this value to determine where to show the Ticks. /// When Ticks is not 'null', Slider will ignore 'TickFrequency', and draw only TickMarks /// that specified in Ticks collection. /// [Bindable(true), Category("Appearance")] public double TickFrequency { get { return (double)GetValue(TickFrequencyProperty); } set { SetValue(TickFrequencyProperty, value); } } // ////// The DependencyProperty for the Ticks property. /// public static readonly DependencyProperty TicksProperty = DependencyProperty.Register("Ticks", typeof(DoubleCollection), typeof(Slider), new FrameworkPropertyMetadata(new FreezableDefaultValueFactory(DoubleCollection.Empty))); ////// Slider uses this value to determine where to show the Ticks. /// When Ticks is not 'null', Slider will ignore 'TickFrequency', and draw only TickMarks /// that specified in Ticks collection. /// [Bindable(true), Category("Appearance")] public DoubleCollection Ticks { get { return (DoubleCollection)GetValue(TicksProperty); } set { SetValue(TicksProperty, value); } } #endregion TickMark support /* * Selection support * * - bool IsSelectionRangeEnabled * - double SelectionStart * - double SelectionEnd */ #region Selection supports ////// The DependencyProperty for the IsSelectionRangeEnabled property. /// public static readonly DependencyProperty IsSelectionRangeEnabledProperty = DependencyProperty.Register("IsSelectionRangeEnabled", typeof(bool), typeof(Slider), new FrameworkPropertyMetadata(false)); ////// Enable or disable selection support on Slider /// [Bindable(true), Category("Appearance")] public bool IsSelectionRangeEnabled { get { return (bool)GetValue(IsSelectionRangeEnabledProperty); } set { SetValue(IsSelectionRangeEnabledProperty, value); } } ////// The DependencyProperty for the SelectionStart property. /// public static readonly DependencyProperty SelectionStartProperty = DependencyProperty.Register("SelectionStart", typeof(double), typeof(Slider), new FrameworkPropertyMetadata(0.0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnSelectionStartChanged), new CoerceValueCallback(CoerceSelectionStart)), new ValidateValueCallback(IsValidDoubleValue)); ////// Get or set starting value of selection. /// [Bindable(true), Category("Appearance")] public double SelectionStart { get { return (double) GetValue(SelectionStartProperty); } set { SetValue(SelectionStartProperty, value); } } private static void OnSelectionStartChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Slider ctrl = (Slider)d; double oldValue = (double)e.OldValue; double newValue = (double)e.NewValue; ctrl.CoerceValue(SelectionEndProperty); ctrl.UpdateSelectionRangeElementPositionAndSize(); } private static object CoerceSelectionStart(DependencyObject d, object value) { Slider slider = (Slider)d; double selection = (double)value; double min = slider.Minimum; double max = slider.Maximum; if (selection < min) { return min; } if (selection > max) { return max; } return value; } ////// The DependencyProperty for the SelectionEnd property. /// public static readonly DependencyProperty SelectionEndProperty = DependencyProperty.Register("SelectionEnd", typeof(double), typeof(Slider), new FrameworkPropertyMetadata(0.0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnSelectionEndChanged), new CoerceValueCallback(CoerceSelectionEnd)), new ValidateValueCallback(IsValidDoubleValue)); ////// Get or set starting value of selection. /// [Bindable(true), Category("Appearance")] public double SelectionEnd { get { return (double) GetValue(SelectionEndProperty); } set { SetValue(SelectionEndProperty, value); } } private static void OnSelectionEndChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Slider ctrl = (Slider)d; ctrl.UpdateSelectionRangeElementPositionAndSize(); } private static object CoerceSelectionEnd(DependencyObject d, object value) { Slider slider = (Slider)d; double selection = (double)value; double min = slider.SelectionStart; double max = slider.Maximum; if (selection < min) { return min; } if (selection > max) { return max; } return value; } ////// Called when the value of SelectionEnd is required by the property system. /// /// The object on which the property was queried. ///The value of the SelectionEnd property on "d." private static object OnGetSelectionEnd(DependencyObject d) { return ((Slider)d).SelectionEnd; } ////// This method is invoked when the Minimum property changes. /// /// The old value of the Minimum property. /// The new value of the Minimum property. protected override void OnMinimumChanged(double oldMinimum, double newMinimum) { CoerceValue(SelectionStartProperty); } ////// This method is invoked when the Maximum property changes. /// /// The old value of the Maximum property. /// The new value of the Maximum property. protected override void OnMaximumChanged(double oldMaximum, double newMaximum) { CoerceValue(SelectionStartProperty); CoerceValue(SelectionEndProperty); } #endregion Selection supports /* * Move-To-Point support * * Property * - bool IsMoveToPointEnabled * * Event Handlers * - OnPreviewMouseLeftButtonDown * - double SelectionEnd */ #region Move-To-Point support ////// The DependencyProperty for the IsMoveToPointEnabled property. /// public static readonly DependencyProperty IsMoveToPointEnabledProperty = DependencyProperty.Register("IsMoveToPointEnabled", typeof(bool), typeof(Slider), new FrameworkPropertyMetadata(false)); ////// Enable or disable Move-To-Point support on Slider. /// Move-To-Point feature, enables Slider to immediately move the Thumb directly to the location where user /// clicked the Mouse. /// [Bindable(true), Category("Behavior")] public bool IsMoveToPointEnabled { get { return (bool)GetValue(IsMoveToPointEnabledProperty); } set { SetValue(IsMoveToPointEnabledProperty, value); } } ////// When IsMoveToPointEneabled is 'true', Slider needs to preview MouseLeftButtonDown event, in order prevent its RepeatButtons /// from handle Left-Click. /// /// protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) { if (IsMoveToPointEnabled && Track != null && Track.Thumb != null && !Track.Thumb.IsMouseOver) { // Move Thumb to the Mouse location Point pt = e.MouseDevice.GetPosition(Track); double newValue = Track.ValueFromPoint(pt); if (System.Windows.Shapes.Shape.IsDoubleFinite(newValue)) { UpdateValue(newValue); } e.Handled = true; } base.OnPreviewMouseLeftButtonDown(e); } #endregion Move-To-Point support #endregion // Properties #region Event Handlers ////// Listen to Thumb DragStarted event. /// /// /// private static void OnThumbDragStarted(object sender, DragStartedEventArgs e) { Slider slider = sender as Slider; slider.OnThumbDragStarted(e); } ////// Listen to Thumb DragDelta event. /// /// /// private static void OnThumbDragDelta(object sender, DragDeltaEventArgs e) { Slider slider = sender as Slider; slider.OnThumbDragDelta(e); } ////// Listen to Thumb DragCompleted event. /// /// /// private static void OnThumbDragCompleted(object sender, DragCompletedEventArgs e) { Slider slider = sender as Slider; slider.OnThumbDragCompleted(e); } ////// Called when user start dragging the Thumb. /// This function can be override to customize the way Slider handles Thumb movement. /// /// protected virtual void OnThumbDragStarted(DragStartedEventArgs e) { // Show AutoToolTip if needed. Thumb thumb = e.OriginalSource as Thumb; if ((thumb == null) || (this.AutoToolTipPlacement == Primitives.AutoToolTipPlacement.None)) { return; } // Save original tooltip _thumbOriginalToolTip = thumb.ToolTip; if (_autoToolTip == null) { _autoToolTip = new ToolTip(); _autoToolTip.Placement = PlacementMode.Custom; _autoToolTip.PlacementTarget = thumb; _autoToolTip.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(this.AutoToolTipCustomPlacementCallback); } thumb.ToolTip = _autoToolTip; _autoToolTip.Content = GetAutoToolTipNumber(); _autoToolTip.IsOpen = true; ((Popup)_autoToolTip.Parent).Reposition(); } ////// Called when user dragging the Thumb. /// This function can be override to customize the way Slider handles Thumb movement. /// /// protected virtual void OnThumbDragDelta(DragDeltaEventArgs e) { Thumb thumb = e.OriginalSource as Thumb; // Convert to Track's co-ordinate if (Track != null && thumb == Track.Thumb) { double newValue = Value + Track.ValueFromDistance(e.HorizontalChange, e.VerticalChange); if (System.Windows.Shapes.Shape.IsDoubleFinite(newValue)) { UpdateValue(newValue); } // Show AutoToolTip if needed if (this.AutoToolTipPlacement != Primitives.AutoToolTipPlacement.None) { if (_autoToolTip == null) { _autoToolTip = new ToolTip(); } _autoToolTip.Content = GetAutoToolTipNumber(); if (thumb.ToolTip != _autoToolTip) { thumb.ToolTip = _autoToolTip; } if (!_autoToolTip.IsOpen) { _autoToolTip.IsOpen = true; } ((Popup)_autoToolTip.Parent).Reposition(); } } } private string GetAutoToolTipNumber() { NumberFormatInfo format = (NumberFormatInfo)(NumberFormatInfo.CurrentInfo.Clone()); format.NumberDecimalDigits = this.AutoToolTipPrecision; return this.Value.ToString("N", format); } ////// Called when user stop dragging the Thumb. /// This function can be override to customize the way Slider handles Thumb movement. /// /// protected virtual void OnThumbDragCompleted(DragCompletedEventArgs e) { // Show AutoToolTip if needed. Thumb thumb = e.OriginalSource as Thumb; if ((thumb == null) || (this.AutoToolTipPlacement == Primitives.AutoToolTipPlacement.None)) { return; } if (_autoToolTip != null) { _autoToolTip.IsOpen = false; } thumb.ToolTip = _thumbOriginalToolTip; } private CustomPopupPlacement[] AutoToolTipCustomPlacementCallback(Size popupSize, Size targetSize, Point offset) { switch (this.AutoToolTipPlacement) { case Primitives.AutoToolTipPlacement.TopLeft: if (Orientation == Orientation.Horizontal) { // Place popup at top of thumb return new CustomPopupPlacement[]{new CustomPopupPlacement( new Point((targetSize.Width - popupSize.Width) * 0.5, -popupSize.Height), PopupPrimaryAxis.Horizontal) }; } else { // Place popup at left of thumb return new CustomPopupPlacement[] { new CustomPopupPlacement( new Point(-popupSize.Width, (targetSize.Height - popupSize.Height) * 0.5), PopupPrimaryAxis.Vertical) }; } case Primitives.AutoToolTipPlacement.BottomRight: if (Orientation == Orientation.Horizontal) { // Place popup at bottom of thumb return new CustomPopupPlacement[] { new CustomPopupPlacement( new Point((targetSize.Width - popupSize.Width) * 0.5, targetSize.Height) , PopupPrimaryAxis.Horizontal) }; } else { // Place popup at right of thumb return new CustomPopupPlacement[] { new CustomPopupPlacement( new Point(targetSize.Width, (targetSize.Height - popupSize.Height) * 0.5), PopupPrimaryAxis.Vertical) }; } default: return new CustomPopupPlacement[]{}; } } ////// Resize and resposition the SelectionRangeElement. /// private void UpdateSelectionRangeElementPositionAndSize() { Size trackSize = new Size(0d, 0d); Size thumbSize = new Size(0d, 0d); if (Track == null || DoubleUtil.LessThan(SelectionEnd,SelectionStart)) { return; } trackSize = Track.RenderSize; thumbSize = (Track.Thumb != null) ? Track.Thumb.RenderSize : new Size(0d, 0d); double range = Maximum - Minimum; double valueToSize; FrameworkElement rangeElement = this.SelectionRangeElement as FrameworkElement; if (rangeElement == null) { return; } if (Orientation == Orientation.Horizontal) { // Calculate part size for HorizontalSlider if (DoubleUtil.AreClose(range, 0d) || (DoubleUtil.AreClose(trackSize.Width, thumbSize.Width))) { valueToSize = 0d; } else { valueToSize = Math.Max(0.0, (trackSize.Width - thumbSize.Width) / range); } rangeElement.Width = ((SelectionEnd - SelectionStart) * valueToSize); if (IsDirectionReversed) { Canvas.SetLeft(rangeElement, (thumbSize.Width * 0.5) + Math.Max(Maximum - SelectionEnd, 0) * valueToSize); } else { Canvas.SetLeft(rangeElement, (thumbSize.Width * 0.5) + Math.Max(SelectionStart - Minimum, 0) * valueToSize); } } else { // Calculate part size for VerticalSlider if (DoubleUtil.AreClose(range, 0d) || (DoubleUtil.AreClose(trackSize.Height, thumbSize.Height))) { valueToSize = 0d; } else { valueToSize = Math.Max(0.0, (trackSize.Height - thumbSize.Height) / range); } rangeElement.Height = ((SelectionEnd - SelectionStart) * valueToSize); if (IsDirectionReversed) { Canvas.SetTop(rangeElement, (thumbSize.Height * 0.5) + Math.Max(SelectionStart - Minimum, 0) * valueToSize); } else { Canvas.SetTop(rangeElement, (thumbSize.Height * 0.5) + Math.Max(Maximum - SelectionEnd,0) * valueToSize); } } } ////// Gets or sets reference to Slider's Track element. /// internal Track Track { get { return _track; } set { _track = value; } } ////// Gets or sets reference to Slider's SelectionRange element. /// internal FrameworkElement SelectionRangeElement { get { return _selectionRangeElement; } set { _selectionRangeElement = value; } } ////// Snap the input 'value' to the closest tick. /// If input value is exactly in the middle of 2 surrounding ticks, it will be snapped to the tick that has greater value. /// /// Value that want to snap to closest Tick. ///Snapped value if IsSnapToTickEnabled is 'true'. Otherwise, returns un-snaped value. private double SnapToTick(double value) { if (IsSnapToTickEnabled) { double previous = Minimum; double next = Maximum; // This property is rarely set so let's try to avoid the GetValue // caching of the mutable default value DoubleCollection ticks = null; bool hasModifiers; if (GetValueSource(TicksProperty, null, out hasModifiers) != BaseValueSourceInternal.Default || hasModifiers) { ticks = Ticks; } // If ticks collection is available, use it. // Note that ticks may be unsorted. if ((ticks != null) && (ticks.Count > 0)) { for (int i = 0; i < ticks.Count; i++) { double tick = ticks[i]; if (DoubleUtil.AreClose(tick, value)) { return value; } if (DoubleUtil.LessThan(tick, value) && DoubleUtil.GreaterThan(tick, previous)) { previous = tick; } else if (DoubleUtil.GreaterThan(tick ,value) && DoubleUtil.LessThan(tick, next)) { next = tick; } } } else if (DoubleUtil.GreaterThan(TickFrequency, 0.0)) { previous = Minimum + (Math.Round(((value - Minimum) / TickFrequency)) * TickFrequency); next = Math.Min(Maximum, previous + TickFrequency); } // Choose the closest value between previous and next. If tie, snap to 'next'. value = DoubleUtil.GreaterThanOrClose(value, (previous + next) * 0.5) ? next : previous; } return value; } // Sets Value = SnapToTick(value+direction), unless the result of SnapToTick is Value, // then it searches for the next tick greater(if direction is positive) than value // and sets Value to that tick private void MoveToNextTick(double direction) { if (direction != 0.0) { double value = this.Value; // Find the next value by snapping double next = SnapToTick(Math.Max(this.Minimum, Math.Min(this.Maximum, value + direction))); bool greaterThan = direction > 0; //search for the next tick greater than value? // If the snapping brought us back to value, find the next tick point if (next == value && !( greaterThan && value == Maximum) // Stop if searching up if already at Max && !(!greaterThan && value == Minimum)) // Stop if searching down if already at Min { // This property is rarely set so let's try to avoid the GetValue // caching of the mutable default value DoubleCollection ticks = null; bool hasModifiers; if (GetValueSource(TicksProperty, null, out hasModifiers) != BaseValueSourceInternal.Default || hasModifiers) { ticks = Ticks; } // If ticks collection is available, use it. // Note that ticks may be unsorted. if ((ticks != null) && (ticks.Count > 0)) { for (int i = 0; i < ticks.Count; i++) { double tick = ticks[i]; // Find the smallest tick greater than value or the largest tick less than value if ((greaterThan && DoubleUtil.GreaterThan(tick, value) && (DoubleUtil.LessThan(tick, next) || next == value)) ||(!greaterThan && DoubleUtil.LessThan(tick, value) && (DoubleUtil.GreaterThan(tick, next) || next == value))) { next = tick; } } } else if (DoubleUtil.GreaterThan(TickFrequency, 0.0)) { // Find the current tick we are at double tickNumber = Math.Round((value - Minimum) / TickFrequency); if (greaterThan) tickNumber += 1.0; else tickNumber -= 1.0; next = Minimum + tickNumber * TickFrequency; } } // Update if we've found a better value if (next != value) { this.Value = next; } } } #endregion Event Handlers #region Override Functions ////// Creates AutomationPeer ( protected override AutomationPeer OnCreateAutomationPeer() { return new SliderAutomationPeer(this); } ///) /// /// This is a class handler for MouseLeftButtonDown event. /// The purpose of this handle is to move input focus to Slider when user pressed /// mouse left button on any part of slider that is not focusable. /// /// /// private static void _OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if(e.ChangedButton != MouseButton.Left) return; Slider slider = (Slider)sender; // When someone click on the Slider's part, and it's not focusable // Slider need to take the focus in order to process keyboard correctly if (!slider.IsKeyboardFocusWithin) { e.Handled = slider.Focus() || e.Handled; } } ////// Perform arrangement of slider's children /// /// protected override Size ArrangeOverride(Size finalSize) { Size size = base.ArrangeOverride(finalSize); UpdateSelectionRangeElementPositionAndSize(); return size; } ////// Update SelectionRange Length. /// /// /// protected override void OnValueChanged(double oldValue, double newValue) { base.OnValueChanged(oldValue, newValue); UpdateSelectionRangeElementPositionAndSize(); } ////// Slider locates the SelectionRangeElement when its visual tree is created /// public override void OnApplyTemplate() { base.OnApplyTemplate(); SelectionRangeElement = GetTemplateChild(SelectionRangeElementName) as FrameworkElement; Track = GetTemplateChild(TrackName) as Track; if (_autoToolTip != null) { _autoToolTip.PlacementTarget = Track != null ? Track.Thumb : null; } } #endregion Override Functions #region Virtual Functions ////// Call when Slider.IncreaseLarge command is invoked. /// protected virtual void OnIncreaseLarge() { MoveToNextTick(this.LargeChange); } ////// Call when Slider.DecreaseLarge command is invoked. /// protected virtual void OnDecreaseLarge() { MoveToNextTick(-this.LargeChange); } ////// Call when Slider.IncreaseSmall command is invoked. /// protected virtual void OnIncreaseSmall() { MoveToNextTick(this.SmallChange); } ////// Call when Slider.DecreaseSmall command is invoked. /// protected virtual void OnDecreaseSmall() { MoveToNextTick(-this.SmallChange); } ////// Call when Slider.MaximizeValue command is invoked. /// protected virtual void OnMaximizeValue() { this.Value = this.Maximum; } ////// Call when Slider.MinimizeValue command is invoked. /// protected virtual void OnMinimizeValue() { this.Value = this.Minimum; } #endregion Virtual Functions #region Helper Functions ////// Helper function for value update. /// This function will also snap the value to tick, if IsSnapToTickEnabled is true. /// /// private void UpdateValue(double value) { Double snappedValue = SnapToTick(value); if (snappedValue != Value) { Value = Math.Max(this.Minimum, Math.Min(this.Maximum, snappedValue)); } } ////// Validate input value in Slider (LargeChange, SmallChange, SelectionStart, SelectionEnd, and TickFrequency). /// /// ///Returns False if value is NaN or NegativeInfinity or PositiveInfinity. Otherwise, returns True. private static bool IsValidDoubleValue(object value) { double d = (double)value; return !(DoubleUtil.IsNaN(d) || double.IsInfinity(d)); } #endregion Helper Functions #region Private Fields private const string TrackName = "PART_Track"; private const string SelectionRangeElementName = "PART_SelectionRange"; // Slider required parts private FrameworkElement _selectionRangeElement; private Track _track; private ToolTip _autoToolTip = null; private object _thumbOriginalToolTip = null; #endregion Private Fields #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
- AttributeInfo.cs
- connectionpool.cs
- DetailsViewInsertEventArgs.cs
- BitmapEffectrendercontext.cs
- DeferrableContentConverter.cs
- mactripleDES.cs
- JournalEntryListConverter.cs
- FixedSOMSemanticBox.cs
- TypeUtil.cs
- TextEffect.cs
- Formatter.cs
- ViewCellSlot.cs
- DataGridColumnHeaderItemAutomationPeer.cs
- QilScopedVisitor.cs
- TimeZoneInfo.cs
- GridViewDeleteEventArgs.cs
- HashMembershipCondition.cs
- CodeDomSerializationProvider.cs
- _FtpDataStream.cs
- TextComposition.cs
- ClickablePoint.cs
- RegexBoyerMoore.cs
- RestHandlerFactory.cs
- DomainConstraint.cs
- WebPartEditVerb.cs
- XhtmlBasicSelectionListAdapter.cs
- CloseCollectionAsyncResult.cs
- ViewBox.cs
- AccessDataSourceWizardForm.cs
- Attributes.cs
- FixedDSBuilder.cs
- ToolStripContentPanel.cs
- MLangCodePageEncoding.cs
- XPathDocumentIterator.cs
- EmptyTextWriter.cs
- RegexParser.cs
- AnonymousIdentificationSection.cs
- VirtualPath.cs
- LocalValueEnumerator.cs
- ObjectListGeneralPage.cs
- isolationinterop.cs
- RuntimeResourceSet.cs
- ClientUtils.cs
- SystemWebSectionGroup.cs
- ChangeTracker.cs
- CheckBoxAutomationPeer.cs
- TextDecorationCollection.cs
- ErrorRuntimeConfig.cs
- MemberInitExpression.cs
- SQLInt32.cs
- ObjectStateManagerMetadata.cs
- PostBackOptions.cs
- FontSourceCollection.cs
- StateMachineSubscriptionManager.cs
- HierarchicalDataTemplate.cs
- DbProviderFactory.cs
- ProfileEventArgs.cs
- AuthStoreRoleProvider.cs
- Page.cs
- DataColumn.cs
- GeometryCombineModeValidation.cs
- ConditionCollection.cs
- EntityViewGenerationAttribute.cs
- Convert.cs
- UserControlCodeDomTreeGenerator.cs
- PropertyState.cs
- WSSecureConversationFeb2005.cs
- AsyncContentLoadedEventArgs.cs
- StrongNameMembershipCondition.cs
- CompilerInfo.cs
- IRCollection.cs
- DecoderExceptionFallback.cs
- DataServiceClientException.cs
- PageCache.cs
- OleDbReferenceCollection.cs
- VarRemapper.cs
- CodeCommentStatement.cs
- HandlerWithFactory.cs
- FileDataSourceCache.cs
- InputReport.cs
- IndexerReference.cs
- HostSecurityManager.cs
- UnsafeNetInfoNativeMethods.cs
- RowToParametersTransformer.cs
- BrowserCapabilitiesFactory.cs
- ZoneLinkButton.cs
- WindowsTreeView.cs
- StaticResourceExtension.cs
- SortExpressionBuilder.cs
- WebScriptServiceHost.cs
- BinaryFormatter.cs
- CompareValidator.cs
- UIAgentInitializationException.cs
- AppSettingsExpressionBuilder.cs
- EventLogEntry.cs
- WindowShowOrOpenTracker.cs
- ColumnResult.cs
- DropShadowBitmapEffect.cs
- HttpConfigurationSystem.cs
- ThreadStartException.cs