一、PasswordBox的SecurePassword属性
正常的String类型值,在脱离开作用域之后,其值在内存中并不会被立即销毁,这时如果有人恶意扫描你的内存,程序中所保存的机密信息就会暴露;于是就有了System.Security.SecureString,SecureString表示一个应保密的文本,它在初始化时就已被加密,并且脱离作用域后会被立即销毁。PasswordBox提供了SecurePassword属性,该属性提供的是一个SecureString类型,SecureString类型转换为string类型如下所示:
IntPtr p = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(this.LoginPasswordBox.SecurePassword);
string password = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(p);
二、PasswordBox的密码明文展示样式实现
由于Password属性不是依赖属性,所以要实现密码的明文展示需要通过附加属性来实现,具体代码如下所示:
public class PasswordBoxAttached
{
public static readonly DependencyProperty PasswordProperty = DependencyProperty.RegisterAttached("Password", typeof(string), typeof(PasswordBoxAttached), new PropertyMetadata("", PasswordPropertyChangedCallback));
[AttachedPropertyBrowsableForType(typeof(System.Windows.Controls.PasswordBox))]
public static string GetPassword(DependencyObject obj)
{
return (string)obj.GetValue(PasswordProperty);
}
public static void SetPassword(DependencyObject obj, string value)
{
obj.SetValue(PasswordProperty, value);
}
private static void PasswordPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is System.Windows.Controls.PasswordBox pb)
{
pb.Password = e.NewValue.ToString();
pb.GetType().GetMethod("Select", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(pb, new object[] { pb.Password.Length, 0 });
}
}
}
<Window.Resources>
<Style x:Key="ToggleButtonStyle" TargetType="ToggleButton">
<Setter Property="Margin" Value="5,0"></Setter>
<Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="BorderThickness" Value="0"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Image x:Name="IconImage" Source="Resources/eye_close.png"></Image>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="IconImage" Property="Source" Value="Resources/eye_open.png"></Setter>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="IconImage" Property="Source" Value="Resources/eye_open.png"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="PasswordBox">
<Setter Property="VerticalContentAlignment" Value="Center"></Setter>
<Setter Property="Padding" Value="5,0"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="PasswordBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="32"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0" x:Name="PART_ContentHost"></ScrollViewer>
<TextBlock Grid.Column="0" x:Name="MessageTextBlock"
Text="{TemplateBinding Tag}"
FontSize="{TemplateBinding FontSize}"
Foreground="{TemplateBinding Foreground}"
Padding="{TemplateBinding Padding}"
VerticalAlignment="Center"
Visibility="Hidden"></TextBlock>
<TextBox Grid.Column="0" VerticalContentAlignment="Center"
BorderThickness="0" Visibility="Hidden"
FontSize="{TemplateBinding FontSize}"
Foreground="{TemplateBinding Foreground}"
Margin="{TemplateBinding Padding}"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=PasswordBox}, Path=(local:PasswordBoxAttached.Password)}"
x:Name="PasswordTextBox"></TextBox>
<ToggleButton Grid.Column="1" x:Name="CheckedToggleButton" Focusable="False"
Style="{StaticResource ToggleButtonStyle}"></ToggleButton>
</Grid>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsKeyboardFocused" Value="False"></Condition>
<Condition Property="local:PasswordBoxAttached.Password" Value=""></Condition>
</MultiTrigger.Conditions>
<Setter TargetName="MessageTextBlock" Property="Visibility" Value="Visible"></Setter>
<Setter TargetName="MessageTextBlock" Property="Opacity" Value="0.5"></Setter>
</MultiTrigger>
<DataTrigger Binding="{Binding ElementName=CheckedToggleButton, Path=IsChecked}" Value="True">
<Setter TargetName="PasswordTextBox" Property="Visibility" Value="Visible"></Setter>
<Setter TargetName="PART_ContentHost" Property="Visibility" Value="Hidden"></Setter>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel Margin="20">
<PasswordBox Height="60"
Tag="请输入密码"
x:Name="LoginPasswordBox"
PasswordChanged="LoginPasswordBox_OnPasswordChanged"></PasswordBox>
</StackPanel>
private void LoginPasswordBox_OnPasswordChanged(object sender, RoutedEventArgs e)
{
if (sender is System.Windows.Controls.PasswordBox pb)
{
PasswordBoxAttached.SetPassword(pb, pb.Password);
}
}