执行查询时,自动填充文本框会冻结。一定是更好的方式!

时间:2021-04-22 15:40:54

everyone! I searched the best I could and did not find exactly the help I was looking for.

大家!我尽我所能搜索,并没有找到我正在寻找的帮助。

Problem

AutoCompleteTextbox FREEZES and "eats" characters while query is performed

执行查询时,AutoCompleteTextbox FREEZES和“吃”字符

Asking for

Mimic Google Instant functionality

模仿Google即搜即得功能

Background

First things first: C#, WPF, .NET 4.0

首先要做的事:C#,WPF,.NET 4.0

Ok, now that's out of the way, I'm trying to find the best way to implement a dynamic AutoComplete Textbox, which queries a database for results after each letter typed.

好的,现在已经开始了,我正在尝试找到实现动态自动完成文本框的最佳方法,该文本框在每个字母输入后查询数据库以查找结果。

The following code gets executed when the AutoCompleteTextBox's TextChanged event is fired:

触发AutoCompleteTextBox的TextChanged事件时,将执行以下代码:

    public void Execute(object sender, object parameter)
    {
        //removed some unnecessary code for the sake of being concise

        var autoCompleteBox = sender as AutoCompleteTextBox;
        var e = parameter as SearchTextEventArgs;

        var result = SearchUnderlyings(e.SearchText);

        autoCompleteBox.ItemsSource = result;
    }

Now, let's say that SearchUnderlyings(e.SearchText) takes an average of 600-1100ms - during that time, the textbox is frozen and it "eats" any keys pressed. This is an annoying problem I've been having. For some reason, the LINQ in SearchUnderlyings(e.SearchText) is running in the GUI thread. I tried delegating this to a background thread, but still same result.

现在,让我们说SearchUnderlyings(e.SearchText)平均需要600-1100ms - 在此期间,文本框被冻结并且“吃掉”任何按下的键。这是我一直遇到的烦人问题。出于某种原因,SearchUnderlyings中的LINQ(e.SearchText)正在GUI线程中运行。我尝试将此委托给后台线程,但仍然是相同的结果。

Ideally, I would like the textbox to work the way Google Instant does - but I don't want to be "killing" threads before the server/query can return a result.

理想情况下,我希望文本框能够像Google Instant一样工作 - 但我不想在服务器/查询返回结果之前“杀死”线程。

Anyone have experience or can offer some guidance which will allow me to query as I type without freezing the GUI or killing the server?

任何人都有经验或可以提供一些指导,这将允许我在不冻结GUI或查杀服务器的情况下进行查询?

Thank you guys!

感谢你们!

3 个解决方案

#1


4  

This line:

var result = SearchUnderlyings(e.SearchText);

Runs synchronously, locking the UI thread. The way to cure this would be to switch to an asynchronous pattern, where you start the query, and then do something when it finishes.

同步运行,锁定UI线程。解决这个问题的方法是切换到异步模式,然后启动查询,然后在完成时执行某些操作。

This article demonstrates it pretty nicely, and shows some solutions - http://www.codeproject.com/KB/cs/AsyncMethodInvocation.aspx

本文非常精彩地展示了它,并展示了一些解决方案 - http://www.codeproject.com/KB/cs/AsyncMethodInvocation.aspx

#2


2  

What is probably killing you is setting the binding source over and over again (which is why running the query on a background thread doesn't make a difference).

什么可能会杀死你一遍又一遍地设置绑定源(这就是为什么在后台线程上运行查询没有什么区别)。

You might consider the algorithm as a whole. Depending on your data, you could wait until the user enters the first three characters and then do one large query against the database. Bind the item source once. Each character typed afterwards just performs a filter against your data that is already cached on the client. That way you are not hitting the database over and over (which is going to be terribly expensive).

您可以将算法视为一个整体。根据您的数据,您可以等到用户输入前三个字符,然后对数据库执行一次大型查询。绑定项目源一次。之后键入的每个字符只对已经缓存在客户端上的数据执行过滤。这样你就不会一遍又一遍地攻击数据库(这将是非常昂贵的)。

Or consider just bringing back three or so results from the DB to keep your service serialization time down.

或者考虑从数据库中返回三个左右的结果,以缩短服务序列化时间。

#3


1  

So, we kind of hacked something quick. By making the calls to SearchUnderlyings(e.SearchText) asynchronous, my GUI thread is no longer blocked and the textbox is no longer "eating" key presses. By adding the lastQueryTag == _lastQuery check, we are trying to ensure some thread-safety, allowing only the most recent query to set the ItemsSource.

所以,我们有点快速入侵了。通过调用SearchUnderlyings(e.SearchText)异步,我的GUI线程不再被阻止,文本框不再“吃掉”按键。通过添加lastQueryTag == _lastQuery检查,我们尝试确保一些线程安全,只允许最新的查询来设置ItemsSource。

Perhaps not the most ideal or elegant solution. I am still open to further critiques and suggestions. Thank you!

也许不是最理想或最优雅的解决方案。我仍然愿意接受进一步的批评和建议。谢谢!

private long _lastQuery = DateTime.Now.Ticks;

public void Execute(object sender, object parameter)
{
    var autoCompleteBox = sender as AutoCompleteTextBox;
    var e = parameter as SearchTextEventArgs;

    // removed unecessary code for clarity

    long lastQueryTag = _lastQuery = DateTime.Now.Ticks;
    Task.Factory.StartNew(() =>
    {                        
        var result = SearchUnderlyings(e.SearchText);

         System.Windows.Application.Current.Dispatch(() =>
         {
             if (lastQueryTag == _lastQuery)
                  autoCompleteBox.ItemsSource = result;
         });
    });
}

#1


4  

This line:

var result = SearchUnderlyings(e.SearchText);

Runs synchronously, locking the UI thread. The way to cure this would be to switch to an asynchronous pattern, where you start the query, and then do something when it finishes.

同步运行,锁定UI线程。解决这个问题的方法是切换到异步模式,然后启动查询,然后在完成时执行某些操作。

This article demonstrates it pretty nicely, and shows some solutions - http://www.codeproject.com/KB/cs/AsyncMethodInvocation.aspx

本文非常精彩地展示了它,并展示了一些解决方案 - http://www.codeproject.com/KB/cs/AsyncMethodInvocation.aspx

#2


2  

What is probably killing you is setting the binding source over and over again (which is why running the query on a background thread doesn't make a difference).

什么可能会杀死你一遍又一遍地设置绑定源(这就是为什么在后台线程上运行查询没有什么区别)。

You might consider the algorithm as a whole. Depending on your data, you could wait until the user enters the first three characters and then do one large query against the database. Bind the item source once. Each character typed afterwards just performs a filter against your data that is already cached on the client. That way you are not hitting the database over and over (which is going to be terribly expensive).

您可以将算法视为一个整体。根据您的数据,您可以等到用户输入前三个字符,然后对数据库执行一次大型查询。绑定项目源一次。之后键入的每个字符只对已经缓存在客户端上的数据执行过滤。这样你就不会一遍又一遍地攻击数据库(这将是非常昂贵的)。

Or consider just bringing back three or so results from the DB to keep your service serialization time down.

或者考虑从数据库中返回三个左右的结果,以缩短服务序列化时间。

#3


1  

So, we kind of hacked something quick. By making the calls to SearchUnderlyings(e.SearchText) asynchronous, my GUI thread is no longer blocked and the textbox is no longer "eating" key presses. By adding the lastQueryTag == _lastQuery check, we are trying to ensure some thread-safety, allowing only the most recent query to set the ItemsSource.

所以,我们有点快速入侵了。通过调用SearchUnderlyings(e.SearchText)异步,我的GUI线程不再被阻止,文本框不再“吃掉”按键。通过添加lastQueryTag == _lastQuery检查,我们尝试确保一些线程安全,只允许最新的查询来设置ItemsSource。

Perhaps not the most ideal or elegant solution. I am still open to further critiques and suggestions. Thank you!

也许不是最理想或最优雅的解决方案。我仍然愿意接受进一步的批评和建议。谢谢!

private long _lastQuery = DateTime.Now.Ticks;

public void Execute(object sender, object parameter)
{
    var autoCompleteBox = sender as AutoCompleteTextBox;
    var e = parameter as SearchTextEventArgs;

    // removed unecessary code for clarity

    long lastQueryTag = _lastQuery = DateTime.Now.Ticks;
    Task.Factory.StartNew(() =>
    {                        
        var result = SearchUnderlyings(e.SearchText);

         System.Windows.Application.Current.Dispatch(() =>
         {
             if (lastQueryTag == _lastQuery)
                  autoCompleteBox.ItemsSource = result;
         });
    });
}