2
votes

I have a UWP application which uses a PasswordBox control for obscured text entry. The valid characters for the password varies by instance, and is known by the ViewModel layer at runtime. I want to be able to filter invalid characters as the user types them. What is the proper way to do that?

We have similar requirements for regular TextBox controls. We have been able to perform this kind of filtering for text boxes using the TextChanging event and the SelectionStart property for resetting the cursor position. But the PasswordBox does not have either of these.

The XAML for the password box looks like this

    <PasswordBox
        x:Name="ThePasswordBox"
        Grid.Row="1"
        MaxLength="{Binding MaxPasswordLength, Mode=OneTime}"
        IsEnabled="{Binding IsPasscodeEnabled, Mode=OneWay}"
        Password="{Binding Password, FallbackValue=1234, Mode=TwoWay}"
        PlaceholderText= "{Binding PlaceholderText, Mode=OneWay}" />

We then respond to the Password.set by checking the validity of the input, and if invalid, resetting to the prior value

        set
        {
            if (_password != value && value != null)
            {
                // verify that the new password would be valid; if not, roll back
                if (!IsPasswordContentAcceptable(value))
                {
                    _passcodeControl.SetPassword(_password);
                    return;
                }

                _password = value;

                // push the new password to the data binding
                _passcodeDataBinding.SetCurrentValue(_password);

                // update the UI
                HandlePasswordChange();

                OnPropertyChanged("Password");
            }
        }

The call to SetCurrentValue() simply stores the entered password into our Model layer and should not be of consequence to this discussion. The call to _passwordControl.SetPassword updates the Password field on ThePasswordBox:

    public void SetPassword(string password)
    {
        ThePasswordBox.Password = password;
    }

HandlePasswordChange() forces other UI elements to re-evaluate, including an OK button that is disabling when the control is invalid. It's implementation should not be important to this question.

The problem with this approach is that when we reset the contents of the PasswordBox (the call to SetPassword, which sets PasswordBox.Password property) the cursor jumps to the first position. So for a numeric password, typing "12a4" would yield "412".

What are our options here?

2
Are you trying to do this for password creation? This isn't typically what people do... Just tell them their password is invalid when they go to submit. Don't confuse the user by removing characters on them... If the password needs to only be numeric you can set the keyboard type to numeric.visc
Yes, for password creation. Plan B is to do as you suggest, validating at submit time, but the current design is to filter out the bad characters as they are entered. Passwords are sometimes all numeric, but not always, so we need a general keyboard (also the app needs to run on a PC)Rich S
I could not reproduce your issue with above code , it missed _passcodeControl.SetPassword and _passcodeDataBinding.SetCurrentValue method. please share minimal reproducible example for us.Nico Zhu - MSFT
@RichS how can you ask the user to enter a password they cannot see and remove characters from what they enter? Or will you enable them to see what they are entering?visc
@visc - we have a "reveal" button on the password control that allows them to view the characters they have enteredRich S

2 Answers

1
votes

What's the proper way to filter characters in a UWP PasswordBox?

From your code, you need not call SetPassword method in Password.set, it will make the cursor back the the start position. the better way is bind Password within two way mode. And check the if Password is available. If the Password contains unallowable character then call BackSpace with InputInjector.

public string PassWord
{

    get { return _passWord; }
    set
    {
        if(value != null && IsPasswordContentAcceptable(value))
        {
            _passWord = value;
            OnPropertyChanged();
        }
        else
        {
            InputInjector inputInjector = InputInjector.TryCreate();
            var info = new InjectedInputKeyboardInfo();
            info.VirtualKey = (ushort)(VirtualKey.Back);
            inputInjector.InjectKeyboardInput(new[] { info });               
        }

    }
}

Xaml

<PasswordBox
    x:Name="ThePassWordBox"
    MaxLength="20"
    Password="{x:Bind PassWord, Mode=TwoWay}"      
    PlaceholderText="Input your Password"
    />
0
votes

Alternatively, you can suppress input as it's being received. In this example, all characters are allowed in the PasswordBox except for "A"

Xaml

    <PasswordBox Name="pwBox"></PasswordBox>

Code

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        CoreWindow cw = CoreWindow.GetForCurrentThread();
        CoreDispatcher cd = cw.Dispatcher;

        cd.AcceleratorKeyActivated += Cd_AcceleratorKeyActivated;
        this.InitializeComponent();
    }

    private void Cd_AcceleratorKeyActivated(CoreDispatcher sender, AcceleratorKeyEventArgs args)
    {
        if (this.pwBox.FocusState != FocusState.Unfocused)
        {
            if(args.VirtualKey == Windows.System.VirtualKey.A)
            {
                args.Handled = true;
            }
        }
    }
}