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>

 

posted @ 2021-11-16 14:56  SFlyers  阅读(800)  评论(8编辑  收藏  举报