Matthew Sanford
Matthew Sanford

Reputation: 1099

How to get the highlighted text in a PasswordBox

I have a password box that I am adding a "Show Password" button to (as shown) :

enter image description here

The relevant XAML for the password textbox looks like this:

                <Border VerticalAlignment="Center" Grid.Row="2" Margin="2" Grid.Column="1" 
                    BorderThickness="1" 
                    BorderBrush="Black" 
                    Background="White">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <PasswordBox Name="txtPassword" Grid.Column="0" MaxLength="100" Margin="2" BorderThickness="0" GotFocus="txtPassword_GotFocus" PasswordChanged="txtPassword_PasswordChanged" />
                    <TextBox Name="txtVisiblePassword" Grid.Column="0" MaxLength="100" Margin="2" BorderThickness="0" Visibility="Collapsed" Height="22" />
                    <Button Grid.Column="1" Style="{StaticResource LightHoverButton}" x:Name="btnShowPassword" PreviewMouseDown="btnShowPassword_PreviewMouseDown" PreviewMouseUp="btnShowPassword_PreviewMouseUp">
                        <Image Source="{StaticResource ShowPassword_128}" Style="{StaticResource Image_16}" Margin="3,0" />
                    </Button>
                </Grid>
            </Border>

The relevant code behind :

    private void txtPassword_PasswordChanged(object sender, RoutedEventArgs e)
    {
        //Can show password is set to false if the password being displayed has been 
        //placed there automatically because the password is being remembered
        if (canshowpassword == false && string.IsNullOrEmpty(txtPassword.Password))
            canshowpassword = true;
        if (canshowpassword)
            btnShowPassword.Visibility = string.IsNullOrEmpty(txtPassword.Password) ? Visibility.Collapsed : Visibility.Visible;
        else
            btnShowPassword.Visibility = Visibility.Collapsed;
    }

The problem I have is, there is a "remember password" option and I do not want the user to be able to see the password unless the password textbox has been "cleared" of all text at least once (to prevent the user from seeing all or part of a password that has automatically been put there). The above code is close, however, it has the problem that if the user selects all the text and then presses a key to begin a new password the "empty" password does not come through the password changed event and the "Show Password" button will not become visible. The user has to delete all the text, then start typing again. I would like to catch the case where the user selects all the text, and the starts typing.

If I Preview the Key down event there is no "SelectedText" property on the PasswordBox so I do not know how to know in advance if the password will be cleared before the new text is added.

Is there a way to know that the PasswordBox password has been cleared, and/or to know if all the text is selected?

Upvotes: 1

Views: 489

Answers (1)

SledgeHammer
SledgeHammer

Reputation: 7736

This one is a very tricky. They don't publicly expose that information, but you can get to it with some reflection. It's kinda nasty because they try to hide everything with internal classes and private properties, but here you go:

        TextSelection selection = (TextSelection)typeof(PasswordBox).GetProperty("Selection", BindingFlags.Instance | BindingFlags.NonPublic).GetMethod.Invoke(textBox, null);

        Type tTextRange = selection.GetType().GetInterfaces().Where(x => x.Name == "ITextRange").FirstOrDefault();

        object oStart = tTextRange.GetProperty("Start").GetMethod.Invoke(selection, null);
        object oEnd = tTextRange.GetProperty("End").GetMethod.Invoke(selection, null);

        MethodInfo mi = oStart.GetType().GetProperty("Offset", BindingFlags.Instance | BindingFlags.NonPublic).GetMethod;

        int nStart = (int)mi.Invoke(oStart, null);
        int nEnd = (int)mi.Invoke(oEnd, null);

        System.Diagnostics.Debug.WriteLine(nStart + " ==> " + nEnd);

You should, of course, cache some of the reflection stuff, but I'll leave that up to you :).

NOTE: you need to get start and end in textBox_PreviewKeyDown(). By the time you get to PasswordChanged, the selection has already been lost obviously. If you want to check the entire password is selected, you would check for 0 ==> len.

Upvotes: 3

Related Questions