WPF密码框“另辟蹊径”的实现方式

  • A+
所属分类:.NET技术
摘要

废话不多说,咱先上效果看一下  为什么说是另辟蹊径呢,因为传统的自定义密码框组件的做法无外乎通过“显示/隐藏”按钮交替展示密码框(PasswordBox)和文本框(TextBox)组件。 再者因为扩展的PasswordBox组件的安全性,而无法使用Binding进行数据绑定

废话不多说,咱先上效果看一下

WPF密码框“另辟蹊径”的实现方式WPF密码框“另辟蹊径”的实现方式

 

 

为什么说是另辟蹊径呢,因为传统的自定义密码框组件的做法无外乎通过“显示/隐藏”按钮交替展示密码框(PasswordBox)和文本框(TextBox)组件。 再者因为扩展的PasswordBox组件的安全性,而无法使用Binding进行数据绑定

考虑到快速开发和使用,我尝试完全通过去扩展TextBox组件来实现PasswordBox的功能。此时我们面临的第一个问题是,如何将我们的明文的值脱敏成“*”/“·”等掩码。其次是脱敏后如何还原回来。 回忆一下在我们使用WPF开发的过程中,哪个功能和我们现在面临的场景大差不差,,,,对的,聪明的你应该想到了,Convert转换,既然关键出来了,那我们也不废话了。直接上代码。

  1 public class SPasswordBox : TextBox   2 {   3     private const string ElementSuffix = "PART_SuffixHost";   4    5     private IconButton _suffix;   6    7     /// <summary>   8     /// 获取或设置前缀图标   9     /// </summary>          10     [Description("获取或设置前缀图标")]  11     [Category("Customer")]  12     public string Prefix  13     {  14         get { return (string)GetValue(PrefixProperty); }  15         set { SetValue(PrefixProperty, value); }  16     }  17   18     /// <summary>  19     /// 前缀图标  20     /// </summary>  21     public static readonly DependencyProperty PrefixProperty = DependencyProperty.Register("Prefix", typeof(string), typeof(SPasswordBox));  22   23     /// <summary>  24     /// 获取或设置后缀图标  25     /// </summary>          26     [Description("获取或设置后缀图标")]  27     [Category("Customer")]  28     public string Suffix  29     {  30         get { return (string)GetValue(SuffixProperty); }  31         set { SetValue(SuffixProperty, value); }  32     }  33   34     /// <summary>  35     /// 后缀图标  36     /// </summary>  37     public static readonly DependencyProperty SuffixProperty = DependencyProperty.Register("Suffix", typeof(string), typeof(SPasswordBox), new PropertyMetadata("xe607"));  38   39     [Description("获取或设置密码状态")]  40     [Category("Common Properties")]  41     public bool ShowPassword  42     {  43         get => (bool)GetValue(ShowPasswordProperty);  44         set => SetValue(ShowPasswordProperty, value);  45     }  46   47     /// <summary>  48     /// 获取或设置密码状态  49     /// </summary>  50     public static readonly DependencyProperty ShowPasswordProperty = DependencyProperty.Register("ShowPassword", typeof(bool), typeof(SPasswordBox), new PropertyMetadata(false));  51   52     /// <summary>  53     /// 获取或设置边框圆角  54     /// </summary>          55     [Description("获取或设置按钮圆角")]  56     [Category("Customer")]  57     public CornerRadius CornerRadius  58     {  59         get { return (CornerRadius)GetValue(CornerRadiusProperty); }  60         set { SetValue(CornerRadiusProperty, value); }  61     }  62   63     /// <summary>  64     /// 边框圆角  65     /// </summary>  66     public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(SPasswordBox));  67   68     /// <summary>  69     /// 获取或设置水印  70     /// </summary>          71     [Description("获取或设置水印")]  72     [Category("Customer")]  73     public string Placeholder  74     {  75         get { return (string)GetValue(PlaceholderProperty); }  76         set { SetValue(PlaceholderProperty, value); }  77     }  78   79     /// <summary>  80     /// 水印  81     /// </summary>  82     public static readonly DependencyProperty PlaceholderProperty = DependencyProperty.Register("Placeholder", typeof(string), typeof(SPasswordBox), new PropertyMetadata(string.Empty));  83   84     public string BindingPath  85     {  86         get => (string)GetValue(BindingPathProperty);  87         set => SetValue(BindingPathProperty, value);  88     }  89   90     public static readonly DependencyProperty BindingPathProperty = DependencyProperty.Register("BindingPath", typeof(string), typeof(SPasswordBox), new PropertyMetadata(string.Empty));  91   92     /// <summary>  93     /// 获取或设置是否错误  94     /// </summary>          95     [Description("获取或设置是否错误")]  96     [Category("Customer")]  97     public bool IsError  98     {  99         get { return (bool)GetValue(IsErrorProperty); } 100         set { SetValue(IsErrorProperty, value); } 101     } 102  103     /// <summary> 104     /// 是否错误 105     /// </summary> 106     public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register("IsError", typeof(bool), typeof(SPasswordBox), new PropertyMetadata(false)); 107  108     public string ErrorMsg 109     { 110  111         get { return (string)GetValue(ErrorMsgProperty); } 112         set { SetValue(ErrorMsgProperty, value); } 113     } 114  115     /// <summary> 116     /// 错误消息 117     /// </summary> 118     public static readonly DependencyProperty ErrorMsgProperty = DependencyProperty.Register("ErrorMsg", typeof(string), typeof(SPasswordBox)); 119  120     public override void OnApplyTemplate() 121     { 122         base.OnApplyTemplate(); 123  124         _suffix = GetTemplateChild(ElementSuffix) as IconButton; 125  126         _suffix.Click += OnShowPassword; 127     } 128  129     private void OnShowPassword(object sender, RoutedEventArgs e) 130     { 131         ShowPassword = !ShowPassword; 132  133         Binding binding = new Binding(); 134  135         binding.Path = new PropertyPath(BindingPath); 136  137         if (!ShowPassword) 138         { 139             binding.Converter = new PasswordConverter(); 140         } 141  142         binding.Mode = BindingMode.TwoWay; 143  144         binding.Source = ApplicationContext.Context; 145  146         binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; 147  148         SetBinding(TextProperty, binding); 149     } 150  151     protected override void OnTextChanged(TextChangedEventArgs e) 152     { 153         SelectionStart = Text.Length; 154  155         base.OnTextChanged(e); 156     } 157  158     static SPasswordBox() 159     { 160         DefaultStyleKeyProperty.OverrideMetadata(typeof(SPasswordBox), new FrameworkPropertyMetadata(typeof(SPasswordBox))); 161     } 162 }

 1 public class PasswordConverter : IValueConverter  2 {  3     private string realWord = "";  4   5     private char replaceChar = '';  6   7     public object Convert(object value, Type targetType, object parameter, CultureInfo culture)  8     {  9         if (parameter != null) 10         { 11             string temp = parameter.ToString(); 12  13             if (!string.IsNullOrEmpty(temp)) 14             { 15                 replaceChar = temp.First(); 16             } 17         } 18  19         if (value != null) 20         { 21             realWord = value.ToString(); 22         } 23  24         string replaceWord = ""; 25  26         for (int index = 0; index < realWord.Length; ++index) 27         { 28             replaceWord += replaceChar; 29         } 30  31         return replaceWord; 32     } 33  34     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 35     { 36         string backValue = ""; 37  38         if (value != null) 39         { 40             string strValue = value.ToString(); 41  42             for (int index = 0; index < strValue.Length; ++index) 43             { 44                 if (strValue.ElementAt(index) == replaceChar) 45                 { 46                     backValue += realWord.ElementAt(index); 47                 } 48                 else 49                 { 50                     backValue += strValue.ElementAt(index); 51                 } 52             } 53         } 54         return backValue; 55     } 56 }

 1  <BooleanToVisibilityConverter x:Key="Boolean2VisibilityConverter"/>  2   3     <local:PasswordConverter x:Key="passWordConverter" />  4   5     <Style x:Key="DefaultSPasswordBoxStyle" TargetType="local:SPasswordBox">  6         <Setter Property="Background" Value="{DynamicResource RegionBrush}"/>  7         <Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>  8         <Setter Property="BorderThickness" Value="1"/>  9         <Setter Property="CornerRadius" Value="4"/> 10         <Setter Property="Template"> 11             <Setter.Value> 12                 <ControlTemplate TargetType="local:SPasswordBox"> 13                     <Border x:Name="border" BorderBrush="#DDDDDD" BorderThickness="1" CornerRadius="{TemplateBinding CornerRadius}"> 14                         <Grid x:Name="content_grid"> 15                             <Grid.ColumnDefinitions> 16                                 <ColumnDefinition Width="auto"/> 17                                 <ColumnDefinition Width="*"/> 18                                 <ColumnDefinition Width="auto"/> 19                             </Grid.ColumnDefinitions> 20                             <Label x:Name="prefix" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Content="{TemplateBinding Prefix}" FontFamily="pack://application:,,,/Controls;component/IconFont/#iconfont" FontSize="18" Foreground="#FFBFBFBF" Focusable="False" Margin="15,0,15,0" Padding="0"/> 21                             <ScrollViewer x:Name="PART_ContentHost"  ToolTip="{TemplateBinding ErrorMsg}" Grid.Column="1" FontFamily="{TemplateBinding FontFamily}" FontSize="{TemplateBinding FontSize}" Foreground="{TemplateBinding Foreground}" VerticalAlignment="Center" VerticalContentAlignment="Center" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" /> 22                             <Label x:Name="placeholder" Grid.Column="1" Height="{TemplateBinding Height}" Content="{TemplateBinding Placeholder}" FontFamily="{TemplateBinding FontFamily}" FontSize="{TemplateBinding FontSize}" Foreground="#999999" Visibility="Collapsed" VerticalContentAlignment="Center" /> 23                             <local:IconButton x:Name="PART_SuffixHost" Grid.Column="2" Icon="{TemplateBinding Suffix}" FontFamily="pack://application:,,,/Controls;component/IconFont/#iconfont" FontSize="18" Foreground="#FFBFBFBF" Focusable="False" Margin="0,0,15,0"/> 24                         </Grid> 25                     </Border> 26                     <ControlTemplate.Triggers> 27                         <DataTrigger Value="" Binding="{Binding Path=Text, RelativeSource={RelativeSource Self}}"> 28                             <Setter TargetName="placeholder" Property="Visibility" Value="Visible" /> 29                         </DataTrigger> 30                         <Trigger Property="IsError" Value="True"> 31                             <Setter TargetName="border" Property="BorderBrush" Value="#FFE02020"/> 32                             <Setter TargetName="PART_SuffixHost" Property="Foreground" Value="#FFE02020"/> 33                             <Setter Property="Foreground" Value="#FFE02020"/> 34                         </Trigger> 35                         <Trigger Property="ShowPassword" Value="True"> 36                             <Setter Property="Suffix" Value="&#xe602;"/> 37                         </Trigger> 38                         <Trigger Property="ShowPassword" Value="False"> 39                             <Setter Property="Suffix" Value="&#xe607;"/> 40                         </Trigger> 41                     </ControlTemplate.Triggers> 42                 </ControlTemplate> 43             </Setter.Value> 44         </Setter> 45     </Style>

<controls:SPasswordBox x:Name="txt_Password" BindingPath="CurrentEquipment.passwords" Text="{Binding Path=CurrentEquipment.passwords,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,Source={x:Static context:ApplicationContext.Context},Converter={StaticResource passWordConverter}}" Height="38" Prefix="&#xe605;" Placeholder="请输入账号密码" ContextMenu="{x:Null}" Margin="0,0,0,16">  <controls:SPasswordBox.CommandBindings>      <CommandBinding Command="ApplicationCommands.Paste" CanExecute="CommandBinding_CanExecute"/>      <CommandBinding Command="ApplicationCommands.Cut" CanExecute="CommandBinding_CanExecute"/>      <CommandBinding Command="ApplicationCommands.Copy" CanExecute="CommandBinding_CanExecute"/>      </controls:SPasswordBox.CommandBindings> </controls:SPasswordBox>