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

时间:2010-12-13 21:55:15

标签: c# wpf linq xaml

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

问题

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

要求

模仿Google即搜即得功能

背景

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

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

触发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;
    }

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

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

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

谢谢你们!

3 个答案:

答案 0 :(得分:4)

这一行:

var result = SearchUnderlyings(e.SearchText);

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

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

答案 1 :(得分:2)

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

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

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

答案 2 :(得分:1)

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

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

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;
         });
    });
}