using System; using System.Windows.Input; using System.Windows.Controls; using System.Windows; namespace Microsoft.Windows.Controls { public abstract class UpDownBase : Control { #region Members /// /// Name constant for Text template part. /// internal const string ElementTextName = "Text"; /// /// 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 /// bool _isSyncingTextAndValueProperties; #endregion //Members #region Properties #region Value public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(T), typeof(UpDownBase), 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 udb = (UpDownBase)d; T oldValue = (T)e.OldValue; T newValue = (T)e.NewValue; udb.SyncTextAndValueProperties(e.Property, e.NewValue); RoutedPropertyChangedEventArgs changedArgs = new RoutedPropertyChangedEventArgs(oldValue, newValue); udb.OnValueChanged(changedArgs); } protected virtual void OnValueChanged(RoutedPropertyChangedEventArgs e) { if (ValueChanged != null) ValueChanged(this, e); } #endregion //Value #region Text public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(UpDownBase), 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) { UpDownBase udb = (UpDownBase)d; udb.SyncTextAndValueProperties(e.Property, e.NewValue); } #endregion //Text #region IsEditable public static readonly DependencyProperty IsEditableProperty = DependencyProperty.Register("IsEditable", typeof(bool), typeof(UpDownBase), 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 source = d as UpDownBase; 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 { _spinner = value; _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; } case Key.Enter: { SyncTextAndValueProperties(UpDownBase.TextProperty, TextBox.Text); 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 /// /// Called by ApplyValue to parse user input. /// /// User input. /// Value parsed from user input. protected abstract T ParseValue(string text); /// /// Renders the value property into the textbox text. /// /// Formatted Value. protected internal abstract string FormatValue(); /// /// 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 #region Protected /// /// GetValue override to return Value property as object type. /// /// The Value property as object type. protected object GetValue() { return Value; } /// /// SetValue override to set value to Value property. /// /// New value. protected void SetValue(object value) { Value = (T)value; } #endregion //Protected #region Private /// /// 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(); } } 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 (UpDownBase.TextProperty == p) { SetValue(UpDownBase.ValueProperty, ParseValue(newValue.ToString())); } //we need to update the text no matter what because the user could have used the spin buttons to change dthe value //or typed in the textbox so we need to reformat the entered value. SetValue(UpDownBase.TextProperty, FormatValue()); _isSyncingTextAndValueProperties = false; } #endregion //Private #region Virtual /// /// Occurs when the spinner spins. /// /// Event args. 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 /// /// Event handler for Spinner template part's Spin event. /// /// The Spinner template part. /// Event args. private void OnSpinnerSpin(object sender, SpinEventArgs e) { OnSpin(e); } #endregion //Event Handlers #region Events /// /// Occurs when Value property has changed. /// public event RoutedPropertyChangedEventHandler ValueChanged; #endregion //Events } }