Telerik blogs

When it comes to built-in filtering, autocompleting search results and virtualization the RadAutoCompleteBox is the control to go. The control provides multiple selection, autocomplete and text searching modes with it’s out of the box enabled filtering and virtualization. In most scenarios when there is a need for searching/autocompleting functionality the RadAutoCompleteBox behaves perfectly and gets the job done but there are couple more complex scenarios where it is possible to improve its functionality.

By design controls of the nature of RadAutoCompleteBox need to have their ItemsSource collection set up in order to work and make their magic but there are scenarios where it is not possible or it is not performance perfect to have that ItemsSource collection statically set up. In those scenarios it is necessary to make a call to the server side where data is stored and retrieve only a small portion of it to use as ItemsSource. Such call could be made on each key stroke of the input in the control but it would quickly make the server overflow with calls and eventually jam it. The best approach to handle such issues is to implement a delay after which a call to the server side would be made for the ItemsSource or directly for populating (filtering) functionality of the control.

There are also other scenarios which could benefit of such populate delay like when the users type slower than normal and populate (filtering) functionality should be enabled only after “X” seconds after the last keyboard key press.

Implementing such behavior in the RadAutoCompleteBox control could easily be achieved with its SearchTextChanged and Populating events. In the next example I will demonstrate how to implement minimum populate delay which will enable the populate (filter) functionality of the control only after “X” seconds of no input has been detected.

First we need to create a ViewModel which hold information about the ItemsSource of the control and the custom delay that will be used. For example purpose we will use a ComboBox that will be our delay selector to selected between different delay values (2, 3, 4 or 5 seconds):

public class ViewModel : Telerik.Windows.Controls.ViewModelBase
{
    private ObservableCollection<Item> items;
    private ObservableCollection<int> delays;
    private int selectedDelay;
 
    public ViewModel()
    {
        ...
    }
 
    public int SelectedDelay
    {
         ...
    }
 
    public ObservableCollection<int> Delays
    {
         ...
    }
 
    public ObservableCollection<Item> Items
    {
         ...
    }
}
public class Item
{
    public string Name { get; set; }
 
    public int Number { get; set; }
}

 

Next we need to declare the RadAutoCompleteBox and the delay selector RadComboBox in the xaml. We will use the SelectionChanged event of the RadComboBox to update the selected delay and the SearchTextChanged/Populating events of the RadAutoCompleteBox so make sure that those events are declared:

 

<StackPanel>
    <TextBlock Text="Delay in seconds:"/>
    <telerik:RadComboBox x:Name="DelaysComboBox"
                            ItemsSource="{Binding Delays}"
                            SelectedItem="{Binding SelectedDelay, Mode=TwoWay}"
                            SelectionChanged="OnDelaysComboBoxSelectionChanged"/>
</StackPanel>
<telerik:RadAutoCompleteBox ItemsSource="{Binding Items}"
                            x:Name="AutoCompleteBox"
                            SearchTextChanged="OnAutoCompleteBoxSearchTextChanged"
                            Populating="OnAutoCompleteBoxPopulating"
                            DisplayMemberPath="Name"/>

 

After setting up the controls and the ViewModel now we will need to handle the delaying of the populate functionality of the control. There are multiple approaches that could be used here but the most straightforward one is to:

  • Use the SearchTextChanged event in order to enable and disable the delay timer
  • Handle the Populating event until the custom delay timer has not completed
  • When that timer has completed manually trigger the Populate (filtering) of the RadAutoCompleteBox with it's current SearchText value

For example purposes I will show how to use a DispatcherTimer in order to enable/disable a delay. It is possible to use the described approach with other timer mechanism. First we will need to handle the different way of deleting entered text in the control. We can use the KeyDown event of the control for this purpose:

private DispatcherTimer timer = new DispatcherTimer();
private bool isHandled = true;
private bool isDeleting;

public MainWindow()
{
    InitializeComponent();
    this.timer.Interval = TimeSpan.FromSeconds((this.DataContext as ViewModel).SelectedDelay);
    this.timer.Tick += OnTimerTick;
}
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;
    }
    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;
    }
}

 

After implementing a mechanism that detects when the input is entered or deleted we will need to handle the SearchTextChanged event. That event is triggered on each keystroke and the built-in populate (filtering) functionality is enabled by it. What we want to do is add a delay when that event is triggered and manually enable the Populate with the latest SearchText of the control:

 

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

 

Finally what is left is to use our isHandled field and set it to the CancelRoutedEventArgs of the Populating event of the control in order to disable the built-in automatic filtering:

 

private void OnAutoCompleteBoxPopulating(object sender, CancelRoutedEventArgs e)
{
    e.Cancel = this.isHandled;
}

 

The next image shows the final result of the described approach:


The full source code of the shown example can be found in the Telerik XAML SDK repository at GitHub here. You can also take advantage of our SDK Samples Browser application that provides a more convenient approach in exploring and executing the examples in the Telerik XAML SDK repository. More details could be found in the online documentation for WPF here and Silverlight here.

The SDK Samples Browser is available for download at the following address: http://demos.telerik.com/xaml-sdkbrowser/


VladimirAmiorkov3
About the Author

Vladimir Amiorkov

Vladimir Amiorkov is a Software Developer at Progress. He is currently working with Web, iOS and Android technologies and is a part of the NativeScript team. In his spare time, he enjoys playing computer games such as Diablo and StarCraft.

Related Posts

Comments

Comments are disabled in preview mode.