Extended WPF Toolkit DoubleUpDown - wpf

I've added an Extended WPF Toolkit DoubleUpDown control.
The behaviour is if you type 5.35 it is fine.
Then say you type 4.3errortext7 and tab it reverts back to 5.35 (as the 4.3errortext7 is not a valid number).
However I'd like it just to go to 4.37 in that case (and ignore the invalid characters.
Is there an elegant way to get my required behaviour?

The best I was able to come up with is to use the PreviewTextInput Event and check for valid input, unfortunatly it will allow still allow a space between numbers, but will prevent all other text from being input.
private void doubleUpDown1_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (!(char.IsNumber(e.Text[0]) || e.Text== "." ))
{
e.Handled = true;
}
}

Maybe a bit late but I had the same problem yesterday and I also did not want to register a handler every time I use the control. I mixed the solution by Mark Hall with an Attached Behavior (inspired by this post: http://www.codeproject.com/Articles/28959/Introduction-to-Attached-Behaviors-in-WPF):
public static class DoubleUpDownBehavior
{
public static readonly DependencyProperty RestrictInputProperty =
DependencyProperty.RegisterAttached("RestrictInput", typeof(bool),
typeof(DoubleUpDownBehavior),
new UIPropertyMetadata(false, OnRestrictInputChanged));
public static bool GetRestrictInput(DoubleUpDown ctrl)
{
return (bool)ctrl.GetValue(RestrictInputProperty);
}
public static void SetRestrictInput(DoubleUpDown ctrl, bool value)
{
ctrl.SetValue(RestrictInputProperty, value);
}
private static void OnRestrictInputChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
DoubleUpDown item = depObj as DoubleUpDown;
if (item == null)
return;
if (e.NewValue is bool == false)
return;
if ((bool)e.NewValue)
item.PreviewTextInput += OnPreviewTextInput;
else
item.PreviewTextInput -= OnPreviewTextInput;
}
private static void OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (!(char.IsNumber(e.Text[0]) ||
e.Text == CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator))
{
e.Handled = true;
}
}
}
Then you can simply set the default style for DoubleUpDown like this:
<Style TargetType="xctk:DoubleUpDown">
<Setter Property="behaviors:DoubleUpDownBehavior.RestrictInput" Value="True" />
</Style>

In my case, it was much better to use regex.
private void UpDownBox_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
var upDownBox = (sender as DoubleUpDown);
TextBox textBoxInTemplate = (TextBox)upDownBox.Template.FindName("PART_TextBox", upDownBox);
Regex regex = new Regex("^[.][0-9]+$|^[0-9]*[.]{0,1}[0-9]*$");
e.Handled = !regex.IsMatch(upDownBox.Text.Insert((textBoxInTemplate).SelectionStart, e.Text));
}

Related

WPF Custom TextBox with Decimal Formatting

I am new to WPF.
I have a requirement that I need to develop a custom textbox control which should support the functionality like:
Should accept only decimal values.
Should round off to 3 decimal places when assigned a value through code or by the user.
Should show the full value(without formatting) on focus.
Eg:
If 2.21457 is assigned to textbox(by code or by user), it should display 2.215. When user clicks in it to edit it, it must show the full value 2.21457.
After the user edits the value to 5.42235 and tabs out, it should again round off to 5.422.
Tried it without success. So need some help on it.
Thanks in advance for the help.
Thanks
I have written a custom control which will have dependency property called ActualText. Bind your value into that ActualText property and manipulated the Text property of the textbox during the gotfocus and lostfocus event. Also validated for decimal number in the PreviewTextInput event. refer the below code.
class TextBoxEx:TextBox
{
public string ActualText
{
get { return (string)GetValue(ActualTextProperty); }
set { SetValue(ActualTextProperty, value); }
}
// Using a DependencyProperty as the backing store for ActualText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ActualTextProperty =
DependencyProperty.Register("ActualText", typeof(string), typeof(TextBoxEx), new PropertyMetadata(string.Empty, OnActualTextChanged));
private static void OnActualTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBox tx = d as TextBox;
tx.Text = (string)e.NewValue;
string str = tx.Text;
double dbl = Convert.ToDouble(str);
str = string.Format("{0:0.###}", dbl);
tx.Text = str;
}
public TextBoxEx()
{
this.GotFocus += TextBoxEx_GotFocus;
this.LostFocus += TextBoxEx_LostFocus;
this.PreviewTextInput += TextBoxEx_PreviewTextInput;
}
void TextBoxEx_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
decimal d;
if(!decimal.TryParse(e.Text,out d))
{
e.Handled = true;
}
}
void TextBoxEx_LostFocus(object sender, System.Windows.RoutedEventArgs e)
{
ConvertText();
}
void TextBoxEx_GotFocus(object sender, System.Windows.RoutedEventArgs e)
{
this.Text = ActualText;
}
private void ConvertText()
{
string str = this.Text;
ActualText = str;
double dbl = Convert.ToDouble(str);
str = string.Format("{0:0.###}", dbl);
this.Text = str;
}
}

TextBox AttachedProperty to Select All text not working as expected?

I have an attached property called "SelectAllOnFocus". Values of true/false.
public static class TextBoxProps
{
private static void MyTextBoxKeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
((TextBox)sender).Text = string.Empty;
}
}
public static void SetSelectAllOnFocus(DependencyObject dependencyObject, bool selectAllOnFocus)
{
if (!ReferenceEquals(null, dependencyObject))
{
dependencyObject.SetValue(SelectAllOnFocus, selectAllOnFocus);
}
}
public static bool GetSelectAllOnFocus(DependencyObject dependencyObject)
{
if (!ReferenceEquals(null, dependencyObject))
{
return (bool)dependencyObject.GetValue(SelectAllOnFocus);
}
else
{
return false;
}
}
private static void OnSelectAllOnFocus(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
bool selectAllOnFocus = (bool)e.NewValue == true;
var theTextBox = d as TextBox;
if (selectAllOnFocus && theTextBox != null)
{
theTextBox.PreviewMouseDown -= MyTextBoxMouseEnter; theTextBox.PreviewMouseDown += MyTextBoxMouseEnter;
}
}
private static void MyTextBoxMouseEnter(object sender, MouseEventArgs e)
{
((TextBox)sender).SelectAll();
e.Handled = false;
}
public static readonly DependencyProperty SelectAllOnFocus
= DependencyProperty.RegisterAttached("SelectAllOnFocus", typeof(bool), typeof(TextBoxEscapeProperty),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnSelectAllOnFocus)));
}
What happens is the following:
The PreviewMouseDown event gets triggered.
The MyTextBoxMouseEnter method gets called.
The SelectAll() Method gets called.
When I do a "watch" on ((TextBox)sender).SelectedText, the value is correct (meaning whatever is in the textbox is showing up as selectedText).
The textbox itself is unchanged. No text is selected.
This is part of a general WPF style. All textboxes in the application should receive this property and it's associated behavior.
I'm stumped. Any ideas?
Thanks
What happens if you call ((TextBox)sender).UpdateLayout(); immediately after the SelectAll command? Or maybe you need to set the Keyboard focus to the text box.
It might be a better option to use something like this, which works if the text box is being selected with the mouse or the keyboard. (You'll need to modify it to check your "SelectAllOnFocus" property)
In your App.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
// Select the text in a TextBox when it receives focus.
EventManager.RegisterClassHandler(typeof(TextBox), TextBox.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(SelectivelyIgnoreMouseButton));
EventManager.RegisterClassHandler(typeof(TextBox), TextBox.GotKeyboardFocusEvent, new RoutedEventHandler(SelectAllText));
EventManager.RegisterClassHandler(typeof(TextBox), TextBox.MouseDoubleClickEvent, new RoutedEventHandler(SelectAllText));
base.OnStartup(e);
}
void SelectivelyIgnoreMouseButton(object sender, MouseButtonEventArgs e)
{
// Find the TextBox
DependencyObject parent = e.OriginalSource as UIElement;
while (parent != null && !(parent is TextBox))
parent = VisualTreeHelper.GetParent(parent);
if (parent != null)
{
var textBox = (TextBox)parent;
if (!textBox.IsKeyboardFocusWithin)
{
// If the text box is not yet focused, give it the focus and
// stop further processing of this click event.
textBox.Focus();
e.Handled = true;
}
}
}
void SelectAllText(object sender, RoutedEventArgs e)
{
var textBox = e.OriginalSource as TextBox;
if (textBox != null)
textBox.SelectAll();
}

Is using an "Attached Property" a good practice for input capturing?

I am looking to find a generic way to support keyboard wedge scanning for my WPF TextBox controls.
(I am really a novice when it comes to more advanced WPF features, so I would like to ask if I am going in the right direction before I put a lot of time into research.)
What I am wanting to do is to add an Attached Property (or something) to my TextBoxes that will cause it to read all input into the box and then call a custom "ScanCompleted" command with the scanned input.
If an Attached Property is not a good fit for this, then is there a way to get this command on a TextBox without descending my own custom "ScanableTextBox"?
(Note: The criteria for a scan (instead of typed data) is that it will start with the Pause key (#19) and end with a Return key (#13).)
I think this could probably be accomplished with attached properties (behaviors), but would be much simpler and more straightforward to simply subclass TextBox and override the OnTextChanged, OnKeyDown, OnKeyUp and similar methods to add custom functionality.
Why don't you want to create your own control in this way?
update: Attached Behaviour
If you really don't want a derived control, here is an attached behaviour that accomplishes this (explanation below):
public class ScanReading
{
private static readonly IDictionary<TextBox, ScanInfo> TrackedTextBoxes = new Dictionary<TextBox, ScanInfo>();
public static readonly DependencyProperty ScanCompletedCommandProperty =
DependencyProperty.RegisterAttached("ScanCompletedCommand", typeof (ICommand), typeof (ScanReading),
new PropertyMetadata(default(ICommand), OnScanCompletedCommandChanged));
public static void SetScanCompletedCommand(TextBox textBox, ICommand value)
{
textBox.SetValue(ScanCompletedCommandProperty, value);
}
public static ICommand GetScanCompletedCommand(TextBox textBox)
{
return (ICommand) textBox.GetValue(ScanCompletedCommandProperty);
}
private static void OnScanCompletedCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBox = d as TextBox;
if (textBox == null)
return;
var command = (ICommand) e.NewValue;
if (command == null)
{
textBox.Unloaded -= OnTextBoxUnloaded;
textBox.KeyUp -= OnTextBoxKeyUp;
TrackedTextBoxes.Remove(textBox);
}
else
{
textBox.Unloaded += OnTextBoxUnloaded;
TrackedTextBoxes.Add(textBox, new ScanInfo(command));
textBox.KeyUp += OnTextBoxKeyUp;
}
}
static void OnTextBoxKeyUp(object sender, KeyEventArgs e)
{
var textBox = (TextBox) sender;
var scanInfo = TrackedTextBoxes[textBox];
if (scanInfo.IsTracking)
{
if (e.Key == Key.Return)
{
scanInfo.ScanCompletedCommand.Execute(textBox.Text);
scanInfo.IsTracking = false;
}
}
else if (string.IsNullOrEmpty(textBox.Text) && e.Key == Key.Pause)
{
TrackedTextBoxes[textBox].IsTracking = true;
}
}
static void OnTextBoxUnloaded(object sender, RoutedEventArgs e)
{
var textBox = (TextBox) sender;
textBox.KeyUp -= OnTextBoxKeyUp;
textBox.Unloaded -= OnTextBoxUnloaded;
TrackedTextBoxes.Remove(textBox);
}
}
public class ScanInfo
{
public ScanInfo(ICommand scanCompletedCommand)
{
ScanCompletedCommand = scanCompletedCommand;
}
public bool IsTracking { get; set; }
public ICommand ScanCompletedCommand { get; private set; }
}
Consume this by declaring a TextBox like so (where local is the namespace of your attached property, and ScanCompleted is an ICommand on your view-model):
<TextBox local:ScanReading.ScanCompletedCommand="{Binding ScanCompleted}" />
Now when this property is set, we add the TextBox to a static collection along with its associated ICommand.
Each time a key is pressed, we check whether it is the Pause key. If it is, and if the TextBox is empty, we set a flag to true to start looking for the Enter key.
Now each time a key is pressed, we check whether it is the Enter key. If it is, we execute the command, passing in the TextBox.Text value, and reset the flag to false for that TextBox.
We've also added a handler for the TextBox.Unloaded event to clean up our event subscriptions and remove the TextBox from the static list.

Keypad decimal separator on a Wpf TextBox, how to?

I have a Wpf application with some textbox for decimal input.
I would that when I press "dot" key (.) on numeric keypad of pc keyboard it send the correct decimal separator.
For example, on Italian language the decimal separator is "comma" (,)...Is possible set the "dot" key to send the "comma" character when pressed?
Quick and dirty:
private void NumericTextBox_KeyDown(object sender, KeyEventArgs e) {
if (e.Key == Key.Decimal) {
var txb = sender as TextBox;
int caretPos=txb.CaretIndex;
txb.Text = txb.Text.Insert(txb.CaretIndex, System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator);
txb.CaretIndex = caretPos + 1;
e.Handled = true;
}
}
Although you may set the default converter locale in WPF as suggested by Mamta Dalal it is not enough to convert the "decimal" key press to the correct string. This code will display the correct currency symbol and date/time format on data-bound controls
//Will set up correct string formats for data-bound controls,
// but will not replace numpad decimal key press
private void Application_Startup(object sender, StartupEventArgs e)
{
//Among other settings, this code may be used
CultureInfo ci = CultureInfo.CurrentUICulture;
try
{
//Override the default culture with something from app settings
ci = new CultureInfo([insert your preferred settings retrieval method here]);
}
catch { }
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
//Here is the important part for databinding default converters
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(ci.IetfLanguageTag)));
//Other initialization things
}
I found that handling the previewKeyDown event window-wide is a little cleaner than textbox-specific (it would be better if it could be applied application-wide).
public partial class MainWindow : Window
{
public MainWindow()
{
//Among other code
if (CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator != ".")
{
//Handler attach - will not be done if not needed
PreviewKeyDown += new KeyEventHandler(MainWindow_PreviewKeyDown);
}
}
void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Decimal)
{
e.Handled = true;
if (CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator.Length > 0)
{
Keyboard.FocusedElement.RaiseEvent(
new TextCompositionEventArgs(
InputManager.Current.PrimaryKeyboardDevice,
new TextComposition(InputManager.Current,
Keyboard.FocusedElement,
CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator)
) { RoutedEvent = TextCompositionManager.TextInputEvent});
}
}
}
}
If anybody could come up with a way to set it application-wide...

Canceling TextBox input on validation error in WPF

I'm trying to figure out how to cancel user input in a TextBox when a validation error occurs. If the user attempts to enter an invalid character I would like to prevent it from being added to the TextBox.
How can I add to or modify the code below to prevent the TextBox from accepting invalid characters? Is it possible without listening to the TextBox.TextChanged event?
My TextBox looks like:
<TextBox Validation.Error="OnSomeTextBoxValidationError">
<TextBox.Text>
<Binding Path="Value" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:SomeValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
My custom validation rule looks like:
public class SomeValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
string hex_string = value as string;
Match invalid_chars = Regex.Match(hex_string, "[^0-9a-fA-F]");
bool is_valid = (invalid_chars.Success == false);
string error_context = null;
if (is_valid == false)
{
error_context = "Invalid characters";
}
return new ValidationResult(is_valid, error_context);
}
}
I have an error handler... can I do anything with it?
private void OnSomeTextBoxValidationError(object sender, ValidationErrorEventArgs e)
{
// Can I do anything here?
}
Please provide an original answer if possible, rather than referring to a URL. I've read a lot of possible solutions involving event handlers, but I haven't come across anyone discussing the possibility of doing all my validation in the ValidationRule.
After a lot of research it seems that the only way to have full control over the input to a TextBox is to handle several events directly. According to WPF Recipes in C# 2008 (1st ed., p. 169):
Unfortunately, there's no easy way (at present) to combine the useful, high-level data binding feature with the lower-level keyboard handling that would be necessary to prevent the user from typing invalid characters altogether.
Here's what I came up with to create a hexadecimal numeric TextBox which only accepts characters a-f, A-F and 0-9.
SomeClass.xaml
<TextBox
x:Name="SomeTextBox"
LostFocus="TextBoxLostFocus"
PreviewKeyDown="TextBoxPreviewKeyDown"
PreviewTextInput="TextBoxPreviewTextInput" />
SomeClass.xaml.cs
private string mInvalidCharPattern = "[^0-9a-fA-F]";
// In my case SomeClass derives from UserControl
public SomeClass()
{
DataObject.AddPastingHandler(
this.SomeTextBox,
new DataObjectPastingEventHandler(TextBoxPasting));
}
private void TextBoxLostFocus(object sender, RoutedEventArgs e)
{
// You may want to refresh the TextBox's Text here. If the user deletes
// the contents of the TextBox and clicks off of it, then you can restore
// the original value.
}
// Catch the space character, since it doesn't trigger PreviewTextInput
private void TextBoxPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space) { e.Handled = true; }
}
// Do most validation here
private void TextBoxPreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (ValidateTextInput(e.Text) == false) { e.Handled = true; }
}
// Prevent pasting invalid characters
private void TextBoxPasting(object sender, DataObjectPastingEventArgs e)
{
string lPastingText = e.DataObject.GetData(DataFormats.Text) as string;
if (ValidateTextInput(lPastingText) == false) { e.CancelCommand(); }
}
// Do the validation in a separate function which can be reused
private bool ValidateTextInput(string aTextInput)
{
if (aTextInput == null) { return false; }
Match lInvalidMatch = Regex.Match(aTextInput, this.mInvalidCharPattern);
return (lInvalidMatch.Success == false);
}
You've probably seen this already, but it's the simplest solution and has always worked for me. I catch the PreviewKeyDown event and..
<TextBox PreviewKeyDown="TextBox_PreviewKeyDown" Width="150" Height="30"></TextBox>
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
... validation here, eg. to stop spacebar from being pressed, you'd use:
if (e.Key == Key.Space) e.Handled = true;
}
I'm using this for a Windows Phone Runtime 8.1 app to allow only certain characters:
<TextBox x:Name="TextBoxTitle"
MaxLength="24"
InputScope="AlphanumericHalfWidth"
TextChanged="TextBoxTitle_TextChanged"
KeyUp="TextBoxTitle_KeyUp"
Paste="TextBoxTitle_Paste"/>
using System.Text.RegularExpressions;
bool textBoxTitle_TextPasted = false;
private void TextBoxTitle_Paste(object sender, TextControlPasteEventArgs e)
{
textBoxTitle_TextPasted = true;
}
// only allow characters A-Z, a-z, numbers and spaces
private void TextBoxTitle_TextChanged(object sender, TextChangedEventArgs e)
{
string fileNameCompatibleString = Regex.Replace(TextBoxTitle.Text, "[^a-zA-Z0-9\x20]", String.Empty);
if (TextBoxTitle.Text != fileNameCompatibleString)
{
if (textBoxTitle_TextPasted)
{
TextBoxTitle.Text = fileNameCompatibleString;
TextBoxTitle.SelectionStart = fileNameCompatibleString.Length;
}
else
{
int selectionStartSaved = TextBoxTitle.SelectionStart;
TextBoxTitle.Text = fileNameCompatibleString;
TextBoxTitle.SelectionStart = selectionStartSaved-1;
}
}
textBoxTitle_TextPasted = false;
}
// close SIP keyboard on enter key up
private void TextBoxTitle_KeyUp(object sender, KeyRoutedEventArgs e)
{
if (e.Key == Windows.System.VirtualKey.Enter)
{
Windows.ApplicationModel.Core.CoreApplication.GetCurrentView().CoreWindow.IsInputEnabled = false;
Windows.ApplicationModel.Core.CoreApplication.GetCurrentView().CoreWindow.IsInputEnabled = true;
e.Handled = true;
}
}
Going after the TextBox PreviewKeyUp event worked well. Captured the current text in the text box by casting the sender as a TextBox. Then used a RegEx replace to replace invalid characters. Could also add some tool tip text in here as well, but for now this removes invalid characters and turns the background red for instant user feedback.

Resources