WPF文本框需求:输入字符显示半秒后转为*掩码
WPF 文本框实现“输入字符显示半秒后转为*”的方案
直接基于你已有的自定义TextBox逻辑,通过真实内容存储+DispatcherTimer延迟替换的方式实现需求,以下是完整的可运行代码:
自定义DelayedPasswordTextBox实现
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace YourAppNamespace.Controls { public class DelayedPasswordTextBox : TextBox { // 存储用户输入的真实机密内容 private string _realContent = string.Empty; // 延迟替换字符的定时器(WPF UI线程专用) private readonly DispatcherTimer _maskTimer; // 记录最后一次输入的位置,保证光标始终在末尾 private int _lastInputPos; public DelayedPasswordTextBox() { // 初始化定时器,设置半秒延迟 _maskTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(500) }; _maskTimer.Tick += OnMaskTimerTick; // 优化UI体验:隐藏只读光标、禁止文本选中 IsReadOnlyCaretVisible = false; IsReadOnly = false; } protected override void OnTextInput(TextCompositionEventArgs e) { // 停止之前的定时器,避免连续输入时多次触发替换 _maskTimer.Stop(); // 追加真实输入内容 _realContent += e.Text; // 更新显示:前面所有字符用*,最后一个显示输入的原字符 Text = new string('*', _realContent.Length - 1) + e.Text; // 定位光标到末尾 _lastInputPos = Text.Length; CaretIndex = _lastInputPos; // 启动定时器,半秒后替换最后一个字符为* _maskTimer.Start(); // 阻止默认文本输入逻辑,避免原字符直接写入TextBox的Text属性 e.Handled = true; } private void OnMaskTimerTick(object sender, EventArgs e) { _maskTimer.Stop(); // 替换所有显示字符为* if (!string.IsNullOrEmpty(_realContent)) { Text = new string('*', _realContent.Length); CaretIndex = Text.Length; } } // 处理退格键,保证真实内容和显示同步 protected override void OnPreviewKeyDown(KeyEventArgs e) { if (e.Key == Key.Back && _realContent.Length > 0) { _maskTimer.Stop(); // 删除真实内容的最后一位 _realContent = _realContent.Substring(0, _realContent.Length - 1); // 更新显示文本 Text = new string('*', _realContent.Length); CaretIndex = Text.Length; e.Handled = true; } base.OnPreviewKeyDown(e); } // 对外提供获取真实机密内容的方法 public string GetRealContent() { return _realContent; } // 可选:添加模式切换依赖属性,支持普通文本/延迟密码模式切换 public static readonly DependencyProperty IsPasswordModeProperty = DependencyProperty.Register(nameof(IsPasswordMode), typeof(bool), typeof(DelayedPasswordTextBox), new PropertyMetadata(true, OnIsPasswordModeChanged)); public bool IsPasswordMode { get => (bool)GetValue(IsPasswordModeProperty); set => SetValue(IsPasswordModeProperty, value); } private static void OnIsPasswordModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var textBox = (DelayedPasswordTextBox)d; if (!(bool)e.NewValue) { // 切换为普通模式:显示真实内容 textBox._maskTimer.Stop(); textBox.Text = textBox._realContent; } else { // 切换为密码模式:恢复掩码显示 textBox.Text = new string('*', textBox._realContent.Length); } } } }
XAML中使用自定义控件
- 先添加命名空间引用(替换成你的控件实际命名空间):
xmlns:controls="clr-namespace:YourAppNamespace.Controls"
- 直接使用控件:
<controls:DelayedPasswordTextBox Width="250" Height="35" />
- 切换模式示例(比如通过CheckBox控制):
<StackPanel> <controls:DelayedPasswordTextBox x:Name="SecretTextBox" Width="250" Height="35" /> <CheckBox Content="切换为普通文本模式" IsChecked="{Binding IsPasswordMode, ElementName=SecretTextBox, Mode=TwoWay}" Margin="0,10,0,0"/> </StackPanel>
关键注意事项
- 所有真实输入内容都存在
_realContent字段中,通过GetRealContent()方法获取,不要直接用TextBox的Text属性 - 使用
DispatcherTimer而非普通Timer,避免WPF跨线程操作UI的异常 - 额外处理了退格键逻辑,保证用户删除操作时显示和真实内容一致
- 可选的
IsPasswordMode依赖属性,直接支持你需要的“特定条件切换密码模式”需求
内容的提问来源于stack exchange,提问作者Praneeth Kumar Gunda
相关产品推荐
相关产品推荐

