using System; using System.Windows.Controls; using Microsoft.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows; using System.Windows.Data; namespace Microsoft.Windows.Controls.Primitives { public abstract class UpDownBase : InputBase { #region Members /// /// Name constant for Text template part. /// internal const string ElementTextName = "TextBox"; /// /// Name constant for Spinner template part. /// internal const string ElementSpinnerName = "Spinner"; #endregion //Members #region Properties #region AllowSpin public static readonly DependencyProperty AllowSpinProperty = DependencyProperty.Register("AllowSpin", typeof(bool), typeof(UpDownBase), new UIPropertyMetadata(true)); public bool AllowSpin { get { return (bool)GetValue(AllowSpinProperty); } set { SetValue(AllowSpinProperty, value); } } #endregion //AllowSpin #region ShowButtonSpinner public static readonly DependencyProperty ShowButtonSpinnerProperty = DependencyProperty.Register("ShowButtonSpinner", typeof(bool), typeof(UpDownBase), new UIPropertyMetadata(true)); public bool ShowButtonSpinner { get { return (bool)GetValue(ShowButtonSpinnerProperty); } set { SetValue(ShowButtonSpinnerProperty, value); } } #endregion //ShowButtonSpinner protected TextBox TextBox { get; private set; } private Spinner _spinner; internal Spinner Spinner { get { return _spinner; } private set { _spinner = value; _spinner.Spin += OnSpinnerSpin; } } #endregion //Properties #region Constructors internal UpDownBase() { } #endregion //Constructors #region Base Class Overrides public override void OnApplyTemplate() { base.OnApplyTemplate(); TextBox = GetTemplateChild(ElementTextName) as TextBox; Spinner = GetTemplateChild(ElementSpinnerName) as Spinner; } protected override void OnPreviewKeyDown(KeyEventArgs e) { switch (e.Key) { case Key.Up: { if (AllowSpin) DoIncrement(); e.Handled = true; break; } case Key.Down: { if (AllowSpin) DoDecrement(); e.Handled = true; break; } //case Key.Enter: // { // if (IsEditable) // SyncTextAndValueProperties(UpDownBase.TextProperty, TextBox.Text); // break; // } } } protected override void OnMouseWheel(MouseWheelEventArgs e) { base.OnMouseWheel(e); if (!e.Handled && AllowSpin) { if (e.Delta < 0) { DoDecrement(); } else if (0 < e.Delta) { DoIncrement(); } e.Handled = true; } } #endregion //Base Class Overrides #region Event Handlers private void OnSpinnerSpin(object sender, SpinEventArgs e) { if (AllowSpin) OnSpin(e); } #endregion //Event Handlers #region Methods protected virtual void OnSpin(SpinEventArgs e) { if (e == null) throw new ArgumentNullException("e"); if (e.Direction == SpinDirection.Increase) DoIncrement(); else DoDecrement(); } /// /// Performs an increment if conditions allow it. /// private void DoDecrement() { if (Spinner == null || (Spinner.ValidSpinDirection & ValidSpinDirections.Decrease) == ValidSpinDirections.Decrease) { OnDecrement(); } } /// /// Performs a decrement if conditions allow it. /// private void DoIncrement() { if (Spinner == null || (Spinner.ValidSpinDirection & ValidSpinDirections.Increase) == ValidSpinDirections.Increase) { OnIncrement(); } } #region Abstract /// /// Called by OnSpin when the spin direction is SpinDirection.Increase. /// protected abstract void OnIncrement(); /// /// Called by OnSpin when the spin direction is SpinDirection.Descrease. /// protected abstract void OnDecrement(); #endregion //Abstract #endregion //Methods } /// /// This is going to be the new UpDownBase class. In order to reliably support the different numeric data types, I need to create a specific control for each data type. /// So instead of copying and pasting I am going to try to create a generic base class that has most of the logic in it. This will make the NumericUpDown control obsolete /// and it will be removed from the toolkit. The new controls will be DecimalUpDown, DoubleUpDown, and IntegerUpDown. /// /// public abstract class UpDownBase : InputBase { #region Members /// /// Name constant for Text template part. /// internal const string ElementTextName = "TextBox"; /// /// Name constant for Spinner template part. /// internal const string ElementSpinnerName = "Spinner"; /// /// Flags if the Text and Value properties are in the process of being sync'd /// private bool _isSyncingTextAndValueProperties; #endregion //Members #region Properties protected Spinner Spinner { get; private set; } protected TextBox TextBox { get; private set; } #region AllowSpin public static readonly DependencyProperty AllowSpinProperty = DependencyProperty.Register("AllowSpin", typeof(bool), typeof(UpDownBase), new UIPropertyMetadata(true)); public bool AllowSpin { get { return (bool)GetValue(AllowSpinProperty); } set { SetValue(AllowSpinProperty, value); } } #endregion //AllowSpin #region ShowButtonSpinner public static readonly DependencyProperty ShowButtonSpinnerProperty = DependencyProperty.Register("ShowButtonSpinner", typeof(bool), typeof(UpDownBase), new UIPropertyMetadata(true)); public bool ShowButtonSpinner { get { return (bool)GetValue(ShowButtonSpinnerProperty); } set { SetValue(ShowButtonSpinnerProperty, value); } } #endregion //ShowButtonSpinner #region Value public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(T), typeof(UpDownBase), new FrameworkPropertyMetadata(default(T), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged, OnCoerceValue)); public T Value { get { return (T)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } private static object OnCoerceValue(DependencyObject o, object value) { UpDownBase upDownBase = o as UpDownBase; if (upDownBase != null) return upDownBase.OnCoerceValue((T)value); else return value; } protected virtual T OnCoerceValue(T value) { return value; } private static void OnValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { UpDownBase upDownBase = o as UpDownBase; if (upDownBase != null) upDownBase.OnValueChanged((T)e.OldValue, (T)e.NewValue); } protected virtual void OnValueChanged(T oldValue, T newValue) { SyncTextAndValueProperties(NumericUpDown.ValueProperty, string.Empty); RoutedPropertyChangedEventArgs args = new RoutedPropertyChangedEventArgs(oldValue, newValue); args.RoutedEvent = NumericUpDown.ValueChangedEvent; RaiseEvent(args); } #endregion //Value #endregion //Properties #region Constructors internal UpDownBase() { } #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(); TextBox = GetTemplateChild(ElementTextName) as TextBox; Spinner = GetTemplateChild(ElementSpinnerName) as Spinner; Spinner.Spin += OnSpinnerSpin; } protected override void OnGotFocus(RoutedEventArgs e) { if (TextBox != null) TextBox.Focus(); } protected override void OnPreviewKeyDown(KeyEventArgs e) { switch (e.Key) { case Key.Up: { if (AllowSpin && IsEditable) DoIncrement(); e.Handled = true; break; } case Key.Down: { if (AllowSpin && IsEditable) DoDecrement(); e.Handled = true; break; } case Key.Enter: { if (IsEditable) { var binding = BindingOperations.GetBindingExpression(TextBox, System.Windows.Controls.TextBox.TextProperty); binding.UpdateSource(); } break; } } } protected override void OnMouseWheel(MouseWheelEventArgs e) { base.OnMouseWheel(e); if (!e.Handled && AllowSpin && IsEditable) { if (e.Delta < 0) { DoDecrement(); } else if (0 < e.Delta) { DoIncrement(); } e.Handled = true; } } protected override void OnTextChanged(string oldValue, string newValue) { SyncTextAndValueProperties(InputBase.TextProperty, newValue); } #endregion //Base Class Overrides #region Event Handlers private void OnSpinnerSpin(object sender, SpinEventArgs e) { if (AllowSpin) OnSpin(e); } #endregion //Event Handlers #region Events public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(UpDownBase)); public event RoutedPropertyChangedEventHandler ValueChanged { add { AddHandler(ValueChangedEvent, value); } remove { RemoveHandler(ValueChangedEvent, value); } } #endregion //Events #region Methods protected virtual void OnSpin(SpinEventArgs e) { if (e == null) throw new ArgumentNullException("e"); if (e.Direction == SpinDirection.Increase) DoIncrement(); else DoDecrement(); } /// /// Performs an increment if conditions allow it. /// private void DoDecrement() { if (Spinner == null || (Spinner.ValidSpinDirection & ValidSpinDirections.Decrease) == ValidSpinDirections.Decrease) { OnDecrement(); } } /// /// Performs a decrement if conditions allow it. /// private void DoIncrement() { if (Spinner == null || (Spinner.ValidSpinDirection & ValidSpinDirections.Increase) == ValidSpinDirections.Increase) { OnIncrement(); } } private void SyncTextAndValueProperties(DependencyProperty p, string text) { //prevents recursive syncing properties if (_isSyncingTextAndValueProperties) return; _isSyncingTextAndValueProperties = true; //this only occures when the user typed in the value if (InputBase.TextProperty == p) { Value = ConvertTextToValue(text); } Text = ConvertValueToText(); _isSyncingTextAndValueProperties = false; } #region Abstract /// /// Called by OnSpin when the spin direction is SpinDirection.Increase. /// protected abstract void OnIncrement(); /// /// Called by OnSpin when the spin direction is SpinDirection.Descrease. /// protected abstract void OnDecrement(); protected abstract T ConvertTextToValue(string text); protected abstract string ConvertValueToText(); #endregion //Abstract #endregion //Methods } }