views:

28

answers:

2

I have found this link about similar problem, except mine has the added twist of the textbox needing to have a formatted value after focus is lost.

<TextBox Text="{Binding Value}" MaxLength="{Binding MaskLength}"/>
<Button Command="{Binding ExecuteCommand}" IsDefault="True"/>

After the user enters text into the textbox I would like to format it with a user defined mask. For example, if the mask is "00000" the convention would be to right justify and zero fill.

123 => 00123
A01 => 00A01
etc...

The problem that I'm running into is that I should only format the text once, when the user has finished typing. Currently, if the user clicks on the button the value the user entered is pushed to the binding and the command is executed. However, if they press the 'Enter' key the value is not pushed to the binding and the command is still executed.

The only way I've found out how to push the binding when the user presses the enter key is to change the textbox's binding and specify UpdateSourceTrigger=PropertyChanged. This does not work nicely because I don't actually know when the user has finished entering their text.

The easiest solution would be to add codebehind to set the button's focus when the enter key is pressed, however I would like focus to remain at the text box they are currently on. Does anybody have a way around this, maybe an attached property?

EDIT:

Here is a brief example of how my viewmodel is formatting the inputted value.

public string Value
{
    get
    {
        return mFieldValue;
    }
    set
    {
        SetValueAndRaisePropertyChange( 
            ref mFieldValue, 
            _ApplyFormat( value ), 
            () => FieldValue );
    }
}

Here is the cleanest code behind hack that I've found. I converted this into an attached property but it still doesn't smell quite right.

private void _HandleTxtKeyDown( object sender, KeyEventArgs e )
{
    if( e.Key == Key.Enter )
    {
        TextBox textBox = (TextBox)sender;
        BindingExpression binding = textBox.GetBindingExpression( TextBox.TextProperty );
        if( binding != null )
        {
            binding.UpdateSource();
        }
    }
}
A: 

If I understood your intention the right way, there is infact no problem.

You only want to mask the input if the user presses the enter key/clicks the button right? Then the solution you already found (UpdateSourceTrigger=PropertyChanged) will do exactly what you intend. The Property in your VM is updated while you are typing. The command will not be executed until you press enter/click the button if you use the PropertyChanged trigger.

The default UpdateSourceTrigger is 'LostFocus'. This of course also happens if you click the button with the mouse, not when pressing enter.

Hope i could help you understanding the mechanism.

JanW
Setting it to PropetyChanged does update the source but the formatting is in correctly. Say I want to format the value 'ab1' as '00AB1'. If set to PropertyChanged when the user hits 'a' the value will go to '0000A'. Now the user hits 'b' and it it ignored because the max length is reached.
Jerod Houghtelling
No its not. See my next answer to see the completely working code (tested)
JanW
A: 

You are not right. If the command updates the text, it is not executed while typing, until you press enter/click the button.

The view:

<Window x:Class="WpfApplication9.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <TextBox MaxLength="{Binding Path=MyLength}" 
                 Text="{Binding Path=MyText, UpdateSourceTrigger=PropertyChanged}"/>

        <Button Command="{Binding Path=MyCommand}" IsDefault="True">Enter</Button>
    </StackPanel>
 </Window>

The viewmodel, which is assigned as the Window's DataContext in constructor:

public class MyViewModel : INotifyPropertyChanged
{
    private string _myText = string.Empty;
    private int _maxLength = 6;
    private ICommand _myCommand;

    public MyViewModel()
    {
        this._myCommand = new MySimpleCommand((obj) => { FormatMyText(); });
    }

    private void FormatMyText()
    {
        this.MyText = this.MyText.PadLeft(this.MyLength, '0');
    }

    public string MyText
    {
        get { return this._myText; }
        set
        {
            this._myText = value;
            PropertyChanged(this, new PropertyChangedEventArgs("MyText"));
        }
    }

    public int MyLength
    {
        get { return this._maxLength; }
        set { }
    }

    public ICommand MyCommand
    {
        get { return this._myCommand; }
        set { }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public class MySimpleCommand : ICommand
    {
        private Action<object> _action;

        public MySimpleCommand(Action<object> cmdAction)
        {
            this._action = cmdAction;
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            _action(parameter);
        }

    }
}
JanW
P.S: I suggest you to put breakpoints in the setter of the MyText property and in the FormatMyText() method.Hope I could help you.
JanW
Yes, that works. However, my command is not doing the formatting. The Value property is doing the formatting when the binding is updated (OnLostFocus[default] or PropertyChanged).
Jerod Houghtelling