Search code examples
data-bindingcollectionviewsourcewindows-phone-7.1

Windows Phone 7 - Performance issues with "Filter as you write"-feature


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


Solution

  • 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