@ -8,6 +8,15 @@ namespace Microsoft.Windows.Controls
{
public class NumericUpDown : UpDownBase
{
#region Members
/// <summary>
/// Flags if the Text and Value properties are in the process of being sync'd
/// </summary>
private bool _ isSyncingTextAndValueProperties ;
#endregion //Members
#region Properties
#region Minimum
@ -88,6 +97,64 @@ namespace Microsoft.Windows.Controls
#endregion //SelectAllOnGotFocus
#region Value
public static readonly DependencyProperty ValueProperty = DependencyProperty . Register ( "Value" , typeof ( decimal? ) , typeof ( NumericUpDown ) , new FrameworkPropertyMetadata ( null , FrameworkPropertyMetadataOptions . BindsTwoWayByDefault , OnValueChanged , OnCoerceValue ) ) ;
public decimal? Value
{
get { return ( decimal? ) GetValue ( ValueProperty ) ; }
set { SetValue ( ValueProperty , value ) ; }
}
private static object OnCoerceValue ( DependencyObject o , object value )
{
NumericUpDown numericUpDown = o as NumericUpDown ;
if ( numericUpDown ! = null )
return numericUpDown . OnCoerceValue ( ( decimal? ) value ) ;
else
return value ;
}
protected virtual decimal? OnCoerceValue ( decimal? value )
{
if ( value = = null ) return value ;
decimal val = value . Value ;
if ( val < Minimum )
{
return Minimum ;
}
else if ( val > Maximum )
{
return Maximum ;
}
else
{
return value ;
}
}
private static void OnValueChanged ( DependencyObject o , DependencyPropertyChangedEventArgs e )
{
NumericUpDown numericUpDown = o as NumericUpDown ;
if ( numericUpDown ! = null )
numericUpDown . OnValueChanged ( ( decimal? ) e . OldValue , ( decimal? ) e . NewValue ) ;
}
protected virtual void OnValueChanged ( decimal? oldValue , decimal? newValue )
{
SetValidSpinDirection ( ) ;
SyncTextAndValueProperties ( NumericUpDown . ValueProperty , newValue ) ;
RoutedPropertyChangedEventArgs < decimal? > args = new RoutedPropertyChangedEventArgs < decimal? > ( oldValue , newValue ) ;
args . RoutedEvent = NumericUpDown . ValueChangedEvent ;
RaiseEvent ( args ) ;
}
#endregion //Value
#endregion
#region Constructors
@ -95,14 +162,20 @@ namespace Microsoft.Windows.Controls
static NumericUpDown ( )
{
DefaultStyleKeyProperty . OverrideMetadata ( typeof ( NumericUpDown ) , new FrameworkPropertyMetadata ( typeof ( NumericUpDown ) ) ) ;
ValueTypeProperty . OverrideMetadata ( typeof ( NumericUpDown ) , new FrameworkPropertyMetadata ( typeof ( decimal ) ) ) ;
ValueProperty . OverrideMetadata ( typeof ( NumericUpDown ) , new FrameworkPropertyMetadata ( default ( decimal ) ) ) ;
}
#endregion //Constructors
#region Base Class Overrides
protected override void OnAccessKey ( AccessKeyEventArgs e )
{
if ( TextBox ! = null )
TextBox . Focus ( ) ;
base . OnAccessKey ( e ) ;
}
public override void OnApplyTemplate ( )
{
base . OnApplyTemplate ( ) ;
@ -116,93 +189,38 @@ namespace Microsoft.Windows.Controls
}
}
protected override void OnAccessKey ( AccessKeyEventArgs e )
{
if ( TextBox ! = null )
TextBox . Focus ( ) ;
base . OnAccessKey ( e ) ;
}
protected override void OnGotFocus ( RoutedEventArgs e )
{
if ( TextBox ! = null )
TextBox . Focus ( ) ;
}
protected override void OnValueChanged ( object oldValue , object newValue )
protected override void OnIncrement ( )
{
SetValidSpinDirection ( ) ;
base . OnValueChanged ( oldValue , newValue ) ;
if ( Value . HasValue )
Value + = Increment ;
}
protected override object OnCoerceValue ( object value )
protected override void OnDecrement ( )
{
if ( value = = null ) return value ;
decimal val = Convert . ToDecimal ( value ) ;
if ( val < Minimum )
{
return Minimum ;
}
else if ( val > Maximum )
{
return Maximum ;
}
else
{
return value ;
}
if ( Value . HasValue )
Value - = Increment ;
}
protected override object ConvertTextToValue ( string text )
protected override void OnPreviewKeyDown ( KeyEventArgs e )
{
object result = null ;
NumberFormatInfo info = NumberFormatInfo . GetInstance ( CultureInfo . CurrentCulture ) ;
try
{
result = FormatString . Contains ( "P" ) ? ParsePercent ( text , ValueType , info ) : ParseDataValue ( text , ValueType , info ) ;
}
catch
{
TextBox . Text = Text = ConvertValueToText ( Value ) ;
return Value ;
}
base . OnPreviewKeyDown ( e ) ;
return result ;
}
protected override string ConvertValueToText ( object value )
{
//TODO: create GetTextFromValue methods for each data type;
if ( value is double )
if ( e . Key = = Key . Enter )
{
double d = ( double ) value ;
if ( Double . IsNaN ( d ) )
return "NaN" ;
else if ( Double . IsPositiveInfinity ( d ) | | Double . MaxValue = = d )
return "Infinity" ;
else if ( Double . IsNegativeInfinity ( d ) | | Double . MinValue = = d )
return "Negative-Infinity" ;
if ( IsEditable )
SyncTextAndValueProperties ( InputBase . TextProperty , TextBox . Text ) ;
}
return ( Convert . ToDecimal ( Value ) ) . ToString ( FormatString , CultureInfo . CurrentCulture ) ;
}
protected override void OnIncrement ( )
{
decimal newValue = ( Convert . ToDecimal ( Value ) + Increment ) ;
Value = ValueType ! = typeof ( Decimal ) ? Convert . ChangeType ( newValue , ValueType ) : newValue ;
}
protected override void OnDecrement ( )
protected override void OnTextChanged ( string previousValue , string currentValue )
{
decimal newValue = ( Convert . ToDecimal ( Value ) - Increment ) ;
Value = ValueType ! = typeof ( Decimal ) ? Convert . ChangeType ( newValue , ValueType ) : newValue ;
SyncTextAndValueProperties ( InputBase . TextProperty , currentValue ) ;
}
#endregion //Base Class Overrides
@ -225,75 +243,67 @@ namespace Microsoft.Windows.Controls
#endregion //Event Handlers
#region Method s
#region Event s
/// <summary>
/// Sets the valid spin direction based on current value, minimum and maximum.
/// </summary>
private void SetValidSpinDirection ( )
public static readonly RoutedEvent ValueChangedEvent = EventManager . RegisterRoutedEvent ( "ValueChanged" , RoutingStrategy . Bubble , typeof ( RoutedPropertyChangedEventHandler < decimal? > ) , typeof ( NumericUpDown ) ) ;
public event RoutedPropertyChangedEventHandler < decimal? > ValueChanged
{
ValidSpinDirections validDirections = ValidSpinDirections . None ;
add { AddHandler ( ValueChangedEvent , value ) ; }
remove { RemoveHandler ( ValueChangedEvent , value ) ; }
}
if ( Convert . ToDecimal ( Value ) < Maximum )
{
validDirections = validDirections | ValidSpinDirections . Increase ;
}
#endregion //Events
if ( Convert . ToDecimal ( Value ) > Minimum )
{
validDirections = validDirections | ValidSpinDirections . Decrease ;
}
#region Methods
if ( Spinner ! = null )
{
Spinner . ValidSpinDirection = validDirections ;
}
}
private decimal? ConvertTextToValue ( string text )
{
decimal? result = null ;
#region Parsing
if ( String . IsNullOrEmpty ( text ) )
return result ;
NumberFormatInfo info = NumberFormatInfo . GetInstance ( CultureInfo . CurrentCulture ) ;
private static object ParseDataValue ( string text , Type dataType , NumberFormatInfo info )
{
try
{
if ( typeof ( decimal ) = = dataType )
{
return ParseDecimal ( text , info ) ;
}
else if ( typeof ( double ) = = dataType )
{
return ParseDouble ( text , info ) ;
}
else if ( typeof ( float ) = = dataType )
{
return ParseFloat ( text , info ) ;
}
else if ( typeof ( byte ) = = dataType | | typeof ( sbyte ) = = dataType | |
typeof ( short ) = = dataType | | typeof ( ushort ) = = dataType | | typeof ( Int16 ) = = dataType | |
typeof ( int ) = = dataType | | typeof ( uint ) = = dataType | | typeof ( Int32 ) = = dataType | |
typeof ( long ) = = dataType | | typeof ( ulong ) = = dataType | | typeof ( Int64 ) = = dataType )
{
return ParseWholeNumber ( text , dataType , info ) ;
}
else
{
throw new ArgumentException ( "Type not supported" ) ;
}
result = FormatString . Contains ( "P" ) ? ParsePercent ( text , info ) : ParseDecimal ( text , info ) ;
}
catch ( Exception ex )
catch
{
throw ;
Text = ConvertValueToText ( Value ) ;
return Value ;
}
return result ;
}
private static double ParseDouble ( string text , NumberFormatInfo info )
private string ConvertValueToText ( object value )
{
return double . Parse ( text , NumberStyles . Any , info ) ;
if ( ! Value . HasValue )
return String . Empty ;
return Value . Value . ToString ( FormatString , CultureInfo . CurrentCulture ) ;
}
private static float ParseFloat ( string text , NumberFormatInfo info )
private void SyncTextAndValueProperties ( DependencyProperty p , object newValue )
{
return float . Parse ( text , NumberStyles . Any , info ) ;
//prevents recursive syncing properties
if ( _ isSyncingTextAndValueProperties )
return ;
_ isSyncingTextAndValueProperties = true ;
//this only occures when the user typed in the value
if ( InputBase . TextProperty = = p )
{
string text = newValue = = null ? String . Empty : newValue . ToString ( ) ;
SetValue ( NumericUpDown . ValueProperty , ConvertTextToValue ( text ) ) ;
}
SetValue ( InputBase . TextProperty , ConvertValueToText ( newValue ) ) ;
_ isSyncingTextAndValueProperties = false ;
}
private static decimal ParseDecimal ( string text , NumberFormatInfo info )
@ -301,23 +311,38 @@ namespace Microsoft.Windows.Controls
return decimal . Parse ( text , NumberStyles . Any , info ) ;
}
private static object ParseWholeNumber ( string text , Type dataType , NumberFormatInfo info )
{
decimal result = decimal . Parse ( text , NumberStyles . Any , info ) ;
return Convert . ChangeType ( result , dataType , info ) ;
}
private static object ParsePercent ( string text , Type dataType , NumberFormatInfo info )
private static decimal ParsePercent ( string text , NumberFormatInfo info )
{
text = text . Replace ( info . PercentSymbol , null ) ;
decimal result = decimal . Parse ( text , NumberStyles . Any , info ) ;
result = result / 1 0 0 ;
return Convert . ChangeType ( result , dataType , info ) ;
return result ;
}
#endregion
/// <summary>
/// Sets the valid spin direction based on current value, minimum and maximum.
/// </summary>
private void SetValidSpinDirection ( )
{
ValidSpinDirections validDirections = ValidSpinDirections . None ;
if ( Convert . ToDecimal ( Value ) < Maximum )
{
validDirections = validDirections | ValidSpinDirections . Increase ;
}
if ( Convert . ToDecimal ( Value ) > Minimum )
{
validDirections = validDirections | ValidSpinDirections . Decrease ;
}
if ( Spinner ! = null )
{
Spinner . ValidSpinDirection = validDirections ;
}
}
#endregion //Methods
}