WPF自定义控件--模拟手机密码输入控件,在输入时显示最后一个输入密码字符
using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; namespace PasswordTest { /// <summary> /// 显示最后一个字符的密码输入框 /// </summary> public class BlinkPasswordBox : TextBox { #region DependencyProperty public static readonly DependencyProperty PasswordCharProperty = DependencyProperty.RegisterAttached("PasswordChar", typeof(char), typeof(BlinkPasswordBox), new FrameworkPropertyMetadata('●')); /// <summary> /// 密文字符 /// </summary> public char PasswordChar { get { return (char)GetValue(PasswordCharProperty); } set { SetValue(PasswordCharProperty, value); } } public static readonly DependencyProperty PasswordProperty = DependencyProperty.RegisterAttached("Password", typeof(string), typeof(BlinkPasswordBox), new UIPropertyMetadata("")); /// <summary> /// 密码字符 /// </summary> public string Password { get { return (string)GetValue(PasswordProperty); } set { SetValue(PasswordProperty, value); } } #endregion #region Properties /// <summary> /// 最后的光标位置 /// </summary> private int mLastStart = 0; /// <summary> /// 明文消失的时间 /// </summary> private int mDelay = 1000; /// <summary> /// 定时Token /// </summary> private CancellationTokenSource mWaitToken = new CancellationTokenSource(); /// <summary> /// 密码 /// </summary> private string mPassword = string.Empty; /// <summary> /// 正在更新文本 /// </summary> private bool IsUpdating; /// <summary> /// 显示的文本 /// </summary> private string ShowText { get { return base.Text; } set { IsUpdating = true; base.Text = value; IsUpdating = false; } } /// <summary> /// 密码设置 /// </summary> public new string Text { get { return mPassword; } set { mPassword = value; this.ShowText = new string(PasswordChar, value.Length); } } #endregion #region protected override protected override void OnTextChanged(TextChangedEventArgs e) { if (IsUpdating == true) return; string currentText = this.ShowText; int start = this.SelectionStart; RemoveChar(currentText, start); SetShowText(currentText, start); base.OnTextChanged(e); } /// <summary> /// 设置要显示的文本 /// </summary> /// <param name="e"></param> /// <param name="currentText"></param> /// <param name="start"></param> private void SetShowText(string currentText, int start) { if (!string.IsNullOrEmpty(currentText)) { string addChar = string.Empty; for (int i = 0; i < currentText.Length; i++) { if (currentText[i] != PasswordChar) { addChar = currentText[i].ToString(); if (this.ShowText.Length == mPassword.Length) { // 修改字符 mPassword = mPassword.Remove(i, 1).Insert(i, currentText[i].ToString()); } else { // 增加字符 mPassword = mPassword.Insert(i, currentText[i].ToString()); } } } Password = mPassword; this.ShowText = new string(PasswordChar, mPassword.Length); if (!string.IsNullOrEmpty(addChar) && start > 0) { ShowText = ShowText.Remove(start - 1, 1).Insert(start - 1, addChar); WaitChange(); } this.SelectionStart = start; } } /// <summary> /// 删除字符 /// </summary> /// <param name="currentText"></param> /// <param name="start"></param> private void RemoveChar(string currentText, int start) { if (currentText.Length < mPassword.Length) { mPassword = mPassword.Remove(start, mPassword.Length - currentText.Length); Password = mPassword; } } #endregion #region Private Methods /// <summary> /// 等待明文自动隐藏 /// </summary> private async void WaitChange() { try { mWaitToken.Cancel(); mWaitToken = new System.Threading.CancellationTokenSource(); await Task.Delay(mDelay, mWaitToken.Token); mLastStart = this.SelectionStart; this.ShowText = new string(PasswordChar, mPassword.Length); this.SelectionStart = mLastStart; } catch { } } #endregion } }