How to 'format' text box values in WPF - wpf

Is there an easy way to have a text box display a format in a text box and remove it when editing ?
Example : I want to ask for a money value. When the user finished entering the value 5.5, it displays $ 5.00 . When the user reclick on the textbox, it should switch back to 5.5 .
Thanks you.
Edit : What I am trying to do would be the equivalent of a converter except the textbox does not have a binding set to it.

There's a couple of solutions for this. Perhaps the easiest would be to implement a custom Behavior (you'll need to reference System.Windows.Interactivity.dll) & hook onto some events TextBox exposes, like GotFocus and LostFocus. Here's a sample I've done for selecting all of a TextBox's text when it gets focused:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace TextBoxUtility
{
public class TextBoxSelectAllOnFocusBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.PreviewMouseLeftButtonDown += OnPreviewMouseLeftButtonDown;
this.AssociatedObject.GotKeyboardFocus += OnSelectAll;
this.AssociatedObject.MouseDoubleClick += OnSelectAll;
}
protected override void OnDetaching()
{
this.AssociatedObject.PreviewMouseLeftButtonDown -= OnPreviewMouseLeftButtonDown;
this.AssociatedObject.GotKeyboardFocus -= OnSelectAll;
this.AssociatedObject.MouseDoubleClick -= OnSelectAll;
base.OnDetaching();
}
private static void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var textbox = sender as TextBox;
if (textbox != null && !textbox.IsKeyboardFocusWithin)
{
textbox.Focus();
e.Handled = true;
}
}
private static void OnSelectAll(object sender, RoutedEventArgs e)
{
var textbox = sender as TextBox;
if (textbox != null)
{
textbox.SelectAll();
}
}
Edit: Forgot to show how to hook the behavior up to the TextBox.
To attach the Behavior, in your XAML you'll need to add a namespace reference to your Behavior's namespace, and then attach it as a child of the TextBox markup like so:
<TextBox Text="{Binding ....}">
<i:Interaction.Behaviors>
<utility:TextBoxSelectAllOnFocusBehavior />
</i:Interaction.Behaviors>
</TextBox>
You could do something similar, though with yours you'd want to hook up to the LostFocus-related events and adjust the contents of the TextBox's Text property.

Related

Set maximum number of characters for MahApps NumericUpDown control

I am using the NumericUpDown control from MahApps, because of its +/- buttons and the ability to set the maximum/minimum allowed value. I now need to somehow limit the number of character, that can be input to the box.
For a standard TextBox this would be done with the MaxLength property, but this property does not exist for the NumericUpDown control.
Am I missing something? Is there some other way to achieve this?
No, there is no such property, but you can easily extend NumericUpDown and add it. BTW, when you focus out, it checks the Maximum allowed value and the input value.
C#
using MahApps.Metro.Controls;
using System.Windows;
using System.Windows.Input;
namespace TestApp.Controls
{
class ExtendedNumericUpDown : NumericUpDown
{
public int MaxLenght
{
get { return (int)GetValue(MaxLenghtProperty); }
set { SetValue(MaxLenghtProperty, value); }
}
public static readonly DependencyProperty MaxLenghtProperty =
DependencyProperty.Register(nameof(MaxLenght), typeof(int), typeof(ExtendedNumericUpDown), new PropertyMetadata(10));
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
e.Handled = ((System.Windows.Controls.TextBox)e.OriginalSource).Text.Length >= MaxLenght;
base.OnPreviewTextInput(e);
}
}
}
XAML
<ctrl:ExtendedNumericUpDown Minimum="0" Maximum="100" MaxLenght="3"/>
Building on #Alex's answer, you can take it one step further by creating a behavior. The behavior can be used by many different types of controls and you don't need to subclass NumericUpDown
public class MaxCharactersBehavior : Behavior<UIElement>
{
public int MaxCharacters { get; set; }
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewTextInput += AssociatedObject_PreviewTextInput;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewTextInput -= AssociatedObject_PreviewTextInput;
}
private void AssociatedObject_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = ((System.Windows.Controls.TextBox)e.OriginalSource).Text.Length >= MaxCharacters;
}
}
<mah:NumericUpDown Width="150" Maximum="999" Minimum="0">
<i:Interaction.Behaviors>
<behaviors:MaxCharactersBehavior MaxCharacters="3" />
</i:Interaction.Behaviors>
</mah:NumericUpDown>
The NumericUpDown control accepts Maximum and Minimum arguments,
<Controls:NumericUpDown Minimum="0" Maximum="{Binding TotalPages}"/>
If you can't find them, try to update the NuGet package of MahApps.

How to set focus on AND SELECT ALL of an initial text box (MVVM-Style)?

I have a simple WPF page with one text box field that my client wants highlighted when the page shows up. In code behind, it would be three lines, but I'm sogging through MVVM (which I'm starting to think is a little over-rated). I've tried so many different variants of behaviors and global events and FocusManager.FocusedElement, but nothing I do will do this.
Ultimately the most of the code I've been using calls these two lines:
Keyboard.Focus(textBox);
textBox.SelectAll();
But no matter where I put these lines the text box is only focused; no text is selected. I have never had this much trouble with something so simple. I've been hitting my head against the internets for two hours. Does anyone know how to do this?
Again, all I want to do is have the text box focus and it's text all selected when the page is navigated to. Please help!
"Focus" and "Select All Text from a TextBox" is a View-specific concern.
Put that in code Behind. It does not break the MVVM separation at all.
public void WhateverControl_Loaded(stuff)
{
Keyboard.Focus(textBox);
textBox.SelectAll();
}
If you need to do it in response to a specific application/business logic. Create an Attached Property.
Or:
have your View resolve the ViewModel by:
this.DataContext as MyViewModel;
then create some event in the ViewModel to which you can hook:
public class MyViewModel
{
public Action INeedToFocusStuff {get;set;}
public void SomeLogic()
{
if (SomeCondition)
INeedToFocusStuff();
}
}
then hook it up in the View:
public void Window_Loaded(Or whatever)
{
var vm = this.DataContext as MyViewModel;
vm.INeedToFocusStuff += FocusMyStuff;
}
public void FocusMyStuff()
{
WhateverTextBox.Focus();
}
See how this simple abstraction keeps View related stuff in the View and ViewModel related stuff in the ViewModel, while allowing them to interact. Keep it Simple. You don't need NASA's servers for a WPF app.
And no MVVM is not overrated, MVVM is extremely helpful and I would say even necessary. You'll quickly realize this as soon as you begin working with ItemsControls such as ListBoxes or DataGrids.
Here are some workthroughs:
Use Interaction.Behaviors
You can install the NuGet package named Microsoft.Xaml.Behaviors.Wpf, and write your own Behavior:
using Microsoft.Xaml.Behaviors;
using System.Windows;
using System.Windows.Controls;
public class AutoSelectAllBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.GotFocus += AssociatedObject_GotFocus;
}
private void AssociatedObject_GotFocus(object sender, RoutedEventArgs e)
{
if (AssociatedObject is TextBox box)
box.SelectAll();
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.GotFocus -= AssociatedObject_GotFocus;
}
}
and attach this behavior to the TextBox in the xaml:
<!-- xmlns:i="http://schemas.microsoft.com/xaml/behaviors" -->
<TextBox>
<i:Interaction.Behaviors>
<br:AutoSelectAllBehavior />
</i:Interaction.Behaviors>
</TextBox>
Use Interaction.Triggers
This is in the same package as mentioned in the last section. This special can be considered to let you be able to bind UIElement events to your ViewModel.
In your ViewModel, suppose you have an ICommand relay command (You may also need Microsoft.Toolkit.MVVM so that you can use some handy relay commands):
public ICommand SelectAllCommand { get; }
public ViewModel()
{
SelectAllCommand = new RelayCommand<TextBox>(box => box.SelectAll());
}
and then attach this command to the TextBox by setting the triggers:
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding SelectAllCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=TextBox}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Use Attached Property
You can also use attached property (write your own class derived from TextBox and use dependency property is quite similar):
using System.Windows;
using System.Windows.Controls;
public class TextBoxProperties
{
public static bool GetAutoSelectAll(DependencyObject obj)
{
return (bool)obj.GetValue(AutoSelectAllProperty);
}
public static void SetAutoSelectAll(DependencyObject obj, bool value)
{
obj.SetValue(AutoSelectAllProperty, value);
}
public static readonly DependencyProperty AutoSelectAllProperty =
DependencyProperty.RegisterAttached("AutoSelectAll", typeof(bool), typeof(TextBoxProperties), new PropertyMetadata(false, TextBoxProperties_PropertyChanged));
private static void TextBoxProperties_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
if (d is TextBox box)
{
box.GotFocus += TextBox_GotFocus;
}
}
}
private static void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
var box = sender as TextBox;
box.SelectAll();
}
}
Then you can use it like:
<!-- xmlns:ap="..." -->
<TextBox ap:TextBoxProperties.AutoSelectAll="True" />

Setting Focus on TextBox from ViewModel in WPF

I am having a master window in which there are plenty of user control. and using navigation i am able to access the user controls. But by question is how to set focus on the first text box when ever the user control is opened.
I tried with dependency property and boolean flags, i was able to succeeded a bit. When i first render the UserControl i was able to focus but when i open for the second time i was not able to set focus on the TextBox.
And one more thing, i have validation for TextBoxes, if the validation fails then the textbox should be emptied and the focus should be on the respective text box.
How can i achieve this using MVVM in WPF (CLR 3.5, VS2008)
thanks in advance.
If you have a UserControl then you also have CodeBehind.
Place this inside your codebehind and you will do fine.
this.Loaded += (o, e) => { Keyboard.Focus(textBox1) }
Place this inside your UserControl XAML if you wish to listen to validation errors.
<UserControl>
<Grid Validation.Error="OnValidationError">
<TextBox Text{Binding ..., NotifyOnValidationError=true } />
</Grid>
<UserControl>
Inside the CodeBehind of your UserControl you will have something like this:
public void OnValidationError(o , args)
{
if(o is TextBox)
{
(TextBox)o).Text = string.Empty;
}
}
You should use AttachedProperty to stick to MVVM pattern it'll keep your view model independent of UI code and fully unit testable. Following attached property binds a boolean property to focus and highlight the TextBox, if you do not want the highlighting then you can remove the highlighting code and just work with focus code.
public class TextBoxBehaviors
{
#region HighlightTextOnFocus Property
public static readonly DependencyProperty HighlightTextOnFocusProperty =
DependencyProperty.RegisterAttached("HighlightTextOnFocus", typeof (bool), typeof (TextBoxBehaviors),
new PropertyMetadata(false, HighlightTextOnFocusPropertyChanged));
public static bool GetHighlightTextOnFocus(DependencyObject obj)
{
return (bool) obj.GetValue(HighlightTextOnFocusProperty);
}
public static void SetHighlightTextOnFocus(DependencyObject obj, bool value)
{
obj.SetValue(HighlightTextOnFocusProperty, value);
}
private static void HighlightTextOnFocusPropertyChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
var uie = sender as UIElement;
if (uie == null) return;
if ((bool) e.NewValue)
{
uie.GotKeyboardFocus += OnKeyboardFocusSelectText;
uie.PreviewMouseLeftButtonDown += OnMouseLeftButtonDownSetFocus;
}
else
{
uie.GotKeyboardFocus -= OnKeyboardFocusSelectText;
uie.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDownSetFocus;
}
}
private static void OnKeyboardFocusSelectText(object sender, KeyboardFocusChangedEventArgs e)
{
var textBox = sender as TextBox;
if (textBox == null) return;
textBox.SelectAll();
}
private static void OnMouseLeftButtonDownSetFocus(object sender, MouseButtonEventArgs e)
{
var textBox = sender as TextBox;
if (textBox == null) return;
if (!textBox.IsKeyboardFocusWithin)
{
textBox.Focus();
e.Handled = true;
}
}
#endregion
}
You can use this attached property in on your TextBox which you want to focus/highlight...
<TextBox ... local:TextBoxBehaviors.HighlightTextOnFocus="{Binding IsScrolledToEnd}" ... />
You can also try using FocusManager
<UserControl>
<Grid FocusManager.FocusedElement="{Binding Path=FocusedTextBox, ElementName=UserControlName}">
<TextBox x:Name="FocusedTextBox" />
</Grid>
<UserControl>

Hook up right click events to all textbox in silverlight

Is there anyway to add right click events to all textbox controls in silverlight without needing to manually adding it to each control in the whole project?
doing like:
<TextBox x:Name="txtName" MouseRightButtonUp="txtName_MouseRightButtonUp"
MouseRightButtonDown="txtName_MouseRightButtonDown" /></TextBox>
then fixing the events in the .cs for about 50+ (hopefully it's just 50+) textboxes can take a while.
If not then what might be the easiest way to do this?
You can extend your textbox
class SimpleTextBox
{
public SimpleTextBox()
{
DefaultStyleKey = typeof (SimpleCombo);
MouseRightButtonDown += OnMouseRightButtonDown;
}
private void OnMouseRightButtonDown(object sender, MouseButtonEventArgs
mouseButtonEventArgs)
{
//TODO something
}
}
==========
And use this control.
Or as alternative solution - you can create behavior:
CS:
...
using System.Windows.Interactivity;
public class TextBoxBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseRightButtonDown += AssociatedObject_MouseRightButtonDown;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseRightButtonDown -= AssociatedObject_MouseRightButtonDown;
}
private void OnMouseRightButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
e.Handled = true;
// DO SOMETHING
}
}
XAML:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<TextBox ...>
<i:Interaction.Behaviors>
<local:TextBoxBehavior />
</i:Interaction.Behaviors>
</TextBox>
And attach this handler to your TextBox general style.
My answer to this question is also the answer to your question.
In short it's probably easiest to derive a type from TextBox, put your MouseRightButtonDown event handler in there and replace all existing instances of textBox with your type.

Workaround for UpdateSourceTrigger LostFocus on Silverlight Datagrid?

I have a Silverlight 2 application that validates data OnTabSelectionChanged. Immediately I began wishing that UpdateSourceTrigger allowed more than just LostFocus because if you click the tab without tabbing off of a control the LINQ object is not updated before validation.
I worked around the issue for TextBoxes by setting focus to another control and then back OnTextChanged:
Private Sub OnTextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
txtSetFocus.Focus()
sender.Focus()
End Sub
Now I am trying to accomplish the same sort of hack within a DataGrid. My DataGrid uses DataTemplates generated at runtime for the CellTemplate and CellEditingTemplate. I tried writing the TextChanged="OnTextChanged" into the TextBox in the DataTemplate, but it is not triggered.
Anyone have any ideas?
You can do it with a behavior applied to the textbox too
// xmlns:int is System.Windows.Interactivity from System.Windows.Interactivity.DLL)
// xmlns:behavior is your namespace for the class below
<TextBox Text="{Binding Description,Mode=TwoWay,UpdateSourceTrigger=Explicit}">
<int:Interaction.Behaviors>
<behavior:TextBoxUpdatesTextBindingOnPropertyChanged />
</int:Interaction.Behaviors>
</TextBox>
public class TextBoxUpdatesTextBindingOnPropertyChanged : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.TextChanged += new TextChangedEventHandler(TextBox_TextChanged);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.TextChanged -= TextBox_TextChanged;
}
void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var bindingExpression = AssociatedObject.GetBindingExpression(TextBox.TextProperty);
bindingExpression.UpdateSource();
}
}
This blog post shows how to update the source of a textbox explicitly using attached property:
http://www.thomasclaudiushuber.com/blog/2009/07/17/here-it-is-the-updatesourcetrigger-for-propertychanged-in-silverlight/
You could easily modify it to work with other controls as well...
I ran into this same problem using MVVM and Silverlight 4. The problem is that the binding does not update the source until after the textbox looses focus, but setting focus on another control doesn't do the trick.
I found a solution using a combination of two different blog posts. I used the code from Patrick Cauldwell's DefaultButtonHub concept, with one "SmallWorkaround" from SmallWorkarounds.net
http://www.cauldwell.net/patrick/blog/DefaultButtonSemanticsInSilverlightRevisited.aspx
www.smallworkarounds.net/2010/02/elementbindingbinding-modes.html
My change resulted in the following code for the DefaultButtonHub class:
public class DefaultButtonHub
{
ButtonAutomationPeer peer = null;
private void Attach(DependencyObject source)
{
if (source is Button)
{
peer = new ButtonAutomationPeer(source as Button);
}
else if (source is TextBox)
{
TextBox tb = source as TextBox;
tb.KeyUp += OnKeyUp;
}
else if (source is PasswordBox)
{
PasswordBox pb = source as PasswordBox;
pb.KeyUp += OnKeyUp;
}
}
private void OnKeyUp(object sender, KeyEventArgs arg)
{
if (arg.Key == Key.Enter)
if (peer != null)
{
if (sender is TextBox)
{
TextBox t = (TextBox)sender;
BindingExpression expression = t.GetBindingExpression(TextBox.TextProperty);
expression.UpdateSource();
}
((IInvokeProvider)peer).Invoke();
}
}
public static DefaultButtonHub GetDefaultHub(DependencyObject obj)
{
return (DefaultButtonHub)obj.GetValue(DefaultHubProperty);
}
public static void SetDefaultHub(DependencyObject obj, DefaultButtonHub value)
{
obj.SetValue(DefaultHubProperty, value);
}
// Using a DependencyProperty as the backing store for DefaultHub. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DefaultHubProperty =
DependencyProperty.RegisterAttached("DefaultHub", typeof(DefaultButtonHub), typeof(DefaultButtonHub), new PropertyMetadata(OnHubAttach));
private static void OnHubAttach(DependencyObject source, DependencyPropertyChangedEventArgs prop)
{
DefaultButtonHub hub = prop.NewValue as DefaultButtonHub;
hub.Attach(source);
}
}
This should be included in some sort of documentation for Silverlight :)
I know it's old news... but I got around this by doing this:
Text="{Binding Path=newQuantity, UpdateSourceTrigger=PropertyChanged}"

Resources