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