WPF PasswordBox控件使用

一、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属性不是依赖属性,所以要实现密码的明文展示需要通过附加属性来实现,具体代码如下所示:

20220626_160232_

 public class PasswordBoxAttached
    {
        public static readonly DependencyProperty PasswordProperty = DependencyProperty.RegisterAttached("Password", typeof(string), typeof(PasswordBoxAttached), new PropertyMetadata("", PasswordPropertyChangedCallback));
        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);
      }
  }
Tags: