如何在 WPF 密码框中显示几秒钟的字符?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6626937/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 22:58:02  来源:igfitidea点击:

How to show characters for a few seconds in a WPF password box?

wpftextboxdelaypasswordbox

提问by Simce

If the user enters 1985 in the password box, four bullets (●●●●) will be shown. How can I show each letter or number entered for a few seconds and after that change it to a bullet? I suppose that this can't be done in the password box, but is there any other way to do it?

如果用户在密码框中输入 1985,将显示四个项目符号 (●●●●)。如何显示输入的每个字母或数字几秒钟,然后将其更改为项目符号?我想这不能在密码框中完成,但是还有其他方法可以做到吗?

回答by Stewbob

Put a textbox on top of the password box, and then use a little databinding and animation. This chunk of XAML will allow the textbox to be visible as long as there is typing going on, but as soon as the typing stops, the textbox will fade away, leaving only the password box with the password characters showing.

在密码框上方放置一个文本框,然后使用一些数据绑定和动画。只要输入正在进行,这个 XAML 块将允许文本框可见,但是一旦输入停止,文本框就会消失,只留下显示密码字符的密码框。

    <Window.Resources>
        <Storyboard x:Key="Storyboard1">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="textBox">
                <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="1"/>
                <EasingDoubleKeyFrame KeyTime="0:0:1.2" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="TextBoxBase.TextChanged" SourceName="textBox">
            <StopStoryboard BeginStoryboardName="Storyboard1_BeginStoryboard"/>
            <BeginStoryboard x:Name="Storyboard1_BeginStoryboard" Storyboard="{StaticResource Storyboard1}"/>
        </EventTrigger>
    </Window.Triggers>




<PasswordBox x:Name="passwordBox"/>
<TextBox x:Name="textBox"
        Text="{Binding ElementName=passwordBox, Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
         Opacity="100"/>

You can play around with the KeyTimes in the animation to get the delay that you prefer. You can also change the font settings in the textbox to get the typed text and the password characters to line up better.

您可以在动画中使用 KeyTimes 来获得您喜欢的延迟。您还可以更改文本框中的字体设置,以使键入的文本和密码字符更好地对齐。

EDIT

编辑

If you want to only display the last character typed as clear text:

如果您只想显示以明文形式输入的最后一个字符:

This situation is a little different and requires some more complexity. this scenario uses only a textbox on the window, not the passwordbox.

这种情况有点不同,需要更多的复杂性。此方案仅使用窗口上的文本框,而不是密码框。

<TextBox Name="tbxPwd" Margin="20,0" 
         Text="{Binding Path=DisplayedPwd}" />

In your code-behind for the window (or in your ViewModel class) you will need two properties, ActualPwdand DisplayedPwd. The textbox is bound to the DisplayedPwdproperty.

在窗口的代码隐藏中(或在您的 ViewModel 类中),您将需要两个属性,ActualPwd以及DisplayedPwd. 文本框绑定到DisplayedPwd属性。

In the code-behind, you will need the following code:

在代码隐藏中,您将需要以下代码:

Private Sub tbxPwd_PreviewKeyDown(sender As Object, e As System.Windows.Input.KeyEventArgs) _
  Handles tbxPwd.PreviewKeyDown

  If e.Key = Key.Back Then
     If ActualPwd.Length > 0 Then
        //Remove the last character.
        ActualPwd = ActualPwd.Substring(0, ActualPwd.Length - 1)
        ShowLastCharacter()
        tbxPwd.CaretIndex = DisplayedPwd.Length
     End If
  End If

End Sub

Private Sub tbxPwd_PreviewTextInput(sender As Object, e As System.Windows.Input.TextCompositionEventArgs) _
  Handles tbxPwd.PreviewTextInput

  ActualPwd &= e.Text
  e.Handled = True

  ShowLastCharacter()

  tbxPwd.CaretIndex = DisplayedPwd.Length

End Sub

Private Sub ShowLastCharacter()
  Dim lastChar As Char = ActualPwd.Substring(ActualPwd.Length - 1)

  //Reset the displayed pwd.
  DisplayedPwd = ""
  For i As Int32 = 0 To ActualPwd.Length - 2
     DisplayedPwd &= "?"
  Next
  DisplayedPwd &= lastChar

End Sub

The tbxPwd_PreviewTextInput method is used to retrieve the character that is typed by the user. The tbxPwd_PreviewKeyDown method is used to retrieve the BackSpace key, or any other control character key you want to detect.

tbxPwd_PreviewTextInput 方法用于检索用户键入的字符。tbxPwd_PreviewKeyDown 方法用于检索 BackSpace 键或您想要检测的任何其他控制字符键。

There is no delay in this code, so it always displays the last character of the password string in clear text. It should be easy enough to add some code along with a Timer to change the last character to the pwd character after some delay.

此代码没有延迟,因此它始终以明文形式显示密码字符串的最后一个字符。添加一些代码和 Timer 应该很容易,以便在一些延迟后将最后一个字符更改为 pwd 字符。

The above code hasn't been thoroughly debugged, so there may be issues if the user backspaces out their entire password to start over.

上面的代码没有经过彻底的调试,所以如果用户退格整个密码重新开始,可能会出现问题。

Tip: Alt+0149 displays the 'bullet' password character.

提示:Alt+0149 显示“子弹”密码字符。

回答by Devam Mehta

This can be achieved using just one TextBox. See the code below:

这可以仅使用一个TextBox. 请参阅下面的代码:

XAML code for window:

窗口的 XAML 代码:

    <Label x:Name="Pwd" Height="30" Width="70" HorizontalAlignment="Left" FontSize="14"
           Margin="10,10,0,0" VerticalAlignment="Top" Content="Password:"/>
    <TextBox x:Name="textBox" Width="130" Height="30" Margin="30,10,0,0" 
             VerticalAlignment="Top" MaxLength="12" FontSize="14"
             PreviewKeyDown="TextBox_PreviewKeyDown" 
             KeyDown="TextBox_KeyDown" />
    <CheckBox x:Name="ckhShowPassword" Height="30" 
              Content="Show password characters" 
              Margin="69,0,59,42" VerticalAlignment="Bottom" 
              Checked="ckhShowPassword_Checked" Unchecked="ckhShowPassword_UnChecked"/>
    <Label x:Name="lblActualPwd" Height="30" Width="200" 
           Margin="10,100,0,0" VerticalAlignment="Top" FontSize="14"
           HorizontalAlignment="Center" HorizontalContentAlignment="Center"/>

C# Code behind:

背后的 C# 代码:

    #region "CLASS LEVEL VARIABLES"
    System.Windows.Threading.DispatcherTimer dispatcherTimer = 
        new System.Windows.Threading.DispatcherTimer();
    string actualPwd = "";
    #endregion

    #region "WINDOW EVENTS"
    public Window2()
    {
        InitializeComponent();
    }

    private void Window2_Loaded(object sender, RoutedEventArgs e)
    {
        lblActualPwd.Visibility = Visibility.Hidden;
        textBox.Focus();
    }
    #endregion

    #region "TEXTBOX EVENTS"
    private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key != Key.Back)
        {
            actualPwd += GetCharFromKey(e.Key); //Store actual characters for later retrieval        
        }
        else if (e.Key == Key.Back)
        {
            if (actualPwd.Length > 0)
                actualPwd = actualPwd.Remove(actualPwd.Length - 1);
            else
                actualPwd = "";
        }
        else
        {
            actualPwd += GetCharFromKey(e.Key);
        }

        string str = "";
        for (int i = 0; i < textBox.Text.Length; i++)
            str += char.ConvertFromUtf32(8226);

        textBox.Text = str;
        textBox.Select(textBox.Text.Length, 0);
    }

    private void TextBox_KeyDown(object sender, KeyEventArgs e)
    {
        dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
        dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 1);
        dispatcherTimer.Start();
    }
    #endregion

    #region "DISPATCHER EVENT"
    private void dispatcherTimer_Tick(object sender, EventArgs e)
    {   
        string str = "";
        for(int i = 0; i < textBox.Text.Length; i++)
            str += char.ConvertFromUtf32(8226);

        textBox.Text = str;
        textBox.Select(textBox.Text.Length, 0);
    }
    #endregion

    #region "CHECKBOX EVENTS"
    private void ckhShowPassword_Checked(object sender, RoutedEventArgs e)
    {
        if (actualPwd.Length > 0)
        {
            lblActualPwd.Foreground = Brushes.Blue;
            lblActualPwd.Content = actualPwd;
            lblActualPwd.Visibility = Visibility.Visible;
        }
        else
        {
            lblActualPwd.Foreground = Brushes.Red;
            lblActualPwd.Content = "Please input password.";
            lblActualPwd.Visibility = Visibility.Visible;
        }
    }

    private void ckhShowPassword_UnChecked(object sender, RoutedEventArgs e)
    {
        lblActualPwd.Content = string.Empty;
        lblActualPwd.Visibility = Visibility.Hidden;
    }
    #endregion

    #region "ENUM TYPE - MAP KEY TO CHARACTER"
    public enum MapType : uint
    {
        MAPVK_VK_TO_VSC = 0x0,
        MAPVK_VSC_TO_VK = 0x1,
        MAPVK_VK_TO_CHAR = 0x2,
        MAPVK_VSC_TO_VK_EX = 0x3,
    }
    #endregion

    #region "INTEROP DLL IMPORT"
    [DllImport("user32.dll")]
    public static extern bool GetKeyboardState(byte[] lpKeyState);
    [DllImport("user32.dll")]
    public static extern uint MapVirtualKey(uint uCode, MapType uMapType);
    [DllImport("user32.dll")]
    #endregion

    #region "VIRTUAL KEY UNICODE CONVERSION"
    public static extern int ToUnicode(
     uint wVirtKey,
     uint wScanCode,
     byte[] lpKeyState,
     [Out, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 4)] 
    StringBuilder pwszBuff,
     int cchBuff,
     uint wFlags);
    #endregion

    #region "FUNCTION - CHAR FROM KEY"
    public static char GetCharFromKey(Key key)
    {
        char ch = ' ';

        int virtualKey = KeyInterop.VirtualKeyFromKey(key);
        byte[] keyboardState = new byte[256];
        GetKeyboardState(keyboardState);

        uint scanCode = MapVirtualKey((uint)virtualKey, MapType.MAPVK_VK_TO_VSC);
        StringBuilder stringBuilder = new StringBuilder(2);

        int result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0);
        switch (result)
        {
            case -1:
                break;
            case 0:
                break;
            case 1:
                {
                    ch = stringBuilder[0];
                    break;
                }
            default:
                {
                    ch = stringBuilder[0];
                    break;
                }
        }
        return ch;
    }
    #endregion

MapTypeenumretrieved from MapType

MapTypeenumMapType检索

回答by bugrasitemkar

I have implemented similar functionality leaving here as a simpler example, passwordbox replaced by textbox to show password when mouse hovers password box as follows

我已经实现了类似的功能,在这里作为一个更简单的例子,当鼠标悬停在密码框上时,将密码框替换为文本框以显示密码,如下所示

xaml code for window

窗口的 xaml 代码

 <PasswordBox Name="LicencePasswordBox" MouseEnter="LicencePasswordBox_MouseEnter"></PasswordBox>
 <TextBox IsReadOnly="True" Name="LicencePasswordTextBox" MouseLeave="LicencePasswordBox_MouseLeave" Visibility="Hidden"></TextBox>

c# code behind

后面的c#代码

 private void LicencePasswordBox_MouseEnter(object sender, MouseEventArgs e)
    {
        LicencePasswordBox.Visibility = Visibility.Hidden;
        LicencePasswordTextBox.Visibility = Visibility.Visible;
    }

    private void LicencePasswordBox_MouseLeave(object sender, MouseEventArgs e)
    {
        LicencePasswordBox.Visibility = Visibility.Visible;
        LicencePasswordTextBox.Visibility = Visibility.Hidden;
    }

Also if you are developing an mvp or mvp-vm wpf application, do not forget to bind coming value to both LicencePasswordBox and LicencePasswordTextBox in code behind

此外,如果您正在开发 mvp 或 mvp-vm wpf 应用程序,请不要忘记在后面的代码中将即将到来的值绑定到 LicencePasswordBox 和 LicencePasswordTextBox