From faa782542349acef5cb5ed86ec1f85c9061b4c07 Mon Sep 17 00:00:00 2001 From: brianlagunas_cp Date: Mon, 28 Mar 2011 22:42:17 +0000 Subject: [PATCH] Major overhaul of NumericUpDown, DateTimeUpDown, and MaskedTextBox in order to support null values. Need extensive testing because I am sure I broke something. --- .../Core/Primitives/InputBase.cs | 146 +-------- .../Core/Primitives/UpDownBase.cs | 12 +- .../Implementation/DateTimeUpDown.cs | 134 +++++--- .../Implementation/MaskedTextBox.cs | 189 +++++++++--- .../Implementation/NumericUpDown.cs | 285 ++++++++++-------- .../Implementation/PropertyGrid.cs | 4 +- .../WPFToolkit.Extended.csproj | 1 - 7 files changed, 409 insertions(+), 362 deletions(-) diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/InputBase.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/InputBase.cs index 58c656be..d802c662 100644 --- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/InputBase.cs +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/InputBase.cs @@ -6,20 +6,8 @@ namespace Microsoft.Windows.Controls.Primitives { public abstract class InputBase : Control { - #region Members - - /// - /// Flags if the Text and Value properties are in the process of being sync'd - /// - private bool _isSyncingTextAndValueProperties; - private bool _isInitialized; - - #endregion //Members - #region Properties - public virtual object PreviousValue { get; internal set; } - #region IsEditable public static readonly DependencyProperty IsEditableProperty = DependencyProperty.Register("IsEditable", typeof(bool), typeof(InputBase), new PropertyMetadata(true)); @@ -33,19 +21,17 @@ namespace Microsoft.Windows.Controls.Primitives #region Text - public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(InputBase), new FrameworkPropertyMetadata(default(String), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnTextPropertyChanged)); + public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(InputBase), new PropertyMetadata(default(String), OnTextPropertyChanged)); public string Text { - get { return (string)this.GetValue(TextProperty); } - set { this.SetValue(TextProperty, value); } + get { return (string)GetValue(TextProperty); } + set { SetValue(TextProperty, value); } } private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { InputBase input = (InputBase)d; input.OnTextChanged((string)e.OldValue, (string)e.NewValue); - if (input._isInitialized) - input.SyncTextAndValueProperties(e.Property, e.NewValue); } protected virtual void OnTextChanged(string previousValue, string currentValue) @@ -55,132 +41,6 @@ namespace Microsoft.Windows.Controls.Primitives #endregion //Text - #region Value - - public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(InputBase), new FrameworkPropertyMetadata(default(object), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValuePropertyChanged, OnCoerceValuePropertyCallback)); - public virtual object Value - { - get { return (object)GetValue(ValueProperty); } - set { SetValue(ValueProperty, value); } - } - - private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - InputBase input = (InputBase)d; - - if (e.OldValue != e.NewValue) - { - input.PreviousValue = e.OldValue; - input.OnValueChanged(e.OldValue, e.NewValue); - - if (input._isInitialized) - input.SyncTextAndValueProperties(e.Property, e.NewValue); - } - } - - protected virtual void OnValueChanged(object oldValue, object newValue) - { - RoutedPropertyChangedEventArgs args = new RoutedPropertyChangedEventArgs(oldValue, newValue); - args.RoutedEvent = InputBase.ValueChangedEvent; - RaiseEvent(args); - } - - private static object OnCoerceValuePropertyCallback(DependencyObject d, object baseValue) - { - InputBase inputBase = d as InputBase; - if (inputBase != null) - return inputBase.OnCoerceValue(baseValue); - else - return baseValue; - } - - protected virtual object OnCoerceValue(object value) - { - return value; - } - - #endregion //Value - - #region ValueType - - public static readonly DependencyProperty ValueTypeProperty = DependencyProperty.Register("ValueType", typeof(Type), typeof(InputBase), new FrameworkPropertyMetadata(typeof(String), FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnValueTypePropertyChanged))); - public Type ValueType - { - get { return (Type)GetValue(ValueTypeProperty); } - set { SetValue(ValueTypeProperty, value); } - } - - private static void OnValueTypePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - InputBase input = (InputBase)d; - input.OnValueTypeChanged((Type)e.OldValue, (Type)e.NewValue); - } - - protected virtual void OnValueTypeChanged(Type oldValue, Type newType) - { - if (_isInitialized) - SyncTextAndValueProperties(TextProperty, Text); - } - - #endregion //ValueType - #endregion //Properties - - #region Base Class Overrides - - protected override void OnInitialized(EventArgs e) - { - base.OnInitialized(e); - - if (!_isInitialized) - { - _isInitialized = true; - SyncTextAndValueProperties(ValueProperty, Value); - } - } - - #endregion //Base Class Overrides - - #region Events - - public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(InputBase)); - public event RoutedPropertyChangedEventHandler ValueChanged - { - add { AddHandler(ValueChangedEvent, value); } - remove { RemoveHandler(ValueChangedEvent, value); } - } - - #endregion //Events - - #region Methods - - protected void SyncTextAndValueProperties(DependencyProperty p, object newValue) - { - //prevents recursive syncing properties - if (_isSyncingTextAndValueProperties) - return; - - _isSyncingTextAndValueProperties = true; - - //this only occures when the user typed in the value - if (InputBase.TextProperty == p) - { - SetValue(InputBase.ValueProperty, ConvertTextToValue(newValue.ToString())); - } - - SetValue(InputBase.TextProperty, ConvertValueToText(newValue)); - - _isSyncingTextAndValueProperties = false; - } - - #endregion //Methods - - #region Abstract - - protected abstract object ConvertTextToValue(string text); - - protected abstract string ConvertValueToText(object value); - - #endregion //Abstract } } diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/UpDownBase.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/UpDownBase.cs index 9be5dfb9..ae415f7b 100644 --- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/UpDownBase.cs +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/UpDownBase.cs @@ -95,12 +95,12 @@ namespace Microsoft.Windows.Controls.Primitives e.Handled = true; break; } - case Key.Enter: - { - if (IsEditable) - SyncTextAndValueProperties(UpDownBase.TextProperty, TextBox.Text); - break; - } + //case Key.Enter: + // { + // if (IsEditable) + // SyncTextAndValueProperties(UpDownBase.TextProperty, TextBox.Text); + // break; + // } } } diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/Implementation/DateTimeUpDown.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/Implementation/DateTimeUpDown.cs index 23fe1cf9..05f0bcdd 100644 --- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/Implementation/DateTimeUpDown.cs +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/Implementation/DateTimeUpDown.cs @@ -14,6 +14,7 @@ namespace Microsoft.Windows.Controls private List _dateTimeInfoList = new List(); private DateTimeInfo _selectedDateTimeInfo; private bool _fireSelectionChangedEvent = true; + private bool _isSyncingTextAndValueProperties; #endregion //Members @@ -21,6 +22,8 @@ namespace Microsoft.Windows.Controls private DateTimeFormatInfo DateTimeFormatInfo { get; set; } + //TODO: add minimum and maximum properties + #region Format public static readonly DependencyProperty FormatProperty = DependencyProperty.Register("Format", typeof(DateTimeFormat), typeof(DateTimeUpDown), new UIPropertyMetadata(DateTimeFormat.FullDateTime, OnFormatChanged)); @@ -72,6 +75,58 @@ namespace Microsoft.Windows.Controls #endregion //FormatString + #region Value + + public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(DateTime?), typeof(DateTimeUpDown), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged, OnCoerceValue)); + public DateTime? Value + { + get { return (DateTime?)GetValue(ValueProperty); } + set { SetValue(ValueProperty, value); } + } + + private static object OnCoerceValue(DependencyObject o, object value) + { + DateTimeUpDown dateTimeUpDown = o as DateTimeUpDown; + if (dateTimeUpDown != null) + return dateTimeUpDown.OnCoerceValue((DateTime?)value); + else + return value; + } + + protected virtual DateTime? OnCoerceValue(DateTime? value) + { + //if the user entered a string value to represent a date or time, we need to parse that string into a valid DatTime value + if (value != null && !(value is DateTime)) + { + return DateTime.Parse(value.ToString(), DateTimeFormatInfo); + } + + return value; + } + + private static void OnValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + DateTimeUpDown dateTimeUpDown = o as DateTimeUpDown; + if (dateTimeUpDown != null) + dateTimeUpDown.OnValueChanged((DateTime?)e.OldValue, (DateTime?)e.NewValue); + } + + protected virtual void OnValueChanged(DateTime? oldValue, DateTime? newValue) + { + //whenever the value changes we need to parse out the value into out DateTimeInfo segments so we can keep track of the individual pieces + //but only if it is not null + if (newValue != null) + ParseValueIntoDateTimeInfo(); + + SyncTextAndValueProperties(DateTimeUpDown.ValueProperty, newValue); + + RoutedPropertyChangedEventArgs args = new RoutedPropertyChangedEventArgs(oldValue, newValue); + args.RoutedEvent = DateTimeUpDown.ValueChangedEvent; + RaiseEvent(args); + } + + #endregion //Value + #endregion //Properties #region Constructors @@ -79,7 +134,6 @@ namespace Microsoft.Windows.Controls static DateTimeUpDown() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DateTimeUpDown), new FrameworkPropertyMetadata(typeof(DateTimeUpDown))); - ValueTypeProperty.OverrideMetadata(typeof(DateTimeUpDown), new FrameworkPropertyMetadata(typeof(Nullable))); } public DateTimeUpDown() @@ -130,27 +184,6 @@ namespace Microsoft.Windows.Controls base.OnPreviewKeyDown(e); } - protected override void OnValueChanged(object oldValue, object newValue) - { - //whenever the value changes we need to parse out the value into out DateTimeInfo segments so we can keep track of the individual pieces - //but only if it is not null - if (newValue != null) - ParseValueIntoDateTimeInfo(); - - base.OnValueChanged(oldValue, newValue); - } - - protected override object OnCoerceValue(object value) - { - //if the user entered a string value to represent a date or time, we need to parse that string into a valid DatTime value - if (value != null && !(value is DateTime)) - { - return DateTime.Parse(value.ToString(), DateTimeFormatInfo); - } - - return base.OnCoerceValue(value); - } - #endregion //Base Class Overrides #region Event Hanlders @@ -165,6 +198,17 @@ namespace Microsoft.Windows.Controls #endregion //Event Hanlders + #region Events + + public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(DateTimeUpDown)); + public event RoutedPropertyChangedEventHandler ValueChanged + { + add { AddHandler(ValueChangedEvent, value); } + remove { RemoveHandler(ValueChangedEvent, value); } + } + + #endregion //Events + #region Methods #region Abstract @@ -181,19 +225,6 @@ namespace Microsoft.Windows.Controls UpdateDateTime(-1); } - protected override object ConvertTextToValue(string text) - { - throw new NotImplementedException("ConvertTextToValue"); - } - - protected override string ConvertValueToText(object value) - { - if (value == null) return string.Empty; - - DateTime dt = DateTime.Parse(value.ToString(), CultureInfo.CurrentCulture); - return dt.ToString(GetFormatString(Format), CultureInfo.CurrentCulture); - } - #endregion //Abstract #region Private @@ -539,6 +570,39 @@ namespace Microsoft.Windows.Controls #endregion //Private + protected object ConvertTextToValue(string text) + { + throw new NotImplementedException("ConvertTextToValue"); + } + + protected string ConvertValueToText(object value) + { + if (value == null) return string.Empty; + + DateTime dt = DateTime.Parse(value.ToString(), CultureInfo.CurrentCulture); + return dt.ToString(GetFormatString(Format), CultureInfo.CurrentCulture); + } + + protected void SyncTextAndValueProperties(DependencyProperty p, object newValue) + { + //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(DateTimeUpDown.ValueProperty, ConvertTextToValue(text)); + } + + SetValue(InputBase.TextProperty, ConvertValueToText(newValue)); + + _isSyncingTextAndValueProperties = false; + } + #endregion //Methods } } diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/MaskedTextBox/Implementation/MaskedTextBox.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/MaskedTextBox/Implementation/MaskedTextBox.cs index 75f05f10..6dd0f035 100644 --- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/MaskedTextBox/Implementation/MaskedTextBox.cs +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/MaskedTextBox/Implementation/MaskedTextBox.cs @@ -9,6 +9,16 @@ namespace Microsoft.Windows.Controls { public class MaskedTextBox : InputBase { + #region Members + + /// + /// Flags if the Text and Value properties are in the process of being sync'd + /// + private bool _isSyncingTextAndValueProperties; + private bool _isInitialized; + + #endregion //Members + #region Properties protected MaskedTextProvider MaskProvider { get; set; } @@ -84,6 +94,58 @@ namespace Microsoft.Windows.Controls #endregion //Mask + #region Value + + public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(MaskedTextBox), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged)); + public object Value + { + get { return (object)GetValue(ValueProperty); } + set { SetValue(ValueProperty, value); } + } + + private static void OnValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + MaskedTextBox maskedTextBox = o as MaskedTextBox; + if (maskedTextBox != null) + maskedTextBox.OnValueChanged((object)e.OldValue, (object)e.NewValue); + } + + protected virtual void OnValueChanged(object oldValue, object newValue) + { + if (_isInitialized) + SyncTextAndValueProperties(DateTimeUpDown.ValueProperty, newValue); + + RoutedPropertyChangedEventArgs args = new RoutedPropertyChangedEventArgs(oldValue, newValue); + args.RoutedEvent = MaskedTextBox.ValueChangedEvent; + RaiseEvent(args); + } + + #endregion //Value + + #region ValueType + + public static readonly DependencyProperty ValueTypeProperty = DependencyProperty.Register("ValueType", typeof(Type), typeof(MaskedTextBox), new UIPropertyMetadata(typeof(String), OnValueTypeChanged)); + public Type ValueType + { + get { return (Type)GetValue(ValueTypeProperty); } + set { SetValue(ValueTypeProperty, value); } + } + + private static void OnValueTypeChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + MaskedTextBox maskedTextBox = o as MaskedTextBox; + if (maskedTextBox != null) + maskedTextBox.OnValueTypeChanged((Type)e.OldValue, (Type)e.NewValue); + } + + protected virtual void OnValueTypeChanged(Type oldValue, Type newValue) + { + if (_isInitialized) + SyncTextAndValueProperties(InputBase.TextProperty, Text); + } + + #endregion //ValueType + #endregion //Properties #region Constructors @@ -115,48 +177,27 @@ namespace Microsoft.Windows.Controls base.OnAccessKey(e); } - protected override object ConvertTextToValue(string text) + protected override void OnGotFocus(RoutedEventArgs e) { - object convertedValue = null; - - Type dataType = ValueType; + if (TextBox != null) + TextBox.Focus(); + } - string valueToConvert = MaskProvider.ToString(); + protected override void OnInitialized(EventArgs e) + { + base.OnInitialized(e); - if (valueToConvert.GetType() == dataType || dataType.IsInstanceOfType(valueToConvert)) - { - convertedValue = valueToConvert; - } -#if !VS2008 - else if (String.IsNullOrWhiteSpace(valueToConvert)) - { - convertedValue = Activator.CreateInstance(dataType); - } -#else - else if (String.IsNullOrEmpty(valueToConvert)) + if (!_isInitialized) { - convertedValue = Activator.CreateInstance(dataType); + _isInitialized = true; + SyncTextAndValueProperties(ValueProperty, Value); } -#endif - else if (null == convertedValue && valueToConvert is IConvertible) - { - convertedValue = Convert.ChangeType(valueToConvert, dataType); - } - - return convertedValue; } - protected override string ConvertValueToText(object value) + protected override void OnTextChanged(string previousValue, string currentValue) { - if (value == null) - value = string.Empty; - - //I have only seen this occur while in Blend, but we need it here so the Blend designer doesn't crash. - if (MaskProvider == null) - return value.ToString(); - - MaskProvider.Set(value.ToString()); - return MaskProvider.ToDisplayString(); + if (_isInitialized) + SyncTextAndValueProperties(InputBase.TextProperty, currentValue); } #endregion @@ -247,6 +288,17 @@ namespace Microsoft.Windows.Controls #endregion //Event Handlers + #region Events + + public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(MaskedTextBox)); + public event RoutedPropertyChangedEventHandler ValueChanged + { + add { AddHandler(ValueChangedEvent, value); } + remove { RemoveHandler(ValueChangedEvent, value); } + } + + #endregion //Events + #region Methods #region Private @@ -284,20 +336,69 @@ namespace Microsoft.Windows.Controls #endregion //Private - #region Public + private object ConvertTextToValue(string text) + { + object convertedValue = null; - /// - /// Attempts to set focus to this element. - /// - public new void Focus() + Type dataType = ValueType; + + string valueToConvert = MaskProvider.ToString().Trim(); + + if (valueToConvert.GetType() == dataType || dataType.IsInstanceOfType(valueToConvert)) + { + convertedValue = valueToConvert; + } +#if !VS2008 + else if (String.IsNullOrWhiteSpace(valueToConvert)) + { + convertedValue = Activator.CreateInstance(dataType); + } +#else + else if (String.IsNullOrEmpty(valueToConvert)) + { + convertedValue = Activator.CreateInstance(dataType); + } +#endif + else if (null == convertedValue && valueToConvert is IConvertible) + { + convertedValue = Convert.ChangeType(valueToConvert, dataType); + } + + return convertedValue; + } + + private string ConvertValueToText(object value) { - if (TextBox != null) - TextBox.Focus(); - else - base.Focus(); + if (value == null) + value = string.Empty; + + //I have only seen this occur while in Blend, but we need it here so the Blend designer doesn't crash. + if (MaskProvider == null) + return value.ToString(); + + MaskProvider.Set(value.ToString()); + return MaskProvider.ToDisplayString(); } - #endregion //Public + protected void SyncTextAndValueProperties(DependencyProperty p, object newValue) + { + //prevents recursive syncing properties + if (_isSyncingTextAndValueProperties) + return; + + _isSyncingTextAndValueProperties = true; + + //this only occures when the user typed in the value + if (InputBase.TextProperty == p) + { + if (newValue != null) + SetValue(MaskedTextBox.ValueProperty, ConvertTextToValue(newValue.ToString())); + } + + SetValue(InputBase.TextProperty, ConvertValueToText(newValue)); + + _isSyncingTextAndValueProperties = false; + } #endregion //Methods diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/NumericUpDown/Implementation/NumericUpDown.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/NumericUpDown/Implementation/NumericUpDown.cs index bff86c73..117a7a31 100644 --- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/NumericUpDown/Implementation/NumericUpDown.cs +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/NumericUpDown/Implementation/NumericUpDown.cs @@ -8,6 +8,15 @@ namespace Microsoft.Windows.Controls { public class NumericUpDown : UpDownBase { + #region Members + + /// + /// Flags if the Text and Value properties are in the process of being sync'd + /// + 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 args = new RoutedPropertyChangedEventArgs(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 Methods + #region Events - /// - /// Sets the valid spin direction based on current value, minimum and maximum. - /// - private void SetValidSpinDirection() + public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(NumericUpDown)); + public event RoutedPropertyChangedEventHandler 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 / 100; - return Convert.ChangeType(result, dataType, info); + return result; } - #endregion + /// + /// Sets the valid spin direction based on current value, minimum and maximum. + /// + 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 } diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/PropertyGrid/Implementation/PropertyGrid.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/PropertyGrid/Implementation/PropertyGrid.cs index 962a3d2c..b5489ee1 100644 --- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/PropertyGrid/Implementation/PropertyGrid.cs +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/PropertyGrid/Implementation/PropertyGrid.cs @@ -322,9 +322,7 @@ namespace Microsoft.Windows.Controls.PropertyGrid editor = new TextBlockEditor(); else if (propertyItem.PropertyType == typeof(bool)) editor = new CheckBoxEditor(); - else if (propertyItem.PropertyType == typeof(int)) - editor = new IntegerUpDownEditor(); - else if (propertyItem.PropertyType == typeof(double)) + else if (propertyItem.PropertyType == typeof(double) || propertyItem.PropertyType == typeof(int)) editor = new NumericUpDownEditor(); else if (propertyItem.PropertyType == typeof(DateTime)) editor = new DateTimeUpDownEditor(); diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj index b1b1930f..8f45e45d 100644 --- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj @@ -210,7 +210,6 @@ -