问题
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?
回答1:
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.
<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.
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, ActualPwd
and DisplayedPwd
. The textbox is bound to the DisplayedPwd
property.
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.
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.
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.
回答2:
This can be achieved using just one TextBox
. See the code below:
XAML code for window:
<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:
#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
enum
retrieved from MapType
回答3:
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
<PasswordBox Name="LicencePasswordBox" MouseEnter="LicencePasswordBox_MouseEnter"></PasswordBox>
<TextBox IsReadOnly="True" Name="LicencePasswordTextBox" MouseLeave="LicencePasswordBox_MouseLeave" Visibility="Hidden"></TextBox>
c# code behind
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
来源:https://stackoverflow.com/questions/6626937/how-to-show-characters-for-a-few-seconds-in-a-wpf-password-box