2

I recently got a requirement that all the textboxes in my application should automatically trim the text when leaving the textbox. I can't/don't want to do this by adding converters or events to every single textbox, or by writing/rewriting the properties in my viewmodel for every single binding. First, it will be very messy and repetetive, second, I already have converters for many of my textboxes, and afaik, you can't connect multiple converters without writing a multiconverter.

Since you can't apply the trim at every key stroke, the trim must occur after the unfocus event but before the binding operation to my viewmodel.

What is the best way of doing this? Writing my own textbox class? Some kind of controltemplate/triggers? I'm not familiar enough with those options to know which path will be easiest.

I already have a style set up in App.xaml for all my textboxes which makes them look like IsEnabled=false when IsReadOnly is set to true, so that might complicate things.

Mårten
  • 231
  • 2
  • 14
  • 1
    Why not trim when you want to use the value? – default Nov 24 '15 at 10:10
  • If I had designed the whole system, that would have been a good option, but this application saves to a database used by other apps that assumes that the strings are trimmed. – Mårten Nov 24 '15 at 10:12
  • Then you could just trim the value before submitting the value to the database. – Basti Nov 24 '15 at 10:14
  • you can use attachedproperty. It will be repetitive but easy to maintain. Writing your own textbox control is a good way also – S. Gmiden Nov 24 '15 at 10:15
  • Basti, many of the textboxes are bound directly to an entity framework entity that is then saved to the database, so I don't need to handle them "manually", and I'd like to keep it that way. – Mårten Nov 24 '15 at 10:16
  • You can write a `AttachedProperty` and set it default style of text box so that all the text box are initialized with `AttachedProperty`. In attached property, you can trim the text either on `LostFocus` or `PreviewLostFocus` event on the attached control. – user1672994 Nov 24 '15 at 10:25
  • Instead of trimming the text you should simply set the [MaxLength](https://msdn.microsoft.com/en-us/library/system.windows.controls.textbox.maxlength(v=vs.110).aspx) property. From MSDN: *You can also use this property to restrict the length of text entered when the data is to be stored in a database so that the text entered into the control does not exceed the maximum length of the corresponding field in the database.* – Clemens Nov 24 '15 at 10:40
  • You could also use a converter to trim the values http://www.wpftutorial.net/ValueConverters.html – Ryan Searle Nov 24 '15 at 10:49
  • Clements, Trim doesn't do the same thing as maxlength, it removes whitespaces which is what I want. Ryan, I specifically mentioned converters and why they aren't an option. – Mårten Nov 24 '15 at 11:37

2 Answers2

2

Ok so I got a good solution working, here's my very simple solution:

I added my own control which inherits from TextBox, TrimmedTextBox. Constructor:

public TrimmedTextBox()
{
    InitializeComponent();
    LostFocus += TrimmedTextBox_LostFocus;
}

private void TrimmedTextBox_LostFocus(object sender, RoutedEventArgs e)
{
    Text = Text.Trim();
}

This is triggered before the binding does it's job, so what's saved to my properties are actually the trimmed value.

I also had to add modifications in all styles where TargetType=TextBox of course.

Mårten
  • 231
  • 2
  • 14
2

The MVVM way would be using Behaviors:

using System.Windows.Controls;
using System.Windows.Interactivity;

public class TrimTextBoxBehavior : Behavior<TextBox>
{
    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.LostFocus += AssociatedObject_LostFocus;

    }

    private void AssociatedObject_LostFocus(object sender, System.Windows.RoutedEventArgs e)
    {
        AssociatedObject.Text = AssociatedObject.Text.Trim();
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        AssociatedObject.LostFocus -= AssociatedObject_LostFocus;
    }
}

Then in your XAML:

<UserControl ...
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:local="clr-namespace:Your.Namespace;assembly=Your.Assembly">

    <TextBox>
        <i:Interaction.Behaviors>
            <local:TrimTextBoxBehavior />
        </i:Interaction.Behaviors>
    </TextBox>

Notice that you need to replaceYour.Namespace and Your.Assembly in the local XML namespace declaration. Also, you need to add System.Windows.Interactivy to your project.

Community
  • 1
  • 1
jnovo
  • 5,659
  • 2
  • 38
  • 56
  • Thank you for your answer, since I got it working with my solution, I will not use this immediately but I will look into it! – Mårten Nov 25 '15 at 06:31