Browse Source
<toolkit:NumericUpDown Value="25" FormatString="C2" Increment=".5" Maximum="30" Minimum="20" />import/raw
9 changed files with 575 additions and 52 deletions
@ -1,6 +1,6 @@ |
|||||
using System; |
using System; |
||||
|
|
||||
namespace Microsoft.Windows.Controls.Primitives |
namespace Microsoft.Windows.Controls |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Represents spin directions that could be initiated by the end-user.
|
/// Represents spin directions that could be initiated by the end-user.
|
||||
@ -1,7 +1,7 @@ |
|||||
using System; |
using System; |
||||
using System.Windows; |
using System.Windows; |
||||
|
|
||||
namespace Microsoft.Windows.Controls.Primitives |
namespace Microsoft.Windows.Controls |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Provides data for the Spinner.Spin event.
|
/// Provides data for the Spinner.Spin event.
|
||||
@ -1,6 +1,6 @@ |
|||||
using System; |
using System; |
||||
|
|
||||
namespace Microsoft.Windows.Controls.Primitives |
namespace Microsoft.Windows.Controls |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Represents spin directions that are valid.
|
/// 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