Autocomplete Control Using Rx

    July 02, 2014

    While attempting to implement a “Goggle search like” auto complete control, I looked at different ways of doing it. What I wanted was to have a Auto complete text box ( similar to Google Search) which would display possible results on each keystroke with some sort of minimum delay so that search is not executed until really necessary. Using the Telerik RadAutoComplete WPF control, the usual way would have been to using event handlers to capture key strokes onkeydown event and then perform the search. However, using RX (Reactive Extensions) I found it’s much simpler and able to achieve the objective with less code.

    To start with, the original example (without Rx) is available here: http://blogs.telerik.com/xamlteam/posts/14-04-30/how-to-add-minimum-populate-delay-in-radautocompletebox-for-wpf-sliverlight

    The original code which handled the onkey event handlers and search:

    public MainWindow()
    {
        InitializeComponent();
        this.timer.Interval = TimeSpan.FromSeconds((this.DataContext as ViewModel).SelectedDelay);
        this.AutoCompleteBox.AddHandler(Control.KeyDownEvent, new KeyEventHandler(OnAutoCompleteBoxKeyDown), true);
    }
    
    private void OnAutoCompleteBoxSearchTextChanged(object sender, EventArgs e)
    {
        if (this.isDeleting || string.IsNullOrEmpty(this.AutoCompleteBox.SearchText))
        {
            this.AutoCompleteBox.Populate(string.Empty);
        }
        else
        {
            if (this.timer != null && this.timer.IsEnabled)
            {
                this.timer.Stop();
                this.timer.Start();
            }
            else
            {
                if (this.timer != null)
                {
                    this.timer.Start();
                    this.timer.Tick += OnTimerTick;
                }
            }
    
            this.SetStatusBusyIndicator(true);
            this.DisabledOverlay.Visibility = System.Windows.Visibility.Visible;
        }
    
        this.isHandled = true;
    }
    
    private void OnTimerTick(object sender, EventArgs e)
    {
        this.timer.Stop();
        this.SetStatusBusyIndicator(false);
        this.DisabledOverlay.Visibility = System.Windows.Visibility.Collapsed;
        this.isHandled = false;
        if (!this.isDeleting)
        {
            this.AutoCompleteBox.Populate(this.AutoCompleteBox.SearchText);
        }
    }
    
    private void OnAutoCompleteBoxPopulating(object sender, CancelRoutedEventArgs e)
    {
        e.Cancel = this.isHandled;
    }
    
    private void OnAutoCompleteBoxKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Back || e.Key == Key.Delete || e.Key == Key.Escape || (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.X))
        {
            this.isDeleting = true;
            this.AutoCompleteBox.IsDropDownOpen = false;
            this.SetStatusBusyIndicator(false);
            this.DisabledOverlay.Visibility = System.Windows.Visibility.Collapsed;
        }
        else
        {
            if (e.Key != Key.Up && e.Key != Key.Down)
            {
                this.AutoCompleteBox.IsDropDownOpen = false;
                if (e.Key == Key.Enter && this.isDeleting)
                {
                    this.isHandled = false;
                    this.AutoCompleteBox.Populate(this.AutoCompleteBox.SearchText);
                }
            }
    
            this.isDeleting = false;
        }
    }
    

    Using Reactive UI (based on Rx), I was able to refactor it down to:

    	public ViewModel()
        {
            //GetItems();
            this.delays = new ObservableCollection<int>()
            {
                1, 2, 3, 4, 5
            };
            this.selectedDelay = this.delays[1];
    
            var executeSearchCommand = new ReactiveCommand();
            var results = executeSearchCommand.RegisterAsyncFunction(s => { return ExecuteSearch(s as string); });
            _executeSearchCommand = executeSearchCommand;
    
            this.ObservableForProperty<ViewModel, string>("SearchText")
                .Throttle(TimeSpan.FromMilliseconds(800))
                .Select(x => x.Value)
                .DistinctUntilChanged()
                .Where(x => !string.IsNullOrWhiteSpace(x))
                .Subscribe(_executeSearchCommand.Execute);
    
            _searchResults = new ObservableAsPropertyHelper<ObservableCollection<Item>>(results, _ => raisePropertyChanged("SearchResults"));
    
        }
    

    So you can cut down a lot of event handling code using Rx and reckon much easier to read without the need for multiple event handler methods.

    The full code sample is available on: https://github.com/rubans/gitprojects/tree/master/RadAutoCompleteExample/MinimumPopulateDelayRx

    Installing Ruby on Windows, the easy Way

    June 26, 2014

    There are various ways to setup Ruby and Rails on Windows, but the easiest and quickest way I found to do it was using the Rails bundler for windows found here: http://railsinstaller.org/en

    If you then want to go further and install Jekyll, use the following:

    cd C:/Railsinstaller 
    gem install jekyll
    

    That’s it.

    Next »