Browse Source
1. added new MaskedTextBox control
2. no longer have UpDownBase<T>
3. NumericUpDown now has new base class. Hopefully I didn't break anything, but I know I did :0)
4. By default the NumericUpDown uses doubles. To use anything else such as decimals or int you must specify the ValueType accordingly.
<extToolkit:NumericUpDown Grid.Row="1" Value="{Binding Age}" Increment="1" Minimum="18" Maximum="65" ValueType="{x:Type sys:Int32}" />
pull/1645/head
8 changed files with 758 additions and 392 deletions
@ -0,0 +1,159 @@ |
|||
using System; |
|||
using System.Windows.Controls; |
|||
using System.Windows; |
|||
|
|||
namespace Microsoft.Windows.Controls.Primitives |
|||
{ |
|||
public abstract class InputBase : Control |
|||
{ |
|||
#region Members
|
|||
|
|||
/// <summary>
|
|||
/// Flags if the Text and Value properties are in the process of being sync'd
|
|||
/// </summary>
|
|||
private bool _isSyncingTextAndValueProperties; |
|||
private bool _isInitialized; |
|||
|
|||
#endregion //Members
|
|||
|
|||
#region Properties
|
|||
|
|||
public virtual object PreviousValue { get; internal set; } |
|||
|
|||
#region DisplayText
|
|||
|
|||
public static readonly DependencyProperty DisplayTextProperty = DependencyProperty.Register("DisplayText", typeof(string), typeof(InputBase), new FrameworkPropertyMetadata(default(String), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayTextPropertyChanged)); |
|||
public string DisplayText |
|||
{ |
|||
get { return (string)this.GetValue(DisplayTextProperty); } |
|||
set { this.SetValue(DisplayTextProperty, value); } |
|||
} |
|||
|
|||
private static void OnDisplayTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|||
{ |
|||
InputBase input = (InputBase)d; |
|||
input.OnDisplayTextChanged((string)e.OldValue, (string)e.NewValue); |
|||
if (input._isInitialized) |
|||
input.SyncTextAndValueProperties(e.Property, e.NewValue); |
|||
} |
|||
|
|||
protected virtual void OnDisplayTextChanged(string previousValue, string currentValue) |
|||
{ |
|||
|
|||
} |
|||
|
|||
#endregion //DisplayText
|
|||
|
|||
#region IsEditable
|
|||
|
|||
public static readonly DependencyProperty IsEditableProperty = DependencyProperty.Register("IsEditable", typeof(bool), typeof(InputBase), new PropertyMetadata(true)); |
|||
public bool IsEditable |
|||
{ |
|||
get { return (bool)GetValue(IsEditableProperty); } |
|||
set { SetValue(IsEditableProperty, value); } |
|||
} |
|||
|
|||
#endregion //IsEditable
|
|||
|
|||
#region Value
|
|||
|
|||
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(InputBase), new FrameworkPropertyMetadata(default(object), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValuePropertyChanged)); |
|||
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) |
|||
{ |
|||
|
|||
} |
|||
|
|||
#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(DisplayTextProperty, DisplayText); |
|||
} |
|||
|
|||
#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 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.DisplayTextProperty == p) |
|||
{ |
|||
SetValue(InputBase.ValueProperty, ConvertTextToValue(newValue.ToString())); |
|||
} |
|||
|
|||
SetValue(InputBase.DisplayTextProperty, ConvertValueToText(newValue)); |
|||
|
|||
_isSyncingTextAndValueProperties = false; |
|||
} |
|||
|
|||
#endregion //Methods
|
|||
|
|||
#region Abstract
|
|||
|
|||
protected abstract object ConvertTextToValue(string text); |
|||
|
|||
protected abstract string ConvertValueToText(object value); |
|||
|
|||
#endregion //Abstract
|
|||
} |
|||
} |
|||
@ -0,0 +1,162 @@ |
|||
using System; |
|||
using System.Windows.Controls; |
|||
using Microsoft.Windows.Controls.Primitives; |
|||
using System.Windows.Input; |
|||
|
|||
namespace Microsoft.Windows.Controls.Primitives |
|||
{ |
|||
public abstract class UpDownBase : InputBase |
|||
{ |
|||
#region Members
|
|||
|
|||
/// <summary>
|
|||
/// Name constant for Text template part.
|
|||
/// </summary>
|
|||
internal const string ElementTextName = "TextBox"; |
|||
|
|||
/// <summary>
|
|||
/// Name constant for Spinner template part.
|
|||
/// </summary>
|
|||
internal const string ElementSpinnerName = "Spinner"; |
|||
|
|||
#endregion //Members
|
|||
|
|||
#region Properties
|
|||
|
|||
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: |
|||
{ |
|||
DoIncrement(); |
|||
e.Handled = true; |
|||
break; |
|||
} |
|||
case Key.Down: |
|||
{ |
|||
DoDecrement(); |
|||
e.Handled = true; |
|||
break; |
|||
} |
|||
case Key.Enter: |
|||
{ |
|||
SyncTextAndValueProperties(InputBase.DisplayTextProperty, 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 Event Handlers
|
|||
|
|||
private void OnSpinnerSpin(object sender, SpinEventArgs e) |
|||
{ |
|||
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(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Performs an increment if conditions allow it.
|
|||
/// </summary>
|
|||
private void DoDecrement() |
|||
{ |
|||
if (Spinner == null || (Spinner.ValidSpinDirection & ValidSpinDirections.Decrease) == ValidSpinDirections.Decrease) |
|||
{ |
|||
OnDecrement(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Performs a decrement if conditions allow it.
|
|||
/// </summary>
|
|||
private void DoIncrement() |
|||
{ |
|||
if (Spinner == null || (Spinner.ValidSpinDirection & ValidSpinDirections.Increase) == ValidSpinDirections.Increase) |
|||
{ |
|||
OnIncrement(); |
|||
} |
|||
} |
|||
|
|||
#region Abstract
|
|||
|
|||
/// <summary>
|
|||
/// Called by OnSpin when the spin direction is SpinDirection.Increase.
|
|||
/// </summary>
|
|||
protected abstract void OnIncrement(); |
|||
|
|||
/// <summary>
|
|||
/// Called by OnSpin when the spin direction is SpinDirection.Descrease.
|
|||
/// </summary>
|
|||
protected abstract void OnDecrement(); |
|||
|
|||
#endregion //Abstract
|
|||
|
|||
#endregion //Methods
|
|||
} |
|||
} |
|||
@ -0,0 +1,255 @@ |
|||
using System; |
|||
using System.Windows.Controls; |
|||
using System.Windows; |
|||
using System.Windows.Input; |
|||
using System.ComponentModel; |
|||
using Microsoft.Windows.Controls.Primitives; |
|||
|
|||
namespace Microsoft.Windows.Controls |
|||
{ |
|||
|
|||
public class MaskedTextBox : InputBase |
|||
{ |
|||
#region Properties
|
|||
|
|||
protected MaskedTextProvider MaskProvider { get; set; } |
|||
private TextBox TextBox { get; set; } |
|||
|
|||
#region IncludePrompt
|
|||
|
|||
public static readonly DependencyProperty IncludePromptProperty = DependencyProperty.Register("IncludePrompt", typeof(bool), typeof(MaskedTextBox), new UIPropertyMetadata(false, OnIncludePromptPropertyChanged)); |
|||
public bool IncludePrompt |
|||
{ |
|||
get { return (bool)GetValue(IncludePromptProperty); } |
|||
set { SetValue(IncludePromptProperty, value); } |
|||
} |
|||
|
|||
private static void OnIncludePromptPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) |
|||
{ |
|||
MaskedTextBox maskedTextBox = o as MaskedTextBox; |
|||
if (maskedTextBox != null) |
|||
maskedTextBox.OnIncludePromptChanged((bool)e.OldValue, (bool)e.NewValue); |
|||
} |
|||
|
|||
protected virtual void OnIncludePromptChanged(bool oldValue, bool newValue) |
|||
{ |
|||
ResolveMaskProvider(Mask); |
|||
} |
|||
|
|||
#endregion //IncludePrompt
|
|||
|
|||
#region IncludeLiterals
|
|||
|
|||
public static readonly DependencyProperty IncludeLiteralsProperty = DependencyProperty.Register("IncludeLiterals", typeof(bool), typeof(MaskedTextBox), new UIPropertyMetadata(true, OnIncludeLiteralsPropertyChanged)); |
|||
public bool IncludeLiterals |
|||
{ |
|||
get { return (bool)GetValue(IncludeLiteralsProperty); } |
|||
set { SetValue(IncludeLiteralsProperty, value); } |
|||
} |
|||
|
|||
private static void OnIncludeLiteralsPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) |
|||
{ |
|||
MaskedTextBox maskedTextBox = o as MaskedTextBox; |
|||
if (maskedTextBox != null) |
|||
maskedTextBox.OnIncludeLiteralsChanged((bool)e.OldValue, (bool)e.NewValue); |
|||
} |
|||
|
|||
protected virtual void OnIncludeLiteralsChanged(bool oldValue, bool newValue) |
|||
{ |
|||
ResolveMaskProvider(Mask); |
|||
} |
|||
|
|||
#endregion //IncludeLiterals
|
|||
|
|||
#region Mask
|
|||
|
|||
public static readonly DependencyProperty MaskProperty = DependencyProperty.Register("Mask", typeof(string), typeof(MaskedTextBox), new UIPropertyMetadata(default(String), OnMaskPropertyChanged)); |
|||
public string Mask |
|||
{ |
|||
get { return (string)GetValue(MaskProperty); } |
|||
set { SetValue(MaskProperty, value); } |
|||
} |
|||
|
|||
private static void OnMaskPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) |
|||
{ |
|||
MaskedTextBox maskedTextBox = o as MaskedTextBox; |
|||
if (maskedTextBox != null) |
|||
maskedTextBox.OnMaskChanged((string)e.OldValue, (string)e.NewValue); |
|||
} |
|||
|
|||
protected virtual void OnMaskChanged(string oldValue, string newValue) |
|||
{ |
|||
ResolveMaskProvider(newValue); |
|||
UpdateText(MaskProvider, 0); |
|||
} |
|||
|
|||
#endregion //Mask
|
|||
|
|||
#endregion //Properties
|
|||
|
|||
#region Constructors
|
|||
|
|||
static MaskedTextBox() |
|||
{ |
|||
DefaultStyleKeyProperty.OverrideMetadata(typeof(MaskedTextBox), new FrameworkPropertyMetadata(typeof(MaskedTextBox))); |
|||
} |
|||
|
|||
#endregion //Constructors
|
|||
|
|||
#region Overrides
|
|||
|
|||
public override void OnApplyTemplate() |
|||
{ |
|||
TextBox = GetTemplateChild("TextBox") as TextBox; |
|||
TextBox.PreviewTextInput += TextBox_PreviewTextInput; |
|||
TextBox.PreviewKeyDown += TextBox_PreviewKeyDown; |
|||
} |
|||
|
|||
protected override object ConvertTextToValue(string text) |
|||
{ |
|||
object convertedValue = null; |
|||
|
|||
Type dataType = ValueType; |
|||
|
|||
string valueToConvert = MaskProvider.ToString(); |
|||
|
|||
if (valueToConvert.GetType() == dataType || dataType.IsInstanceOfType(valueToConvert)) |
|||
{ |
|||
convertedValue = valueToConvert; |
|||
} |
|||
else if (String.IsNullOrWhiteSpace(valueToConvert)) |
|||
{ |
|||
convertedValue = Activator.CreateInstance(dataType); |
|||
} |
|||
else if (null == convertedValue && valueToConvert is IConvertible) |
|||
{ |
|||
convertedValue = Convert.ChangeType(valueToConvert, dataType); |
|||
} |
|||
|
|||
return convertedValue; |
|||
} |
|||
|
|||
protected override string ConvertValueToText(object value) |
|||
{ |
|||
if (value == null) |
|||
value = string.Empty; |
|||
|
|||
MaskProvider.Set(value.ToString()); |
|||
return MaskProvider.ToDisplayString(); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region Event Handlers
|
|||
|
|||
void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e) |
|||
{ |
|||
//if the text is readonly do not add the text
|
|||
if (TextBox.IsReadOnly) |
|||
{ |
|||
e.Handled = true; |
|||
return; |
|||
} |
|||
|
|||
int position = TextBox.SelectionStart; |
|||
MaskedTextProvider provider = MaskProvider; |
|||
if (position < TextBox.Text.Length) |
|||
{ |
|||
position = GetNextCharacterPosition(position); |
|||
|
|||
if (Keyboard.IsKeyToggled(Key.Insert)) |
|||
{ |
|||
if (provider.Replace(e.Text, position)) |
|||
position++; |
|||
} |
|||
else |
|||
{ |
|||
if (provider.InsertAt(e.Text, position)) |
|||
position++; |
|||
} |
|||
|
|||
position = GetNextCharacterPosition(position); |
|||
} |
|||
|
|||
UpdateText(provider, position); |
|||
e.Handled = true; |
|||
|
|||
base.OnPreviewTextInput(e); |
|||
} |
|||
|
|||
void TextBox_PreviewKeyDown(object sender, KeyEventArgs e) |
|||
{ |
|||
base.OnPreviewKeyDown(e); |
|||
MaskedTextProvider provider = MaskProvider; |
|||
int position = TextBox.SelectionStart; |
|||
int selectionlength = TextBox.SelectionLength; |
|||
// If no selection use the start position else use end position
|
|||
int endposition = (selectionlength == 0) ? position : position + selectionlength - 1; |
|||
|
|||
if (e.Key == Key.Delete && position < TextBox.Text.Length)//handle the delete key
|
|||
{ |
|||
if (provider.RemoveAt(position, endposition)) |
|||
UpdateText(provider, position); |
|||
|
|||
e.Handled = true; |
|||
} |
|||
else if (e.Key == Key.Space) |
|||
{ |
|||
if (provider.InsertAt(" ", position)) |
|||
UpdateText(provider, position); |
|||
e.Handled = true; |
|||
} |
|||
else if (e.Key == Key.Back)//handle the back space
|
|||
{ |
|||
if ((position > 0) && (selectionlength == 0)) |
|||
{ |
|||
position--; |
|||
if (provider.RemoveAt(position)) |
|||
UpdateText(provider, position); |
|||
} |
|||
|
|||
if (selectionlength != 0) |
|||
{ |
|||
if (provider.RemoveAt(position, endposition)) |
|||
{ |
|||
if (position > 0) |
|||
position--; |
|||
|
|||
UpdateText(provider, position); |
|||
} |
|||
} |
|||
|
|||
e.Handled = true; |
|||
} |
|||
} |
|||
|
|||
#endregion //Event Handlers
|
|||
|
|||
#region Methods
|
|||
|
|||
private void UpdateText(MaskedTextProvider provider, int position) |
|||
{ |
|||
DisplayText = provider.ToDisplayString(); |
|||
|
|||
if (TextBox != null) |
|||
TextBox.SelectionStart = position; |
|||
} |
|||
|
|||
private int GetNextCharacterPosition(int startPosition) |
|||
{ |
|||
int position = MaskProvider.FindEditPositionFrom(startPosition, true); |
|||
return position == -1 ? startPosition : position; |
|||
} |
|||
|
|||
private void ResolveMaskProvider(string mask) |
|||
{ |
|||
MaskProvider = new MaskedTextProvider(mask) |
|||
{ |
|||
IncludePrompt = this.IncludePrompt, |
|||
IncludeLiterals = this.IncludeLiterals |
|||
}; |
|||
} |
|||
|
|||
#endregion //Methods
|
|||
} |
|||
} |
|||
@ -1,321 +0,0 @@ |
|||
using System; |
|||
using System.Windows.Input; |
|||
using System.Windows.Controls; |
|||
using System.Windows; |
|||
|
|||
namespace Microsoft.Windows.Controls |
|||
{ |
|||
public abstract class UpDownBase<T> : Control |
|||
{ |
|||
#region Members
|
|||
|
|||
/// <summary>
|
|||
/// Name constant for Text template part.
|
|||
/// </summary>
|
|||
internal const string ElementTextName = "Text"; |
|||
|
|||
/// <summary>
|
|||
/// Name constant for Spinner template part.
|
|||
/// </summary>
|
|||
internal const string ElementSpinnerName = "Spinner"; |
|||
|
|||
/// <summary>
|
|||
/// Flags if the Text and Value properties are in the process of being sync'd
|
|||
/// </summary>
|
|||
bool _isSyncingTextAndValueProperties; |
|||
|
|||
#endregion //Members
|
|||
|
|||
#region Properties
|
|||
|
|||
#region Value
|
|||
|
|||
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(T), typeof(UpDownBase<T>), 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<T> udb = (UpDownBase<T>)d; |
|||
T oldValue = (T)e.OldValue; |
|||
T newValue = (T)e.NewValue; |
|||
|
|||
udb.SyncTextAndValueProperties(e.Property, e.NewValue); |
|||
|
|||
RoutedPropertyChangedEventArgs<T> changedArgs = new RoutedPropertyChangedEventArgs<T>(oldValue, newValue); |
|||
udb.OnValueChanged(changedArgs); |
|||
} |
|||
|
|||
protected virtual void OnValueChanged(RoutedPropertyChangedEventArgs<T> e) |
|||
{ |
|||
if (ValueChanged != null) |
|||
ValueChanged(this, e); |
|||
} |
|||
|
|||
#endregion //Value
|
|||
|
|||
#region Text
|
|||
|
|||
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(UpDownBase<T>), 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<T> udb = (UpDownBase<T>)d; |
|||
udb.SyncTextAndValueProperties(e.Property, e.NewValue); |
|||
} |
|||
|
|||
#endregion //Text
|
|||
|
|||
#region IsEditable
|
|||
|
|||
public static readonly DependencyProperty IsEditableProperty = DependencyProperty.Register("IsEditable", typeof(bool), typeof(UpDownBase<T>), 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<T> source = d as UpDownBase<T>; |
|||
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<T>.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
|
|||
|
|||
/// <summary>
|
|||
/// Called by ApplyValue to parse user input.
|
|||
/// </summary>
|
|||
/// <param name="text">User input.</param>
|
|||
/// <returns>Value parsed from user input.</returns>
|
|||
protected abstract T ParseValue(string text); |
|||
|
|||
/// <summary>
|
|||
/// Renders the value property into the textbox text.
|
|||
/// </summary>
|
|||
/// <returns>Formatted Value.</returns>
|
|||
protected internal abstract string FormatValue(); |
|||
|
|||
/// <summary>
|
|||
/// Called by OnSpin when the spin direction is SpinDirection.Increase.
|
|||
/// </summary>
|
|||
protected abstract void OnIncrement(); |
|||
|
|||
/// <summary>
|
|||
/// Called by OnSpin when the spin direction is SpinDirection.Descrease.
|
|||
/// </summary>
|
|||
protected abstract void OnDecrement(); |
|||
|
|||
#endregion //Abstract
|
|||
|
|||
#region Protected
|
|||
|
|||
/// <summary>
|
|||
/// GetValue override to return Value property as object type.
|
|||
/// </summary>
|
|||
/// <returns>The Value property as object type.</returns>
|
|||
protected object GetValue() |
|||
{ |
|||
return Value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SetValue override to set value to Value property.
|
|||
/// </summary>
|
|||
/// <param name="value">New value.</param>
|
|||
protected void SetValue(object value) |
|||
{ |
|||
Value = (T)value; |
|||
} |
|||
|
|||
#endregion //Protected
|
|||
|
|||
#region Private
|
|||
|
|||
/// <summary>
|
|||
/// Performs an increment if conditions allow it.
|
|||
/// </summary>
|
|||
private void DoDecrement() |
|||
{ |
|||
if (Spinner == null || (Spinner.ValidSpinDirection & ValidSpinDirections.Decrease) == ValidSpinDirections.Decrease) |
|||
{ |
|||
OnDecrement(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Performs a decrement if conditions allow it.
|
|||
/// </summary>
|
|||
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<T>.TextProperty == p) |
|||
{ |
|||
SetValue(UpDownBase<T>.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<T>.TextProperty, FormatValue()); |
|||
|
|||
_isSyncingTextAndValueProperties = false; |
|||
} |
|||
|
|||
#endregion //Private
|
|||
|
|||
#region Virtual
|
|||
|
|||
/// <summary>
|
|||
/// Occurs when the spinner spins.
|
|||
/// </summary>
|
|||
/// <param name="e">Event args.</param>
|
|||
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
|
|||
|
|||
/// <summary>
|
|||
/// Event handler for Spinner template part's Spin event.
|
|||
/// </summary>
|
|||
/// <param name="sender">The Spinner template part.</param>
|
|||
/// <param name="e">Event args.</param>
|
|||
private void OnSpinnerSpin(object sender, SpinEventArgs e) |
|||
{ |
|||
OnSpin(e); |
|||
} |
|||
|
|||
#endregion //Event Handlers
|
|||
|
|||
#region Events
|
|||
|
|||
/// <summary>
|
|||
/// Occurs when Value property has changed.
|
|||
/// </summary>
|
|||
public event RoutedPropertyChangedEventHandler<T> ValueChanged; |
|||
|
|||
#endregion //Events
|
|||
} |
|||
} |
|||
Loading…
Reference in new issue