Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/277.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Don';在连续键入时,不能更改raise TEXT_C#_Winforms_.net 4.0 - Fatal编程技术网

C# Don';在连续键入时,不能更改raise TEXT

C# Don';在连续键入时,不能更改raise TEXT,c#,winforms,.net-4.0,C#,Winforms,.net 4.0,我有一个文本框,它有一个相当大的\u TextChanged事件处理程序。在正常的输入条件下,性能是可以的,但是当用户执行长时间的连续操作时,性能会明显滞后,例如按住backspace按钮一次删除大量文本 例如,事件需要0.2秒才能完成,但用户每0.1秒执行一次删除。因此,它无法迎头赶上,需要处理的事件将积压,从而导致UI滞后 但是,事件不需要在状态之间运行,因为它只关心最终结果。有没有办法让事件处理程序知道它应该只处理最新的事件,而忽略以前所有过时的更改?您可以将事件处理程序标记为async,

我有一个文本框,它有一个相当大的
\u TextChanged
事件处理程序。在正常的输入条件下,性能是可以的,但是当用户执行长时间的连续操作时,性能会明显滞后,例如按住backspace按钮一次删除大量文本

例如,事件需要0.2秒才能完成,但用户每0.1秒执行一次删除。因此,它无法迎头赶上,需要处理的事件将积压,从而导致UI滞后


但是,事件不需要在状态之间运行,因为它只关心最终结果。有没有办法让事件处理程序知道它应该只处理最新的事件,而忽略以前所有过时的更改?

您可以将事件处理程序标记为
async
,并执行以下操作:

bool isBusyProcessing = false;

private async void textBox1_TextChanged(object sender, EventArgs e)
{
    while (isBusyProcessing)
        await Task.Delay(50);

    try
    {
        isBusyProcessing = true;
        await Task.Run(() =>
        {
            // Do your intensive work in a Task so your UI doesn't hang
        });

    }
    finally
    {
        isBusyProcessing = false;
    }
}

Try
Try finally
子句是必需的,以确保在某个点将
isBusyProcessing
设置为
false
,这样您就不会陷入无限循环。

我不知道如何剔除事件队列,但我可以想出两种方法来处理这个问题

如果您想要一些快速的东西(按照某些人的标准,它有点脏),您可以引入一个等待计时器——当验证函数运行时,用当前时间设置一个标志(函数中的静态变量应该足够了)。如果在上次运行并完成后的0.5秒内再次调用该函数,请立即退出该函数(大大缩短函数的运行时间)。这将解决事件积压问题,前提是函数的内容导致它变慢,而不是事件本身的触发。这样做的缺点是,您必须引入某种备份检查,以确保当前状态已被验证,即,如果上次更改发生在0.5s块发生时

或者,如果您唯一的问题是不希望在用户进行连续操作时进行验证,您可以尝试修改事件处理程序,使其在按键进行时不执行验证而退出,或者甚至可以将验证操作绑定到KeyUp而不是TextChanged

有很多方法可以实现这一点。例如,如果对特定的键执行KeyDown事件(例如,您的示例中使用backspace,但理论上您应该将其扩展到任何将键入字符的项),则验证函数将不执行任何操作而退出,直到触发同一键的keydup事件。这样,直到最后一次修改完成后它才会运行。。。希望如此

这可能不是达到预期效果的最佳方式(它可能根本不起作用!有可能在用户按完键之前触发_TextChanged事件),但理论是正确的。如果不花一些时间玩,我不能绝对确定按键的行为-你能检查一下按键是否被按下并退出,或者你是否必须手动升起一个在KeyDown和KeyUp之间为真的标志?稍微玩弄一下你的选择,就可以清楚地知道什么是适合你的特定情况的最佳方法


我希望这有帮助

我已经多次遇到这个问题,根据我自己的经验,到目前为止,我发现这个解决方案简单明了。它基于
Windows窗体
,但可以轻松转换为
WPF

工作原理:

TypeAssistant
得知发生了
文本更改时,它会运行计时器。在
等待毫秒后
计时器引发
空闲
事件。通过处理此事件,您可以执行任何您想要的工作(例如处理输入的tex)。如果在计时器启动和等待毫秒后的时间范围内发生另一次
文本更改
,计时器将重置

public class TypeAssistant
{
    public event EventHandler Idled = delegate { };
    public int WaitingMilliSeconds { get; set; }
    System.Threading.Timer waitingTimer;

    public TypeAssistant(int waitingMilliSeconds = 600)
    {
        WaitingMilliSeconds = waitingMilliSeconds;
        waitingTimer = new Timer(p =>
        {
            Idled(this, EventArgs.Empty);
        });
    }
    public void TextChanged()
    {
        waitingTimer.Change(WaitingMilliSeconds, System.Threading.Timeout.Infinite);
    }
}
用法:

public partial class Form1 : Form
{
    TypeAssistant assistant;
    public Form1()
    {
        InitializeComponent();
        assistant = new TypeAssistant();
        assistant.Idled += assistant_Idled;          
    }

    void assistant_Idled(object sender, EventArgs e)
    {
        this.Invoke(
        new MethodInvoker(() =>
        {
            // do your job here
        }));
    }

    private void yourFastReactingTextBox_TextChanged(object sender, EventArgs e)
    {
        assistant.TextChanged();
    }
}
优势:

  • 简单
  • WPF
    Windows窗体中工作
  • 使用.NETFramework 3.5+
缺点:

  • 再运行一个线程
  • 需要调用而不是直接操纵表单
    • 我们正在很好地处理这种情况

      因此,您希望通过限制
      0.1
      秒来捕获
      TextChanged
      事件并处理输入。 您可以将
      TextChanged
      事件转换为
      IObservable
      并订阅它

      像这样的

      (from evt in Observable.FromEventPattern(textBox1, "TextChanged")
       select ((TextBox)evt.Sender).Text)
      .Throttle(TimeSpan.FromMilliSeconds(90))
      .DistinctUntilChanged()
      .Subscribe(result => // process input);
      
      因此,这段代码订阅
      TextChanged
      事件,对其进行限制,确保只获取不同的值,然后从事件参数中提取
      Text

      请注意,这个代码更像是一个伪代码,我没有测试它。 要使用
      Rx Linq
      ,您需要安装


      如果您喜欢这种方法,可以使用Rx Linq检查是否实现了自动完成控制。我还建议您使用反应式扩展。

      您不能按照下面的思路做一些事情吗

      Stopwatch stopWatch;
      
      TextBoxEnterHandler(...)
      {
          stopwatch.ReStart();
      }
      
      TextBoxExitHandler(...)
      {
          stopwatch.Stop();
      }
      
      TextChangedHandler(...)
      {
          if (stopWatch.ElapsedMiliseconds < threshHold)
          {
              stopwatch.Restart();
              return;
          }
      
          {
             //Update code
          }
      
          stopwatch.ReStart()
      }
      
      秒表秒表;
      TextBoxEnterHandler(…)
      {
      stopwatch.ReStart();
      }
      TextBoxExitHandler(…)
      {
      秒表;
      }
      TextChangedHandler(…)
      {
      if(stopWatch.ElapsedMiliseconds
      我还认为,反应式扩展是解决这一问题的方法。不过,我有一个稍微不同的问题

      我的代码如下所示:

              IDisposable subscription =
                  Observable
                      .FromEventPattern(
                          h => textBox1.TextChanged += h,
                          h => textBox1.TextChanged -= h)
                      .Select(x => textBox1.Text)
                      .Throttle(TimeSpan.FromMilliseconds(300))
                      .Select(x => Observable.Start(() => /* Do processing */))
                      .Switch()
                      .ObserveOn(this)
                      .Subscribe(x => textBox2.Text = x);
      
      // last processed text
      string lastProcessed;
      private async void textBox1_TextChanged(object sender, EventArgs e) {
          // clear last processed text if user deleted all text
          if (string.IsNullOrEmpty(textBox1.Text)) lastProcessed = null;
          // this inner method checks if user is still typing
          async Task<bool> UserKeepsTyping() {
              string txt = textBox1.Text;   // remember text
              await Task.Delay(500);        // wait some
              return txt != textBox1.Text;  // return that text chaged or not
          }
          if (await UserKeepsTyping() || textBox1.Text == lastProcessed) return;
          // save the text you process, and do your stuff
          lastProcessed = textBox1.Text;   
      }
      
      现在,这正是您预期的工作方式

      FromEventPattern
      TextChanged
      转换为可观察对象,返回发送方和事件参数
      private async void textBox1_TextChanged(object sender, EventArgs e) {
          // this inner method checks if user is still typing
          async Task<bool> UserKeepsTyping() {
              string txt = textBox1.Text;   // remember text
              await Task.Delay(500);        // wait some
              return txt != textBox1.Text;  // return that text chaged or not
          }
          if (await UserKeepsTyping()) return;
          // user is done typing, do your stuff    
      }
      
      Func<Task<bool>> UserKeepsTyping = async delegate () {...}
      
      // last processed text
      string lastProcessed;
      private async void textBox1_TextChanged(object sender, EventArgs e) {
          // clear last processed text if user deleted all text
          if (string.IsNullOrEmpty(textBox1.Text)) lastProcessed = null;
          // this inner method checks if user is still typing
          async Task<bool> UserKeepsTyping() {
              string txt = textBox1.Text;   // remember text
              await Task.Delay(500);        // wait some
              return txt != textBox1.Text;  // return that text chaged or not
          }
          if (await UserKeepsTyping() || textBox1.Text == lastProcessed) return;
          // save the text you process, and do your stuff
          lastProcessed = textBox1.Text;   
      }
      
          private async Task ValidateText()
          {
              if (m_isBusyProcessing)
                  return;
              // Don't validate on each keychange
              m_isBusyProcessing = true;
              await Task.Delay(200);
              m_isBusyProcessing = false;
      
              // Do your work here.       
          }
      
          string mostRecentText = "";
      
          async void entry_textChanged(object sender, EventArgs e)
          {
              //get the entered text
              string enteredText = (sender as Entry).Text;
      
              //set the instance variable for entered text
              mostRecentText = enteredText;
      
              //wait 1 second in case they keep typing
              await Task.Delay(1000);
      
              //if they didn't keep typing
              if (enteredText == mostRecentText)
              {
                  //do what you were going to do
                  doSomething(mostRecentText);
              }
          }
      
      private List<Task<bool>> taskTypeQueue = new List<Task<bool>>();
      private async void textBox_TextChanged(object sender, EventArgs e)
      {
          async Task<bool> isStillTyping()
          {
              Application.DoEvents();
      
              int taskCount = taskTypeQueue.Count;
              string oldStr = textBox.Text;
              await Task.Delay(1500);
      
              if ((oldStr != textBox.Text) || (taskCount != taskTypeQueue.Count - 1))
              {
                  return true;
              }
      
              return false;
          }
      
          taskTypeQueue.Add(isStillTyping());
          if (await taskTypeQueue[taskTypeQueue.Count - 1])
              return;
      
          // typing appears to have stopped, continue
          taskTypeQueue.Clear();
      }