问题
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
Asking for
Mimic Google Instant functionality
Background
First things first: 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:
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.
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.
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?
Thank you guys!
回答1:
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.
This article demonstrates it pretty nicely, and shows some solutions - http://www.codeproject.com/KB/cs/AsyncMethodInvocation.aspx
回答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:
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.
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;
});
});
}
来源:https://stackoverflow.com/questions/4433874/autocomplete-textbox-freezes-while-executing-query-must-be-a-better-way