2

I am trying to implement a Search as you type functionality ( like the one in the search in the default email app ) - I have a listbox with say 50 items - each item bound to a class object which has string fields ... I wish to search and display items which have the text in the search box in one of their string fields - this as the user keys in to the textbox ... tried a couple of approaches -->

1 >> Using a CollectionViewSource
- Bound a CollectionViewSource to All the items from the DB
- Bound the List Box to a CollectionViewSource
- Setting the filter property of the CollectionViewSource - to whose function which searchs for the text in the Search box in the items and set the e.Accepted - on every keyup event
- Filtering works fine but its slow with 50 items :( - guess cos of filter taking each item and checking if to set e.Accepted property to true
.... One DB call on load but seems to be a lot of processing to decide which element to disply in the filer by the CollectionViewSource

2 >> Filter @ DB level
- on keyup - sending the text in the search box to the ViewModel where a function returns an ObservableCollection of objects which has the search string
- ObservableCollection is bound to the listbox
.... Not much processing @ the top layer but multiple DB calls on each Keypress - still slow but just a tad faster than Approach One

Is there any other approch you would recommend ? or any suggestions to further optimize the above mentioned approaches? - any tweak to give smooth functioning for the search?

First time into mobile dev :) ... Thanx in advance :)

chancyjohn
  • 25
  • 4
  • here you can look for the samples http://stackoverflow.com/questions/5782585/filtering-an-observablecollection – JSJ Jun 04 '12 at 10:31
  • Hi I have already done that .. See approach 1 ... Was wondering if anyone has any ideas to improve the performance ... It's ok with a handful of items but is noticeably slow with 50 items... – chancyjohn Jun 04 '12 at 12:13
  • then i think you should go with read only items. that will defiantly increase the performance. – JSJ Jun 04 '12 at 12:18
  • Oh ... Hmm read only ... U mean set the table as read only ? I also will add items to the list ... The save is in another page how ever – chancyjohn Jun 04 '12 at 12:34

1 Answers1

1

You can optimize the search process further by delaying the search action by x milliseconds to allow the user to continue the writing of the search criteria. This way, each new typed char will delay the search action by xxx milliseconds till the last char where the action is triggered, you'll get a better performance using just one call instead of say 10 calls. Technically speaking, you can achieve this by using a Timer class. Here you'll find a sample source code example that shows u how to implement this.

    private void txtSearchCriteria_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (txtSearchCriteria.Text == "Search ...")
            return;

        if (this.deferredAction == null)
        {
            this.deferredAction = DeferredAction.Create(() => ApplySearchCriteria());
        }

        // Defer applying search criteria until time has elapsed.
        this.deferredAction.Defer(TimeSpan.FromMilliseconds(250));
    }


    private DeferredAction deferredAction;


    private class DeferredAction
    {
        public static DeferredAction Create(Action action)
        {
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }

            return new DeferredAction(action);
        }

        private Timer timer;

        private DeferredAction(Action action)
        {
            this.timer = new Timer(new TimerCallback(delegate
            {
                Deployment.Current.Dispatcher.BeginInvoke(action);
            }));
        }

        public void Defer(TimeSpan delay)
        {
            // Fire action when time elapses (with no subsequent calls).
            this.timer.Change(delay, TimeSpan.FromMilliseconds(-1));
        }
    }

    public void ApplySearchCriteria()
    {
        ICollectionView dataView = this.ViewSource.View;

        string criteria = this.txtSearchCriteria.Text;
        string lowerCriteria = criteria.ToLower();
        using (dataView.DeferRefresh())
        {
            Func<object, bool> filter = word =>
            {
                bool result = word.ToString().ToLower().StartsWith(lowerCriteria);
                return result;
            };

            dataView.Filter = l => { return filter.Invoke(l); };
        }

        this.lstWords.ItemsSource = dataView;                       
        if (this.lstWords.Items.Count != 0)
        {
            this.lblError.Visibility = Visibility.Collapsed;
        }
        else
        {
            this.lblError.Visibility = Visibility.Visible;
        }
    }
Waleed
  • 3,105
  • 2
  • 24
  • 31