i have some performance problems implementing a feature, where a listbox is realtime-filtered while user is typing the filter-string to a textbox. Feature i'm trying to create is similar to the call history search in WP7.
I created a simple project to test this and copy-pasted the important bits below. Basically i have a TextBox, where user is supposed to write a string that will be used to filter the data bound to a listbox. This filtering should happen in realtime, not after tapping any sort of filter-button etc.
ListBox is bound to a CollectionViewSource which uses a ObservableCollection as a source. When something is entered to the textbox, that value is instantly databound to a property in view model. View model property's setter fires up the filtering of CollectionViewSource, which updates the ListBox's contents.
In the actual project i'm doing, the ListBox can contain a hundred or so items.
Here's the related XAML:
<TextBox TextChanged="TextBox_TextChanged" Text="{Binding FilterString, Mode=TwoWay, UpdateSourceTrigger=Explicit}"></TextBox>
<ListBox ItemsSource="{Binding ItemsListCVS.View, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Prop1, Mode=TwoWay}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code behind to trigger instant binding to ViewModel-property:
private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
var textBox = sender as TextBox;
// Update the binding source
BindingExpression bindingExpr = textBox.GetBindingExpression(TextBox.TextProperty);
bindingExpr.UpdateSource();
}
ViewModel:
private ObservableCollection<AnItem> _itemsList = new ObservableCollection<AnItem>();
private CollectionViewSource _itemsListCvs = new CollectionViewSource();
public ObservableCollection<AnItem> ItemsList
{
get
{
return _itemsList;
}
set
{
_itemsList = value;
// Update bindings, no broadcast
RaisePropertyChanged(ItemsListPropertyName);
}
}
public string FilterString
{
get
{
return _filterString;
}
set
{
if (_filterString == value)
{
return;
}
_filterString = value;
// Update bindings, no broadcast
RaisePropertyChanged(FilterStringPropertyName);
this.Filter();
}
}开发者_如何学Python
public CollectionViewSource ItemsListCVS
{
get
{
return _itemsListCvs;
}
set
{
if (_itemsListCvs == value)
{
return;
}
_itemsListCvs = value;
// Update bindings, no broadcast
RaisePropertyChanged(ItemListPropertyName);
}
}
public MainViewModel()
{
var items = Builder<AnItem>.CreateListOfSize(100).Build();
this.ItemsList = new ObservableCollection<AnItem>(items);
this.ItemsListCVS.Source = this.ItemsList;
}
private void Filter()
{
this.ItemsListCVS.View.Filter = r =>
{
if (r == null) return true;
return ((AnItem)r).Prop1.ToString().ToLowerInvariant().Contains(FilterString);
};
}
AnItem-class which is databound to the list:
public class AnItem
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
public string Prop4 { get; set; }
public string Prop5 { get; set; }
}
Question:
Everything is working okay, but there is a horrific lag between writing to the TextBox and updating of the ListBox.
Am i simply doing it wrong? If so, then how should i change my approach? I think that this is quite common requirement, so there's probably some nice solution for it.
Rather than rolling your own filter, could you use the AutoCompleteBox from the toolkit?
As an alternative, could you categorise the data and make it searchable via a LongListSelector?
Ultimately, if you've got poorly performing code you should use the Profiler to see where the actual bottleneck is. http://msdn.microsoft.com/en-us/library/hh202934(v=vs.92).aspx
精彩评论