Allow only numeric entry in WPF Text Box - c#

I will like to validate user entry to ensure they are integers. How can I do it? I thought of using IDataErrorInfo which seems like the "correct" way to do validation in WPF. So I tried implementing it, in my ViewModel.
But the thing is my text box is bound to an integer field, and there isn't any need to validate if an int is an int. I noticed that WPF automatically adds a red border around the textbox to notify the user of the error. The underlying property doesn't change to an invalid value. But I would like to notify the user of this. How can I do it?

Another way is simply to not allow values that are not integers.
The following implementation is a little bit sucky, and I would like to abstract it later on in order for it to be more reusable, but here is what I did:
in the code behind in my view (I know this is might hurt if you are a hardcore mvvm ;o) )
I defined the following functions :
private void NumericOnly(System.Object sender, System.Windows.Input.TextCompositionEventArgs e)
{
e.Handled = IsTextNumeric(e.Text);
}
private static bool IsTextNumeric(string str)
{
System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("[^0-9]");
return reg.IsMatch(str);
}
And in the XAML view, every textbox that was only supposed to accept integers
was defined like this:
<TextBox Padding="2" TextAlignment="Right" PreviewTextInput="NumericOnly" Text="{Binding xxx.yyyy}" MaxLength="1" />
The key attribute being PreviewTextInput

The red border you've seen is actually a ValidationTemplate, which you can extend and add a info for the user. See this example:
<UserControl.Resources>
<ControlTemplate x:Key="validationTemplate">
<Grid>
<Label Foreground="Red" HorizontalAlignment="Right" VerticalAlignment="Center">Please insert a integer</Label>
<Border BorderThickness="1" BorderBrush="Red">
<AdornedElementPlaceholder />
</Border>
</Grid>
</ControlTemplate>
</UserControl.Resources>
<TextBox Name="tbValue" Validation.ErrorTemplate="{StaticResource validationTemplate}">

We can do validation on text box changed event.
The following implementation prevents keypress input other than numeric and one decimal point.
private void textBoxNumeric_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = sender as TextBox;
Int32 selectionStart = textBox.SelectionStart;
Int32 selectionLength = textBox.SelectionLength;
String newText = String.Empty;
int count = 0;
foreach (Char c in textBox.Text.ToCharArray())
{
if (Char.IsDigit(c) || Char.IsControl(c) || (c == '.' && count == 0))
{
newText += c;
if (c == '.')
count += 1;
}
}
textBox.Text = newText;
textBox.SelectionStart = selectionStart <= textBox.Text.Length ? selectionStart : textBox.Text.Length;
}

if you are working in WPF Better to use PreviewTextInput event which support all platforms and from presentationcore.dll
here is the example:
private void TextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
if ((e.Text) == null || !(e.Text).All(char.IsDigit))
{
e.Handled = true;
}
}

Here is how to create a numeric field in WPF, using the regular expressions.
XAML:
<TextBox PreviewTextInput="NumberValidationTextBox"></TextBox>
Code behind:
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
e.Handled = regex.IsMatch(e.Text);
}

Related

Set TextBox AccessText without an accompanying Label

I have a series of textboxes in a usercontrol that do not have accompanying Labels.
I need to provide a means for a user to press Alt+(Some Key) to set focus to each of these textboxes.
If I want to use the built in WPF "AccessText" way of doing this, I would need to put a Label up next to each textbox, specify the content with an '_' character preceding the shortcut key, and specify the "Target" property of each Label to their respective Textbox.
Unfortunately in this case, there are no Labels for each textbox, and there will not be.
Is it possible to specify the AccessText shortcut key for a textbox, without a Label?
you can Handle key combinations press and after that focus on the TextBox :
*.xaml:
<UserControl Loaded="UserControl_Loaded">
<Grid>
<StackPanel>
<TextBox Height="30" x:Name="txt1"/>
<TextBox Height="30" x:Name="txt2"/>
</StackPanel>
</Grid>
</UserControl>
*.cs
public UserControl()
{
InitializeComponent();
}
private void KeyDownEvent(object sender, KeyEventArgs e)
{
bool x = Keyboard.IsKeyDown(Key.System);
if (Keyboard.IsKeyDown(Key.System) && Keyboard.IsKeyDown(Key.B))// Alt+B
{
txt2.Focusable = true;
txt2.Focus();
}
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
var window = Window.GetWindow(this);
window.KeyDown += KeyDownEvent;
txt1.Focusable = true;
txt1.Focus();
}
}

What is the correct way to code the nested methods?

I have created a registration form in silverlight 4, where i have a large number of text-boxes, in front of each text box i have placed a text-block as a required field validator, when any of the textbox left empty while loosing focus, the textblock placed in front of it must become red.
textboxes named textbox1, textbox2 ... and so as the textblocks
the problem is, i do not want code the specific method for each specific textbox, all i want to do is to complete such in just two three methods
here i did some coding which doesn't seems to be correct
private void textBox_LostFocus(object sender, RoutedEventArgs e)
{
var textBox = (TextBox) sender;
if (textbox.Text == "")
{
var textblock = "textblock" + textBox.Name.Remove(0,7);
TextblockColorChange(textblock);
}
}
private void TextblockColorChange(object sender)
{
var textblock = (TextBlock) sender;
textblock.Foreground= new SolidColorBrush(Colors.Red);
}
please suggest some better way to do so..
I'd create a UserControl that contains the TextBlock and the TextBox and use this UserControl everywhere you currently have the TextBlock and TextBox combination. Then this Usercontrol would have the LostFocus logic inside it and update the TextBlock appropriately. This prevents the need to figure out the right name of the control to update.
you need something like this,
XAML part:
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Orientation="Horizontal" Height="25">
<TextBox Width="150" LostFocus="TextBox_LostFocus"/>
<TextBlock Text="*" Foreground="#FF0000" VerticalAlignment="Center" Margin="10,0,0,0" Visibility="Collapsed"/>
</StackPanel>
</Grid>
C# Part:
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
var textbox = sender as TextBox;
if(textbox == null) return;
var stackPanel = textbox.Parent as StackPanel;
if(stackPanel == null) return;
var textBlock = stackPanel.Children.Where(a => a is TextBlock).FirstOrDefault();
if (textBlock == null) return;
if (string.IsNullOrEmpty(textbox.Text)) textBlock.Visibility = Visibility.Visible;
else textBlock.Visibility = Visibility.Collapsed;
}
Whilst I actually prefer Bills approach (although I'd be inclined to use a Templated Control) here is another alternative which is quite fun. In your xaml use this sort of markup:-
<TextBlock Text="Enter Value 1" Foreground="{Binding Tag, ElementName=textBox1, TargetNullValue=Black}" />
<TextBox x:Name="textBox1" LostFocus="txt_LostFocus" />
Your common txt_LostFocus can look like this:-
private void txt_LostFocus(object sender, RoutedEventArgs e)
{
TextBox txt = ((TextBox)sender);
if (String.IsNullOrEmpty(txt.Text))
{
txt.Tag = new SolidColorBrush(Colors.Red);
}
else
{
txt.Tag = null;
}
}
var textblock = "textblock" + textBox.Name.Remove(0,7);
TextblockColorChange(textblock);
This code above will just send a string to TextblockColorChange()
You don't show any other code, but I'm guessing you want to do a FindControl or FindControl like search on that string before passing the result to your code.

WPF EditingCommands is not working when RichTextBox is just load/empty?

Here is a very simple code example:
<DockPanel>
<ToolBar DockPanel.Dock="Top" IsTabStop="False">
<ToggleButton MinWidth="40" Command="EditingCommands.ToggleBold" CommandTarget="{Binding ElementName=XAMLRichBox}" TextBlock.FontWeight="Bold" IsTabStop="False">B</ToggleButton>
</ToolBar>
<RichTextBox x:Name="XAMLRichBox" SpellCheck.IsEnabled="True" MinHeight="100"/>
</DockPanel>
when I run it, after typing something into the RichTextBox, I can use the ToggleButton to get the BOLD effect, and everything is fine.
But if I click ToggleButton before typing in anything into RichTextBox (no matter RichTextBox get focus or not), although ToggleButton became Checked, my RichTextBox still using the normal style (not BOLD) until I click ToggleButton again.
Is this a bug? how can I get around? Thanks!
Mainwindow.xaml
<DockPanel>
<ToolBar
DockPanel.Dock="Top"
IsTabStop="False">
<ToggleButton
x:Name="boldButton"
Command="EditingCommands.ToggleBold"
CommandTarget="{Binding ElementName=XAMLRichBox}"
TextBlock.FontWeight="Bold"
ToolTip="Bold">
B
</ToggleButton>
</ToolBar>
<RichTextBox
x:Name="XAMLRichBox"
SpellCheck.IsEnabled="True"
SelectionChanged="SynchronizeWith"
MinHeight="100" />
</DockPanel>
Mainwindow.xaml.cs
private void SynchronizeWith(object sender, RoutedEventArgs e)
{
object currentValue = XAMLRichBox.Selection.GetPropertyValue(TextElement.FontWeightProperty);
boldButton.IsChecked = (currentValue == DependencyProperty.UnsetValue) ? false : currentValue != null && currentValue.Equals(FontWeights.Bold);
}
I found a semi-solution and I thought I would share since this problem is not answered anywhere on the web and I think many people are having issues with it.
I set a Variable NewInput in the constructor. When the first input in the richTextBox will be fired, I'll just apply every formating I need to it and pass it to the control.
private bool NewInput;
private void richTxt_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (NewInput)
{
richTxt.BeginChange();
TextPointer startPosition = richTxt.Selection.Start;
Run r = new Run(e.Text, startPosition);
if (IsSelectionBold)
{
r.FontWeight = FontWeights.Bold;
}
if (IsSelectionItalic)
{
r.FontStyle = FontStyles.Italic;
}
if (IsSelectionUnderlined)
{
r.TextDecorations = TextDecorations.Underline;
}
r.FontSize = double.Parse(SelectedFontHeight);
r.FontFamily = new FontFamily(SelectedFont);
richTxt.EndChange();
NewInput = false;
e.Handled = true;
richTxt.CaretPosition = richTxt.CaretPosition.GetPositionAtOffset(1);
}
}
I then replace the carret at the right place. Like this, the formating is kept even if there is nothing in the RichTextBox.
I'm sure it'll help somebody one day.
#Sinity was close, but that solution does not work when the caret is placed within the text block, only if it is at the very end.
There are two positions at the end of a Run: Run.ContentEnd and Run.ElementEnd. It appears that the ContentEnd is "just outside" the run (so any new text entered does not take on the run's style), but ElementEnd is "just inside" the end of the run, and the typed text is added into the run.
Here is a modified solution (that applies a pending Bold style as an example) that seems to work in all cases:
private bool IsBoldStylePending { get; set; }
private void RichTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (!IsBoldStylePending)
return;
rtb.BeginChange();
Run run = new Run(e.Text, rtb.CaretPosition); // Add the next character with style
run.FontWeight = FontWeights.Bold;
rtb.EndChange();
rtb.CaretPosition = run.ElementEnd; // Keep caret just within the run
IsBoldStylePending = false;
e.Handled = true;
}

TextBox - Bound Property Name

I have a list of TextBoxes which are bound to different properties.
<TextBox Text="{Binding Name, Mode=TwoWay,ValidatesOnDataErrors=True, ValidatesOnExceptions=True, NotifyOnValidationError=True}" VerticalAlignment="Center" Margin="5" Width="300" Grid.Column="1" Grid.Row="1" LostFocus="TextBox_Validate"/>
I would like to write ONE handler such as
private void TextBox_Validate(object sender, RoutedEventArgs e)
{
var textBox = (sender as TextBox);
if(textBox!=null)
{
var propertyName = X; // Get propertyName textBox.Text is bound to.
CurrentDataContext.ValidateFields("Name"); // Name in this specific textBox
}
}
Is it possible to get the name of the property so I won't have to write many different methods that do the same thing?
I think this is what you want:
var expression = textBox.GetBindingExpression(TextBox.TextProperty);
if (expression != null && expression.ParentBinding != null)
{
var propertyName = expression.ParentBinding.Path.Path;
}
Edit
Or you can use BindingOperations.GetBinding as shown here. I'm not sure if one way is better than the other.
Name the TextBox in xaml, x:Name="MyTextBox", then you can check it, textBox.Name == "MyTextBox".

How do I know that a Silverlight control has been displayed?

I have a list box displaying the names of help topics which can be added to and the names of the topics changed. Originally it was just displaying strings, but to get the inline editing working I changed it to use a custom type consisting of a string and an InEdit property so the UI can determine whether to display the TextBlock or TextBox:
XAML:
<ListBox ItemsSource="{Binding HelpTopics, Mode=TwoWay}"
SelectedValuePath="Description"
SelectedValue="{Binding SelectedPageId, Mode=TwoWay}"
SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Description, Mode=TwoWay}"
VerticalAlignment="Center"
MouseLeftButtonUp="TopicTextBlock_MouseLeftButtonUp"
Visibility="{Binding InEdit, Converter={StaticResource boolToVisibilityConverter}, ConverterParameter=contra}"/>
<TextBox Text="{Binding Description, Mode=TwoWay}"
Visibility="{Binding InEdit, Converter={StaticResource boolToVisibilityConverter}, ConverterParameter=pro}"
LostFocus="EditTopicTextBox_LostFocus"
HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Margin="5" Content="Add Topic" Command="{Binding AddTopicCommand}"/>
HelpTopics is an ObservableCollection<EditableHelpTopic>.
SelectedPageId is a string.
boolToVisibilityConverter is a converter that does what it says.
What works:
Adding a topic creates a new item and adds it to the list and put the item in to edit mode.
Double clicking on an existing item puts that item into edit mode sets the focus to the TextBox and selects all the text so it can be overwritten.
When the TextBox loses focus the edit is saved and the display returns to the TextBlock.
What doesn't work:
When a new topic is added the TextBox should have focus and the text selected so the user can enter a new name.
So my question is is there a point in the code or an event where I know that the TextBox has been created and is visible so I can set focus and select its contents. I've tried hooking into the SelectionChanged event but when that fires the TextBox hasn't yet been displayed. I also added an event to the OnAddTopicExecute method in the view model which I handled in the view, but again that fired before the TextBox was visible.
Below is the code that supports the above XAML. I've tried to cut it down, but there still seems to be a lot of it, so you can skip this if you're not interested ;)
Code behind:
private DateTime lastClickTime = DateTime.MinValue;
private Point lastClickPosition;
private void TopicTextBlock_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
UIElement element = sender as UIElement;
if ((DateTime.Now - this.lastClickTime).TotalMilliseconds > 300)
{
this.lastClickPosition = e.GetPosition(element);
this.lastClickTime = DateTime.Now;
}
else
{
Point position = e.GetPosition(element);
if (Math.Abs(this.lastClickPosition.X - position.X) < 4 && Math.Abs(this.lastClickPosition.Y - position.Y) < 4)
{
var textBlock = sender as TextBlock;
var editableHelpTopic = textBlock.DataContext as EditableHelpTopic;
editableHelpTopic.InEdit = true;
var parent = textBlock.Parent as Grid;
TextBox textBox = parent.Children.First(c => c.GetType() == typeof(TextBox)) as TextBox;
textBox.Focus();
textBox.SelectAll();
}
}
}
private void EditTopicTextBox_LostFocus(object sender, RoutedEventArgs e)
{
var textBox = sender as TextBox;
var editableHelpTopic = textBox.DataContext as EditableHelpTopic;
editableHelpTopic.InEdit = false;
if (!textBox.Text.Equals(editableHelpTopic.Description))
{
this.editViewModel.RenameTopic(textBox.Text);
}
}
View Model:
public EditViewModel()
{
...
this.AddTopicCommand = new DelegateCommand(this.OnAddTopicExecute, this.OnAddTopicCanExecute);
...
}
where DelegateCommand is an implemetation of ICommand.
private void OnAddTopicExecute(object parameter)
{
var newTopic = new EditableHelpTopic
{
Description = "NewTopic",
InEdit = true
};
this.HelpTopics.Add(newTopic);
this.SelectedPageId = newTopic.Description;
}
Definitions:
public class EditableHelpTopic : INotifyPropertyChanged
{
public bool InEdit { ... }
public string Description { ... }
}
It turned out to be simpler than I thought.
I just needed to add a Loaded event handler to the TextBox:
private void EditTopicTextBox_Loaded(object sender, RoutedEventArgs e)
{
var textBox = sender as TextBox;
var editableHelpTopic = textBox.DataContext as EditableHelpTopic;
if (editableHelpTopic.InEdit)
{
textBox.Focus();
textBox.SelectAll();
}
}

Categories

Resources