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"; /// /// 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 MouseWheelActiveOnFocus public static readonly DependencyProperty MouseWheelActiveOnFocusProperty = DependencyProperty.Register("MouseWheelActiveOnFocus", typeof(bool), typeof(UpDownBase), new UIPropertyMetadata(true)); public bool MouseWheelActiveOnFocus { get { return (bool)GetValue(MouseWheelActiveOnFocusProperty); } set { SetValue(MouseWheelActiveOnFocusProperty, value); } } #endregion //MouseWheelActiveOnFocus #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)); public T Value { get { return (T)GetValue(ValueProperty); } set { SetValue(ValueProperty, 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) { ValidateValue(newValue); SyncTextAndValueProperties(ValueProperty, string.Empty); SetValidSpinDirection(); RoutedPropertyChangedEventArgs args = new RoutedPropertyChangedEventArgs(oldValue, newValue); args.RoutedEvent = 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; SetValidSpinDirection(); } 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 && !IsReadOnly) DoIncrement(); e.Handled = true; break; } case Key.Down: { if (AllowSpin && !IsReadOnly) DoDecrement(); e.Handled = true; break; } case Key.Enter: { if (!IsReadOnly) { 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 && !IsReadOnly && ((TextBox.IsFocused && MouseWheelActiveOnFocus) || !MouseWheelActiveOnFocus)) { 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 && !IsReadOnly) OnSpin(e); } #endregion //Event Handlers #region Events //Due to a bug in Visual Studio, you cannot create event handlers for generic T args in XAML, so I have to use object instead. 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(); } } protected 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(); #if VS2008 //there is a bug in .NET 3.5 which will not correctly update the textbox text through binding. if (TextBox != null) TextBox.Text = Text; #endif _isSyncingTextAndValueProperties = false; } #region Abstract /// /// Coerces the value. /// protected abstract T CoerceValue(T value); /// /// Converts the formatted text to a value. /// protected abstract T ConvertTextToValue(string text); /// /// Converts the value to formatted text. /// /// protected abstract string ConvertValueToText(); /// /// 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(); /// /// Sets the valid spin directions. /// protected abstract void SetValidSpinDirection(); /// /// Validates the value and keeps it between the Min and Max values. /// /// The value. protected abstract void ValidateValue(T value); #endregion //Abstract #endregion //Methods } }