Browse Source
<toolkit:NumericUpDown Value="25" FormatString="C2" Increment=".5" Maximum="30" Minimum="20" />pull/1645/head
9 changed files with 575 additions and 52 deletions
@ -1,6 +1,6 @@ |
|||
using System; |
|||
|
|||
namespace Microsoft.Windows.Controls.Primitives |
|||
namespace Microsoft.Windows.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Represents spin directions that could be initiated by the end-user.
|
|||
@ -1,7 +1,7 @@ |
|||
using System; |
|||
using System.Windows; |
|||
|
|||
namespace Microsoft.Windows.Controls.Primitives |
|||
namespace Microsoft.Windows.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Provides data for the Spinner.Spin event.
|
|||
@ -1,6 +1,6 @@ |
|||
using System; |
|||
|
|||
namespace Microsoft.Windows.Controls.Primitives |
|||
namespace Microsoft.Windows.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Represents spin directions that are valid.
|
|||
@ -0,0 +1,163 @@ |
|||
using System; |
|||
using System.Windows; |
|||
using System.Globalization; |
|||
|
|||
namespace Microsoft.Windows.Controls |
|||
{ |
|||
|
|||
public class NumericUpDown : UpDownBase<double> |
|||
{ |
|||
#region Properties
|
|||
|
|||
#region Minimum
|
|||
|
|||
public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(0d, OnMinimumPropertyChanged)); |
|||
public double Minimum |
|||
{ |
|||
get { return (double)GetValue(MinimumProperty); } |
|||
set { SetValue(MinimumProperty, value); } |
|||
} |
|||
|
|||
private static void OnMinimumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|||
{ |
|||
} |
|||
|
|||
protected virtual void OnMinimumChanged(double oldValue, double newValue) |
|||
{ |
|||
} |
|||
#endregion Minimum
|
|||
|
|||
#region Maximum
|
|||
|
|||
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(100d, OnMaximumPropertyChanged)); |
|||
public double Maximum |
|||
{ |
|||
get { return (double)GetValue(MaximumProperty); } |
|||
set { SetValue(MaximumProperty, value); } |
|||
} |
|||
|
|||
private static void OnMaximumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|||
{ |
|||
} |
|||
|
|||
protected virtual void OnMaximumChanged(double oldValue, double newValue) |
|||
{ |
|||
} |
|||
#endregion Maximum
|
|||
|
|||
#region Increment
|
|||
|
|||
public static readonly DependencyProperty IncrementProperty = DependencyProperty.Register("Increment", typeof(double), typeof(NumericUpDown), new PropertyMetadata(1d, OnIncrementPropertyChanged)); |
|||
public double Increment |
|||
{ |
|||
get { return (double)GetValue(IncrementProperty); } |
|||
set { SetValue(IncrementProperty, value); } |
|||
} |
|||
|
|||
private static void OnIncrementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|||
{ |
|||
} |
|||
|
|||
protected virtual void OnIncrementChanged(double oldValue, double newValue) |
|||
{ |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region FormatString
|
|||
|
|||
public static readonly DependencyProperty StringFormatProperty = DependencyProperty.Register("FormatString", typeof(string), typeof(NumericUpDown), new PropertyMetadata("F0", OnStringFormatPropertyPropertyChanged)); |
|||
public string FormatString |
|||
{ |
|||
get { return (string)GetValue(StringFormatProperty); } |
|||
set { SetValue(StringFormatProperty, value); } |
|||
} |
|||
|
|||
private static void OnStringFormatPropertyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|||
{ |
|||
NumericUpDown nud = d as NumericUpDown; |
|||
nud.OnStringFormatChanged(e.OldValue.ToString(), e.NewValue.ToString()); |
|||
} |
|||
|
|||
protected virtual void OnStringFormatChanged(string oldValue, string newValue) |
|||
{ |
|||
Text = FormatValue(); |
|||
} |
|||
|
|||
#endregion //FormatString
|
|||
|
|||
#endregion
|
|||
|
|||
#region Constructors
|
|||
|
|||
public NumericUpDown() |
|||
: base() |
|||
{ |
|||
DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(typeof(NumericUpDown))); |
|||
} |
|||
|
|||
#endregion //Constructors
|
|||
|
|||
#region Base Class Overrides
|
|||
|
|||
public override void OnApplyTemplate() |
|||
{ |
|||
base.OnApplyTemplate(); |
|||
SetValidSpinDirection(); |
|||
} |
|||
|
|||
protected override void OnValueChanged(RoutedPropertyChangedEventArgs<double> e) |
|||
{ |
|||
SetValidSpinDirection(); |
|||
} |
|||
|
|||
protected override double ParseValue(string text) |
|||
{ |
|||
return double.Parse(text, NumberStyles.Any, CultureInfo.CurrentCulture); |
|||
} |
|||
|
|||
protected internal override string FormatValue() |
|||
{ |
|||
return Value.ToString(FormatString, CultureInfo.CurrentCulture); |
|||
} |
|||
|
|||
protected override void OnIncrement() |
|||
{ |
|||
Value = (double)((decimal)Value + (decimal)Increment); |
|||
} |
|||
|
|||
protected override void OnDecrement() |
|||
{ |
|||
Value = (double)((decimal)Value - (decimal)Increment); |
|||
} |
|||
|
|||
#endregion //Base Class Overrides
|
|||
|
|||
#region Methods
|
|||
|
|||
/// <summary>
|
|||
/// Sets the valid spin direction based on current value, minimum and maximum.
|
|||
/// </summary>
|
|||
private void SetValidSpinDirection() |
|||
{ |
|||
ValidSpinDirections validDirections = ValidSpinDirections.None; |
|||
|
|||
if (Value < Maximum) |
|||
{ |
|||
validDirections = validDirections | ValidSpinDirections.Increase; |
|||
} |
|||
|
|||
if (Value > Minimum) |
|||
{ |
|||
validDirections = validDirections | ValidSpinDirections.Decrease; |
|||
} |
|||
|
|||
if (Spinner != null) |
|||
{ |
|||
Spinner.ValidSpinDirection = validDirections; |
|||
} |
|||
} |
|||
|
|||
#endregion //Methods
|
|||
} |
|||
} |
|||
@ -0,0 +1,331 @@ |
|||
using System; |
|||
using System.Windows.Input; |
|||
using System.Windows.Controls; |
|||
using System.Windows; |
|||
|
|||
namespace Microsoft.Windows.Controls |
|||
{ |
|||
public abstract class UpDownBase<T> : Control |
|||
{ |
|||
#region Members
|
|||
|
|||
/// <summary>
|
|||
/// Name constant for Text template part.
|
|||
/// </summary>
|
|||
internal const string ElementTextName = "Text"; |
|||
|
|||
/// <summary>
|
|||
/// Name constant for Spinner template part.
|
|||
/// </summary>
|
|||
internal const string ElementSpinnerName = "Spinner"; |
|||
|
|||
/// <summary>
|
|||
/// Flags if the Text and Value properties are in the process of being sync'd
|
|||
/// </summary>
|
|||
bool _isSyncingTextAndValueProperties; |
|||
|
|||
#endregion //Members
|
|||
|
|||
#region Properties
|
|||
|
|||
#region Value
|
|||
|
|||
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(T), typeof(UpDownBase<T>), new FrameworkPropertyMetadata(default(T), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValuePropertyChanged)); |
|||
public virtual T Value |
|||
{ |
|||
get { return (T)GetValue(ValueProperty); } |
|||
set { SetValue(ValueProperty, value); } |
|||
} |
|||
|
|||
private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|||
{ |
|||
UpDownBase<T> udb = (UpDownBase<T>)d; |
|||
T oldValue = (T)e.OldValue; |
|||
T newValue = (T)e.NewValue; |
|||
|
|||
udb.SyncTextAndValueProperties(e.Property, e.NewValue); |
|||
|
|||
RoutedPropertyChangedEventArgs<T> changedArgs = new RoutedPropertyChangedEventArgs<T>(oldValue, newValue); |
|||
udb.OnValueChanged(changedArgs); |
|||
} |
|||
|
|||
protected virtual void OnValueChanged(RoutedPropertyChangedEventArgs<T> e) |
|||
{ |
|||
if (ValueChanged != null) |
|||
ValueChanged(this, e); |
|||
} |
|||
|
|||
#endregion //Value
|
|||
|
|||
#region Text
|
|||
|
|||
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(UpDownBase<T>), new FrameworkPropertyMetadata("0", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnTextPropertyChanged)); |
|||
public string Text |
|||
{ |
|||
get { return (string)GetValue(TextProperty); } |
|||
set { SetValue(TextProperty, value); } |
|||
} |
|||
|
|||
private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|||
{ |
|||
NumericUpDown nud = (NumericUpDown)d; |
|||
nud.SyncTextAndValueProperties(e.Property, e.NewValue); |
|||
} |
|||
|
|||
protected virtual void OnTextChanged(string oldValue, string newValue) |
|||
{ |
|||
|
|||
} |
|||
|
|||
#endregion //Text
|
|||
|
|||
#region IsEditable
|
|||
|
|||
public static readonly DependencyProperty IsEditableProperty = DependencyProperty.Register("IsEditable", typeof(bool), typeof(UpDownBase<T>), new PropertyMetadata(true, OnIsEditablePropertyChanged)); |
|||
public bool IsEditable |
|||
{ |
|||
get { return (bool)GetValue(IsEditableProperty); } |
|||
set { SetValue(IsEditableProperty, value); } |
|||
} |
|||
|
|||
private static void OnIsEditablePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|||
{ |
|||
UpDownBase<T> source = d as UpDownBase<T>; |
|||
source.OnIsEditableChanged((bool)e.OldValue, (bool)e.NewValue); |
|||
} |
|||
|
|||
protected virtual void OnIsEditableChanged(bool oldValue, bool newValue) |
|||
{ |
|||
if (TextBox != null) |
|||
TextBox.IsReadOnly = !IsEditable; |
|||
} |
|||
|
|||
#endregion //IsEditable
|
|||
|
|||
internal TextBox TextBox { get; private set; } |
|||
|
|||
private Spinner _spinner; |
|||
internal Spinner Spinner |
|||
{ |
|||
get { return _spinner; } |
|||
private set |
|||
{ |
|||
if (_spinner != null) |
|||
{ |
|||
_spinner.Spin -= OnSpinnerSpin; |
|||
} |
|||
|
|||
_spinner = value; |
|||
|
|||
if (_spinner != null) |
|||
{ |
|||
_spinner.Spin += OnSpinnerSpin; |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endregion //Properties
|
|||
|
|||
#region Constructors
|
|||
|
|||
protected UpDownBase() |
|||
{ |
|||
} |
|||
|
|||
#endregion //Constructors
|
|||
|
|||
#region Base Class Overrides
|
|||
|
|||
public override void OnApplyTemplate() |
|||
{ |
|||
base.OnApplyTemplate(); |
|||
|
|||
TextBox = GetTemplateChild(ElementTextName) as TextBox; |
|||
Spinner = GetTemplateChild(ElementSpinnerName) as Spinner; |
|||
|
|||
if (TextBox != null) |
|||
TextBox.IsReadOnly = !IsEditable; |
|||
} |
|||
|
|||
protected override void OnPreviewKeyDown(KeyEventArgs e) |
|||
{ |
|||
switch (e.Key) |
|||
{ |
|||
case Key.Up: |
|||
{ |
|||
DoIncrement(); |
|||
e.Handled = true; |
|||
break; |
|||
} |
|||
case Key.Down: |
|||
{ |
|||
DoDecrement(); |
|||
e.Handled = true; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected override void OnMouseWheel(MouseWheelEventArgs e) |
|||
{ |
|||
base.OnMouseWheel(e); |
|||
|
|||
if (!e.Handled) |
|||
{ |
|||
if (e.Delta < 0) |
|||
{ |
|||
DoDecrement(); |
|||
} |
|||
else if (0 < e.Delta) |
|||
{ |
|||
DoIncrement(); |
|||
} |
|||
|
|||
e.Handled = true; |
|||
} |
|||
} |
|||
|
|||
#endregion //Base Class Overrides
|
|||
|
|||
#region Methods
|
|||
|
|||
#region Abstract
|
|||
|
|||
/// <summary>
|
|||
/// Called by ApplyValue to parse user input.
|
|||
/// </summary>
|
|||
/// <param name="text">User input.</param>
|
|||
/// <returns>Value parsed from user input.</returns>
|
|||
protected abstract T ParseValue(string text); |
|||
|
|||
/// <summary>
|
|||
/// Renders the value property into the textbox text.
|
|||
/// </summary>
|
|||
/// <returns>Formatted Value.</returns>
|
|||
protected internal abstract string FormatValue(); |
|||
|
|||
/// <summary>
|
|||
/// Called by OnSpin when the spin direction is SpinDirection.Increase.
|
|||
/// </summary>
|
|||
protected abstract void OnIncrement(); |
|||
|
|||
/// <summary>
|
|||
/// Called by OnSpin when the spin direction is SpinDirection.Descrease.
|
|||
/// </summary>
|
|||
protected abstract void OnDecrement(); |
|||
|
|||
#endregion //Abstract
|
|||
|
|||
#region Protected
|
|||
|
|||
/// <summary>
|
|||
/// GetValue override to return Value property as object type.
|
|||
/// </summary>
|
|||
/// <returns>The Value property as object type.</returns>
|
|||
protected object GetValue() |
|||
{ |
|||
return Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SetValue override to set value to Value property.
|
|||
/// </summary>
|
|||
/// <param name="value">New value.</param>
|
|||
protected void SetValue(object value) |
|||
{ |
|||
Value = (T)value; |
|||
} |
|||
|
|||
#endregion //Protected
|
|||
|
|||
#region Private
|
|||
|
|||
/// <summary>
|
|||
/// Performs an increment if conditions allow it.
|
|||
/// </summary>
|
|||
private void DoDecrement() |
|||
{ |
|||
if (Spinner == null || (Spinner.ValidSpinDirection & ValidSpinDirections.Decrease) == ValidSpinDirections.Decrease) |
|||
{ |
|||
OnDecrement(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Performs a decrement if conditions allow it.
|
|||
/// </summary>
|
|||
private void DoIncrement() |
|||
{ |
|||
if (Spinner == null || (Spinner.ValidSpinDirection & ValidSpinDirections.Increase) == ValidSpinDirections.Increase) |
|||
{ |
|||
OnIncrement(); |
|||
} |
|||
} |
|||
|
|||
private void SyncTextAndValueProperties(DependencyProperty p, object newValue) |
|||
{ |
|||
//prevents recursive syncing properties
|
|||
if (_isSyncingTextAndValueProperties) |
|||
return; |
|||
|
|||
_isSyncingTextAndValueProperties = true; |
|||
|
|||
if (NumericUpDown.ValueProperty == p) |
|||
{ |
|||
SetValue(NumericUpDown.TextProperty, FormatValue()); |
|||
} |
|||
else if (NumericUpDown.TextProperty == p) |
|||
{ |
|||
SetValue(NumericUpDown.ValueProperty, ParseValue(newValue.ToString())); |
|||
} |
|||
|
|||
_isSyncingTextAndValueProperties = false; |
|||
} |
|||
|
|||
#endregion //Private
|
|||
|
|||
#region Virtual
|
|||
|
|||
/// <summary>
|
|||
/// Occurs when the spinner spins.
|
|||
/// </summary>
|
|||
/// <param name="e">Event args.</param>
|
|||
protected virtual void OnSpin(SpinEventArgs e) |
|||
{ |
|||
if (e == null) |
|||
throw new ArgumentNullException("e"); |
|||
|
|||
if (e.Direction == SpinDirection.Increase) |
|||
DoIncrement(); |
|||
else |
|||
DoDecrement(); |
|||
} |
|||
|
|||
#endregion //Virtual
|
|||
|
|||
#endregion //Methods
|
|||
|
|||
#region Event Handlers
|
|||
|
|||
/// <summary>
|
|||
/// Event handler for Spinner template part's Spin event.
|
|||
/// </summary>
|
|||
/// <param name="sender">The Spinner template part.</param>
|
|||
/// <param name="e">Event args.</param>
|
|||
private void OnSpinnerSpin(object sender, SpinEventArgs e) |
|||
{ |
|||
OnSpin(e); |
|||
} |
|||
|
|||
#endregion //Event Handlers
|
|||
|
|||
#region Events
|
|||
|
|||
/// <summary>
|
|||
/// Occurs when Value property has changed.
|
|||
/// </summary>
|
|||
public event RoutedPropertyChangedEventHandler<T> ValueChanged; |
|||
|
|||
#endregion //Events
|
|||
} |
|||
} |
|||
Loading…
Reference in new issue