UWP 自定义密码框控件

1. 概述

微软官方有提供自己的密码控件,但是控件默认的行为是输入密码,会立即显示掩码,比如 *。如果像查看真实的文本,需要按查看按钮。

而我现在自定义的密码控件是先显示你输入的字符2s,然后再显示成掩码。当然这种场景并不一定适用于密码,也可以用在Pin码。

a. 微软官方的密码框

UWP 自定义密码框控件

b. 自定义的效果

UWP 自定义密码框控件

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" />

一个例子


        <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}"/>
    

你也可以看我在Youtube上的视频演示

https://youtu.be/eHhDG3dW1bI

欢迎点击右下角的订阅按钮,还有红色小铃铛。谢谢

UWP 自定义密码框控件

Original: https://www.cnblogs.com/hupo376787/p/13372655.html
Author: 猫叔Vincent
Title: UWP 自定义密码框控件

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/607082/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • zookeeper 配置文件详情

    目录结构 目录名作用 bin 存放系统脚本 conf 存放配置文件 contrib zk附加功能支持 dist-maven maven仓库文件 docs zk文档 lib 依赖的第…

    Linux 2023年6月8日
    066
  • WEB自动化-10-Page Object 模型

    10 Page Object 模型 10.1 概述 在针对一个WEB页面编写自动化测试用例时,需要引用页面中的元素( 数据)才能进行操作( 动作)并显示出页面内容。如果编写的测试用…

    Linux 2023年6月7日
    071
  • flask操作(增删改查操作)

    增加数据 from .models import Goods from app.extensions import db goods1 = Goods(name=’魅族18s’, …

    Linux 2023年6月8日
    092
  • JavaScript闭包

    <!doctype html> <html lang="en"> <head> <title>&#x95…

    Linux 2023年6月13日
    074
  • springboot分析——与其他组件的整合(JPA规范/atomikos/redis)

    一:与JPA规范整合 jpa是一套orm的规范,提供api接口,hirebnate就是对jpa的一套实现,下面我们看看springboot如何 与jpa整合 1:添加依赖和配置 j…

    Linux 2023年5月28日
    0112
  • 性能测试—实施流程记录

    posted @2022-06-08 17:31 尼古丁·瘾 阅读(26 ) 评论() 编辑 Original: https://www.cnblogs.com/ngd-mzl/p…

    Linux 2023年6月8日
    0113
  • Python 练习题:用索引取出LIST中的值

    请用索引取出下面list的指定元素,分别为Apple,Python,Lisa -*- coding: utf-8 -*- 请用索引取出下面list的指定元素 L = [ [‘App…

    Linux 2023年6月8日
    095
  • jmeter的一些概念知识

    前言 一、Jmeter的作用 – 1.jmeter进行接口操作 2. jmeter进行性能操作 二、Jmeter的一些概念的理解 – 1.事务 2. TPS…

    Linux 2023年6月14日
    0106
  • linux系统(centos)下kvm虚拟化用命令行给虚拟机添加硬盘

    linux系统(centos)下kvm虚拟化用命令行给虚拟机添加硬盘 背景 公司有用单台服务器使用kvm装虚拟机,利用webvirtmgr进行界面管理。当虚拟机创建时固定硬盘后,不…

    Linux 2023年6月8日
    096
  • oracle 删除表空间与用户

    以system用户登录,查找需要删除的用户: –查找用户 select * from dba_users; –查找工作空间的路径select * from …

    Linux 2023年6月8日
    059
  • Zookeeper集群搭建及原理

    1 概述 1.1 简介 ZooKeeper 是 Apache 的一个顶级项目,为分布式应用提供高效、高可用的分布式协调服务,提供了诸如数据发布/订阅、负载均衡、命名服务、分布式协调…

    Linux 2023年6月13日
    082
  • Color 16 Base Code 颜色代码大全

    颜色预览表,请参考以下图片。 十六进制颜色编码字符串如下所示(前置的英语单词都是颜色) ‘aliceblue’: ‘#F0F8FF’…

    Linux 2023年6月7日
    0113
  • 使用Kotlin协程配合Retrofit发送请求

    Retrofit2.6开始增加了对Kotlin协程的支持,可以通过suspend函数进行异步调用。本文简单介绍一下使用Kotlin协程配合Retrofit使用,发起网络请求。 ap…

    Linux 2023年6月8日
    0101
  • Java学习笔记_Lambda学习

    在Java8之前,如果想”让参数具备行为能力”,即将代码块作为参数进行传递,这是很不方便的。比较普遍的方式就是创建一个类的实例对象,让实例去调用这个方法,从…

    Linux 2023年6月7日
    095
  • 面试题:Java序列化与反序列化

    序列化和反序列化的概念 应用场景? 序列化实现的方式 继承Serializable接口,普通序列化 继承Externalizable接口,强制自定义序列化 serialVersio…

    Linux 2023年6月6日
    0114
  • 搭建ES集群

    添加以下elasticsearch用户: bash;gutter:true; 1 useradd elasticsearch 2 passwd elasticsearch</…

    Linux 2023年6月8日
    077
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球