WPF App: Add Search Box With Filter & Shortcut

by Marco 47 views

Hey guys! Let's dive into adding a nifty search box feature to our WPF application. This will make navigating through our entities a breeze. We're going to focus on creating a search box above the entity list on the left side that filters the entity list box as you type. And yes, it's going to be case-insensitive! Plus, we'll add a cool shortcut key to quickly focus on the search box, similar to what you're used to in Visual Studio. So, buckle up, and let's get started!

Implementing the Search Box Functionality

First off, we need to create a search box in our WPF application. This involves adding a TextBox control above the entity list. We will bind the TextChanged event of the TextBox to a method that filters the entity list. The core of our task is to implement a case-insensitive search that supports the specified search patterns. Let's break down how we'll handle each type of search pattern:

  • Search by Property: When the search string starts with a dot (.), like .ClientId, we will filter entities that contain a property matching "ClientId". This means we need to iterate through the properties of each entity and check if any property name contains the search term. Use reflection to get all properties of an object.
  • Search by Name: If the search string is a simple word like Hello, we will filter entities whose names contain "Hello". This is the most straightforward case, where we directly compare the search term with the entity's name property.
  • Combined Search: For a search string like Hello.Client, we filter entities that have a name containing "Hello" and properties containing "Client". This requires us to combine both the name search and the property search.

To achieve this, we can use the CollectionViewSource to filter the entity list. Every time the text in the search box changes, we will refresh the filter.

Here is some example C# code to illustrate the filtering logic:

private string _searchText;
public string SearchText
{
    get { return _searchText; }
    set
    {
        _searchText = value;
        _entityListCollectionView.Refresh();
        OnPropertyChanged(nameof(SearchText));
    }
}

private bool FilterEntity(object obj)
{
    var entity = obj as YourEntityType; // Replace YourEntityType
    if (entity == null) return false;

    if (string.IsNullOrEmpty(SearchText))
        return true;

    string searchText = SearchText.ToLower();

    if (searchText.StartsWith("."))
    {
        string propertySearch = searchText.Substring(1);
        return entity.GetType().GetProperties()
                     .Any(p => p.Name.ToLower().Contains(propertySearch));
    }
    else if (searchText.Contains("."))
    {
        string[] parts = searchText.Split(".");
        string nameSearch = parts[0];
        string propertySearch = parts[1];

        return entity.Name.ToLower().Contains(nameSearch) &&
               entity.GetType().GetProperties()
                     .Any(p => p.Name.ToLower().Contains(propertySearch));
    }
    else
    {
        return entity.Name.ToLower().Contains(searchText);
    }
}

// In the constructor or initialization method:
_entityListCollectionView = CollectionViewSource.GetDefaultView(_entityList);
_entityListCollectionView.Filter = FilterEntity;

Case-Insensitive Search

To make the search case-insensitive, we can convert both the search term and the entity properties to lowercase before comparison. This ensures that the search works regardless of the casing used by the user. As seen in the example, .ToLower() is used to convert strings to lowercase for comparison.

XAML Implementation

In your XAML, you'll need to bind the Text property of the TextBox to the SearchText property in your ViewModel. Make sure your ViewModel implements the INotifyPropertyChanged interface so that the UI updates when the SearchText changes.

<TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />

This setup ensures that every time the text in the TextBox changes, the SearchText property in your ViewModel is updated, triggering the FilterEntity method to refresh the entity list.

Adding a Shortcut Key to Focus the Search Box

To enhance the user experience, we'll add a shortcut key (Ctrl+,) to focus the search box. This will allow users to quickly jump to the search box without having to use the mouse. We can achieve this by using InputBindings in XAML. Here’s how you can do it:

XAML Configuration

First, add an InputBinding to your main window or user control. This InputBinding will associate the Ctrl+, key combination with a command that focuses on the search box. Here’s the XAML code:

<Window.InputBindings>
    <KeyBinding Key="OemComma" Modifiers="Control" Command="{Binding FocusSearchBoxCommand}" />
</Window.InputBindings>

In this code:

  • Key="OemComma" specifies the comma key. OemComma is used because the comma key can vary based on the keyboard layout.
  • Modifiers="Control" specifies that the Ctrl key must be pressed along with the comma key.
  • Command="{Binding FocusSearchBoxCommand}" binds the key combination to a command in your ViewModel.

ViewModel Implementation

Next, you need to implement the FocusSearchBoxCommand in your ViewModel. This command will set the focus on the search box. Here’s how you can do it:

First, add the necessary namespaces and properties to your ViewModel:

using System.Windows.Input;

public ICommand FocusSearchBoxCommand { get; private set; }

Then, initialize the command in the ViewModel’s constructor:

public YourViewModel()
{
    FocusSearchBoxCommand = new RelayCommand(FocusSearchBox);
}

Here, RelayCommand is a simple implementation of ICommand. If you don't have one, you can easily find an implementation online or create your own.

Finally, implement the FocusSearchBox method:

private void FocusSearchBox()
{
    // Method to focus the search box
    Application.Current.Dispatcher.Invoke(() =>
    {
        Keyboard.Focus(SearchBoxElement);
    });
}

Code-Behind Implementation

In your code-behind, you need a way to access the TextBox element. One way to do this is by naming the TextBox in XAML and then accessing it in the code-behind. This method ensures that the focus operation is performed on the UI thread, preventing cross-thread exceptions. The Keyboard.Focus method sets the keyboard focus to the specified UI element.

Here’s how to set the name in XAML:

<TextBox x:Name="SearchBox" Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />

Then, in your code-behind, you need to handle the Loaded event of the Window or UserControl to set the SearchBoxElement:

public partial class YourView : Window
{
    public YourView()
    {
        InitializeComponent();
        Loaded += YourView_Loaded;
    }

    private void YourView_Loaded(object sender, RoutedEventArgs e)
    {
        ((YourViewModel)DataContext).SearchBoxElement = SearchBox;
    }
}

In your ViewModel, add a property to hold the IInputElement:

public IInputElement SearchBoxElement { get; set; }

RelayCommand Implementation (Optional)

If you don’t have a RelayCommand implementation, here’s a simple one:

using System;
using System.Windows.Input;

public class RelayCommand : ICommand
{
    private readonly Action _execute;
    private readonly Func<bool> _canExecute;

    public RelayCommand(Action execute, Func<bool> canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute();
    }

    public void Execute(object parameter)
    {
        _execute();
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
}

Summary

By following these steps, you can add a shortcut key (Ctrl+,) to focus the search box in your WPF application. This will significantly improve the usability and user experience, allowing users to quickly access the search functionality without needing to use the mouse.

Conclusion

Alright, guys! We've successfully added a fantastic search box feature to our WPF application. With the case-insensitive filtering, search patterns, and the shortcut key, navigating through entities is now super easy. Remember to test thoroughly to ensure everything works smoothly and efficiently. Keep up the great work, and happy coding!