- A+
所属分类:.NET技术
废话不多说,咱先上效果看一下
为什么说是另辟蹊径呢,因为传统的自定义密码框组件的做法无外乎通过“显示/隐藏”按钮交替展示密码框(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=""/> 37 </Trigger> 38 <Trigger Property="ShowPassword" Value="False"> 39 <Setter Property="Suffix" Value=""/> 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="" 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>