网站首页 > 技术文章 正文
1. 概述
微软官方有提供自己的密码控件,但是控件默认的行为是输入密码,会立即显示掩码,比如 *。如果像查看真实的文本,需要按查看按钮。
而我现在自定义的密码控件是先显示你输入的字符2s,然后再显示成掩码。当然这种场景并不一定适用于密码,也可以用在Pin码。
a. 微软官方的密码框
b. 自定义的效果
2. 密码框控件实现
要想实现自定义的密码框,我们当然要继承微软的TextBox控件,然后加入我们自己需要的东西。
a. 这里添加了一个加密类型的密码 Password 字段,真实密码文本 RealPassword 字段,一个Timer定时器来控制显示掩码
internal class CustomPasswordBox : TextBox
{
#region Member Variables
public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register("Password", typeof(SecureString), typeof(CustomPasswordBox), new PropertyMetadata(new SecureString()));
public static readonly DependencyProperty RealPasswordProperty = DependencyProperty.Register("RealPassword", typeof(string), typeof(CustomPasswordBox), new PropertyMetadata(string.Empty));
private DispatcherTimer maskTimer;
#endregion }
b. 公开控件属性字段
public SecureString Password
{
get {
return (SecureString)GetValue(PasswordProperty);
}
set {
SetValue(PasswordProperty, value);
}
}
public string RealPassword
{
get {
return (string)GetValue(RealPasswordProperty);
}
set {
SetValue(RealPasswordProperty, value);
}
}
c. 然后再构造函数里添加一个需要相应的事件,还有初始化Timer
public CustomPasswordBox()
{
PreviewKeyDown += CustomPasswordBox_PreviewKeyDown;
CharacterReceived += CustomPasswordBox_CharacterReceived;
BeforeTextChanging += CustomPasswordBox_BeforeTextChanging;
SelectionChanged += CustomPasswordBox_SelectionChanged;
ContextMenuOpening += CustomPasswordBox_ContextMenuOpening;
TextCompositionStarted += CustomPasswordBox_TextCompositionStarted;
maskTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 2) };
maskTimer.Tick += MaskTimer_Tick;
}
在PreviewKeyDown、CharacterReceived、BeforeTextChanging这三个事件里,主要处理字符的输入删除等逻辑。在SelectionChanged事件里,我们主要处理不让用户选中文本在ContextMenuOpening,主要是屏蔽右键菜单,比如复制剪切粘贴在TextCompositionStarted主要屏蔽中文等组合输入法
另外,在我的场景里面,我们只让用户输入数字,所以加入了数字验证,以及在Xbox上开启了InputScope=NumbericPin模式。
d. 添加事件响应代码
private void CustomPasswordBox_PreviewKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
{
switch (e.OriginalKey)
{
case VirtualKey.Back:
case VirtualKey.Delete:
if (SelectionLength > 0)
{
RemoveFromSecureString(SelectionStart, SelectionLength);
}
else if (e.OriginalKey == VirtualKey.Delete && SelectionStart < Text.Length)
{
RemoveFromSecureString(SelectionStart, 1);
}
else if (e.OriginalKey == VirtualKey.Back && SelectionStart > 0)
{
int caretIndex = SelectionStart;
if (SelectionStart > 0 && SelectionStart < Text.Length)
caretIndex = caretIndex - 1;
RemoveFromSecureString(SelectionStart - 1, 1);
//SelectionStart = caretIndex;
}
e.Handled = true;
break;
default:
//e.Handled = true;
break;
}
}
private void CustomPasswordBox_CharacterReceived(UIElement sender, Windows.UI.Xaml.Input.CharacterReceivedRoutedEventArgs args)
{
if (!char.IsDigit(args.Character))
return;
AddToSecureString(args.Character.ToString());
args.Handled = true;
}
private void CustomPasswordBox_BeforeTextChanging(TextBox sender, TextBoxBeforeTextChangingEventArgs args)
{
args.Cancel = args.NewText.Any(c => !char.IsDigit(c) && !char.ToString(c).ToString().Equals("●"));
//if (args.NewText.Replace("●", "") != "")
// AddToSecureString(args.NewText.Replace("●", ""));
}
private void CustomPasswordBox_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
e.Handled = true;
}
private void CustomPasswordBox_SelectionChanged(object sender, RoutedEventArgs e)
{
SelectionStart = Text.Length;
SelectionLength = 0;
}
private void CustomPasswordBox_TextCompositionStarted(TextBox sender, TextCompositionStartedEventArgs args)
{
return;
}
private void MaskTimer_Tick(object sender, object e)
{
MaskAllDisplayText();
}
e. 实现Timer里面的2s后将字符串变成掩码
2s后看TextBox里面的字符数,然后设置对应长度的掩码
private void MaskAllDisplayText()
{
maskTimer.Stop();
int caretIndex = SelectionStart;
Text = new string('●', Text.Length);
SelectionStart = caretIndex;
}
f. 添加字符和删除字符
private void AddToSecureString(string text)
{
if (SelectionLength > 0)
{
RemoveFromSecureString(SelectionStart, SelectionLength);
}
if (Password.Length >= 4 || RealPassword.Length >= 4)
return;
foreach (char c in text)
{
System.Diagnostics.Debug.WriteLine(text);
int caretIndex = SelectionStart;
if (caretIndex - 1 < 0)
Password.InsertAt(0, c);
else Password.InsertAt(caretIndex - 1, c);
RealPassword += c.ToString();
//MaskAllDisplayText();
if (caretIndex == Text.Length)
{
maskTimer.Stop();
maskTimer.Start();
//Text = Text.Insert(caretIndex++, c.ToString());
}
else {
//Text = Text.Insert(caretIndex++, "●");
}
SelectionStart = Text.Length;
}
}
private void RemoveFromSecureString(int startIndex, int trimLength)
{
int caretIndex = SelectionStart;
for (int i = 0; i < trimLength; ++i)
{
Password.RemoveAt(startIndex);
RealPassword = RealPassword.Remove(startIndex, 1);
}
Text = Text.Remove(startIndex, trimLength);
SelectionStart = caretIndex;
}
3. 控件使用方法
<local:CustomPasswordBox
InputScope="NumericPin" MaxLength="4" />
一个例子
<Grid>
<local:CustomPasswordBox
x:Name="PSW"
InputScope="NumericPin"
Padding="200 0 0 0"
Style="{StaticResource MyTextBox}"
Height="Auto"
MaxLength="4"
CharacterSpacing="1000"
FontSize="100"
VerticalAlignment="Top"/>
<TextBlock
x:Name="realPSW"
FontSize="88"
Margin="0 200" HorizontalAlignment="Center"
Text="{Binding ElementName=PSW, Path=RealPassword, Mode=OneWay}"/>
</Grid>
你也可以看我在Youtube上的视频演示
https://youtu.be/eHhDG3dW1bI
欢迎点击右下角的订阅按钮,还有红色小铃铛。谢谢
猜你喜欢
- 2024-10-02 MySQL用的再溜,不知道业务如何设计也白搭!!!
- 2024-10-02 JShaman本地部署专业版,批量加密工具「源码」
- 2024-10-02 干货-带你总结Mysql相关优化(mysql常用优化方案)
- 2024-10-02 Redis全文搜索教程之创建索引并关联源数据
- 2024-10-02 PHP数据类型与常量(php的数据类型主要有哪几种?)
- 2024-10-02 TensorFlow2学习25、TF2.0使用YoloV3
- 2024-10-02 Java对象内存布局(java对象在内存中如何存储)
- 2024-10-02 PHP手机和身份证号打码,生成随机数,判断逗号隔开的数字串等
- 2024-10-02 杀死僵尸进程,你需要这些神奇高效的Linux命令行
- 2024-10-02 C1DataGrid for WPF基础教程:列的类型
- 最近发表
- 标签列表
-
- cmd/c (64)
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- js判断是否空对象 (63)
- pythoncase语句 (81)
- es6includes (73)
- sqlset (64)
- phprequire_once (61)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)