对textbox_textchanged强制执行延迟

本文关键字:强制执行 延迟 textchanged textbox | 更新日期: 2023-09-27 18:24:10

我有一个模拟键盘输入的条形码扫描仪。我用它在一个文本框中输入ISBN编号,然后该文本框执行对该标题的搜索。在做任何事情之前,我需要文本框方法来等待一个10或13个字符的条目,但我不知道该怎么做。

到目前为止,我有以下内容:

private void scanBox_TextChanged(object sender, EventArgs e)
        {
            if (scanBox.Text.Length == 10)
            {
                getRecord10();
            }
            else if (scanBox.Text.Length == 13)
            {
                getRecord13();
            }
            else
            {
                MessageBox.Show("Not in directory", "Error");
            }
        }

我正在考虑某种定时器实现来推迟最后一个条件,但我真正需要的是该方法等待10或13位数字。条形码扫描仪模拟被按下的各个键,这就是它目前失败的原因。

对textbox_textchanged强制执行延迟

您可以使用Timer(或WPF中的DispatcherTimer)。此示例应用程序在最后一次按键后300毫秒更新窗口的标题。

    System.Windows.Forms.Timer _typingTimer; // WinForms
    // System.Windows.Threading.DispatcherTimer _typingTimer; // WPF
    public MainWindow()
    {
        InitializeComponent();
    }
    private void scanBox_TextChanged(object sender, EventArgs e)
    {
        if (_typingTimer == null)
        {
            /* WinForms: */
            _typingTimer = new Timer();
            _typingTimer.Interval = 300;
            /* WPF: 
            _typingTimer = new DispatcherTimer();
            _typingTimer.Interval = TimeSpan.FromMilliseconds(300);
            */
            _typingTimer.Tick += new EventHandler(this.handleTypingTimerTimeout);
        }
        _typingTimer.Stop(); // Resets the timer
        _typingTimer.Tag = (sender as TextBox).Text; // This should be done with EventArgs
        _typingTimer.Start(); 
    }
    private void handleTypingTimerTimeout(object sender, EventArgs e)
    {
        var timer = sender as Timer; // WinForms
        // var timer = sender as DispatcherTimer; // WPF
        if (timer == null)
        {
            return;
        }
        // Testing - updates window title
        var isbn = timer.Tag.ToString();
        windowFrame.Text = isbn; // WinForms
        // windowFrame.Title = isbn; // WPF
        // The timer must be stopped! We want to act only once per keystroke.
        timer.Stop();
    }

部分代码取自Roslyn语法可视化工具

我提出了一个使用Microsoft Reactive Extensions的解决方案,该解决方案以nuget包的形式提供。

Reactive Extensions是一个库,用于使用可观察集合和LINQ风格的查询运算符来编写异步和基于事件的程序。

如果你使用RX扩展,你的问题只需两行代码就可以解决:

注册活动:这里有计数==10

    IObservable<string> textChangedObservable =
    Observable.FromEventPattern(textBox1, "TextChanged")
    .Select(evt => ((TextBox)evt.Sender).Text).Where(x => x.Length == 10);

订阅活动:

    textChangedObservable.Subscribe(e => MessageBox.Show(e));

检查这是否有帮助。

    private System.Timers.Timer timer;
    private void scanBox_TextChanged(object sender, EventArgs e)
    {
        if (scanBox.Text.Length == 10)
        {
            //wait for 10 chars and then set the timer
            timer = new System.Timers.Timer(2000); //adjust time based on time required to enter the last 3 chars 
            timer.Elapsed += OnTimedEvent;
            timer.Enabled = true;
        }
    }
    private void OnTimedEvent(Object source, ElapsedEventArgs e)
    {
        timer.Enabled = false;
        if (scanBox.Text.Length == 10)
        {
            getRecord10();                
        }
        else if (scanBox.Text.Length == 13)
        {
            getRecord13();
        }
        else
        {
            MessageBox.Show("Not in directory", "Error");
        }
    }

我选择了定时器解决方案,并创建了一个简单的类来更巧妙地封装它。如果需要,TypeingFinished事件将从窗体访问。

TextChanged事件包装在HandleCreated

调用Settings.Default.Save是因为它对我来说总是正确的,但也可以放在TypeingFinished事件中。

using YourApp.Properties;
using System;
using System.Windows.Forms;
namespace YourApp.Controls
{
    public partial class TypeDelayTextBox : TextBox
    {
        public TypeDelayTextBox()
        {
            InitializeComponent();
            this.HandleCreated += (senderX, argsX) => { this.TextChanged += OnTextChanged; };
        }
        private void OnTextChanged(object sender, EventArgs args)
        {
            _timer.Enabled = true;
            _timer.Stop();
            _timer.Start();
        }
        private void _timer_Tick(object sender, EventArgs e)
        {
            _timer.Stop();
            Settings.Default.Save();
            OnTypingFinished();
        }
        public event EventHandler TypingFinished;
        protected virtual void OnTypingFinished()
        {
            TypingFinished?.Invoke(this, EventArgs.Empty);
        }
    }
}

设计者代码:

namespace YourApp.Controls
{
    partial class TypeDelayTextBox
    {
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
        #region Component Designer generated code
        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this._timer = new System.Windows.Forms.Timer(this.components);
            this.SuspendLayout();
            // 
            // _timer
            // 
            this._timer.Interval = 3000;
            this._timer.Tick += new System.EventHandler(this._timer_Tick);
            this.ResumeLayout(false);
        }
        #endregion
        private System.Windows.Forms.Timer _timer;
    }
}

这里有一个解决方案,用于在定时器周期后传递要调用的操作。感谢Amadeusz Wieczorek asnwer:

    private Timer _typingTimer;
    private void textBox1_TextChanged(object sender, EventArgs e)
    {
        CheckTimer(() => { label1.Text = (sender as TextBox)?.Text; });
    }
    private void CheckTimer(Action act)
    {
        if (_typingTimer == null)
        {
            _typingTimer = new Timer { Interval = 350 };
            _typingTimer.Tick += (sender, args) =>
            {
                if (!(sender is Timer timer))
                    return;
                act?.Invoke();
                timer.Stop();
            };
        }
        _typingTimer.Stop();
        _typingTimer.Start();
    }