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

14

如果用户在密码框中输入1985,将显示四个子弹符号 (●●●●)。如何显示每个输入的字母或数字,持续几秒钟后再将其更改为子弹符号?我认为这不能在密码框中完成,但是否有其他方法可以实现?


就像手机一样,对吧?这样用户就可以看到他们输入的内容的视觉确认 - 如果他们没有盯着键盘:P - Jerry Nixon
3个回答

19

在密码框上方放置一个文本框,然后使用一些数据绑定和动画效果。这段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"/>
你可以在动画中调整KeyTimes以获得所需的延迟时间。你还可以更改文本框中的字体设置,以使输入的文本和密码字符对齐更好。
编辑:
如果你只想显示最后一个字符,而不是所有字符都以明文形式显示:
这种情况略有不同,并需要更复杂的操作。该场景仅使用窗口上的文本框,而不是密码框。
<TextBox Name="tbxPwd" Margin="20,0" 
         Text="{Binding Path=DisplayedPwd}" />

在窗口的代码后台(或ViewModel类)中,您需要两个属性:ActualPwdDisplayedPwd。 文本框绑定到DisplayedPwd属性。

在代码后台中,您需要以下代码:

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

tbxPwd_PreviewTextInput方法用于获取用户键入的字符。tbxPwd_PreviewKeyDown方法用于检测退格键或其他控制字符键。

这段代码没有延迟,因此它始终以明文显示密码字符串的最后一个字符。通过添加一些代码和定时器,可以轻松地在一定延迟后将最后一个字符更改为pwd字符。

上述代码尚未经过彻底的调试,因此如果用户清除整个密码重新开始,则可能会出现问题。

提示:Alt+0149可以显示“bullet”密码字符。


是的,这是其中一种方法。我试着在代码后台实现,但这种解决方案更好。但还有一个小要求。客户只想看到他输入的最后一个字符。这就是我正在尝试做的事情。我试着把客户输入的内容保存在字符串中,然后用子弹替换,但我找不到能按照我解释的方式工作的解决方案:( 但非常感谢!!!:) - Simce
非常感谢您的澄清。这确实对答案产生了相当大的影响。请查看编辑内容。 - Stewbob

2

只需要使用一个TextBox就可以实现此功能。请见下面的代码:

窗口的 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#代码后台:

    #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

MapType 检索到的 MapType 枚举。

0

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

窗口的XAML代码如下:

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

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;
    }

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接