Martijn
Martijn

Reputation: 542

Catch a range of characters with keybindings in xaml

I'm developing a feature where a user can press 1 trough 9 or 'a' through 'z' that executes a command in a list. I built support for the numbers, but I don't really like the way I made it.

<Grid.InputBindings>
    <KeyBinding Command="{Binding ShortcutCharacterCommand}" Key="D0" CommandParameter="0" />
    <KeyBinding Command="{Binding ShortcutCharacterCommand}" Key="D1" CommandParameter="1" />
    <KeyBinding Command="{Binding ShortcutCharacterCommand}" Key="D2" CommandParameter="2" />
    <KeyBinding Command="{Binding ShortcutCharacterCommand}" Key="D3" CommandParameter="3" />
    <KeyBinding Command="{Binding ShortcutCharacterCommand}" Key="D4" CommandParameter="4" />
    <KeyBinding Command="{Binding ShortcutCharacterCommand}" Key="D5" CommandParameter="5" />
    <KeyBinding Command="{Binding ShortcutCharacterCommand}" Key="D6" CommandParameter="6" />
    <KeyBinding Command="{Binding ShortcutCharacterCommand}" Key="D7" CommandParameter="7" />
    <KeyBinding Command="{Binding ShortcutCharacterCommand}" Key="D8" CommandParameter="8" />
    <KeyBinding Command="{Binding ShortcutCharacterCommand}" Key="D9" CommandParameter="9" />
</Grid.InputBindings>

If I implement the remaining characters in the same manner, I'll have a huge list of bindings. Not only to support the letters, but also the numpad. I'd rather test for ranges of characters like I did with a Winform control in another part of our application with the code:

if (e.KeyValue >= '1' && e.KeyValue <= '9' ||
    e.KeyValue >= 'A' && e.KeyValue <= 'Z')
    {
        FavoriteShortcutKeyPressedCallBack.Raise(e.KeyValue);
    } 

I really think it is possible but I can't seem to work out a solution or find one on the internet that adheres to the MVVM pattern.

So basically my question is, how can this be done in WPF/MVVM in a more generic, elegant manner?

How I solved it

I took the suggestion from the answer of mm8 to use EventToCommandBinding. This resulted in the following code in the XAML:

<i:Interaction.Behaviors>
    <behaviors:EventToCommandBehavior Event="PreviewTextInput"
                                      Command="{Binding TextInputCommand}"
                                      PassArguments="True" />
</i:Interaction.Behaviors>

The ViewModel has a TextInputCommand that reads the text from the EventArgs and selects the corresponding item.

public RelayCommand<TextCompositionEventArgs> TextInputCommand { get; set; }

private void HandleTextInputCommand(TextCompositionEventArgs args)
{
   SelectItemBoundToShortcut(args.Text);
}

At first I used the KeyDown event as user1672994 suggested in the comments. But found out that I had to account for different keyboard layouts and check separately for the numpad characters. Using the PreviewTextInput event just sends the typed text, which is exactly what I need.

Upvotes: 0

Views: 191

Answers (1)

mm8
mm8

Reputation: 169200

You could handle the PreviewKeyDown event. Either in the code-behind of the view, from where you then invoke the command of the view model:

private void Grid_PreviewKeyDown(object sender, KeyEventArgs e)
{
    var viewModel = DataContext as YourViewModel;
    if (viewModel != null)
    {
        switch (e.Key)
        {
            case Key.D0:
                viewModel.ShortcutCharacterCommand.Execute("0");
                break;
            case Key.D1:
                viewModel.ShortcutCharacterCommand.Execute("1");
                break;
                //...
        }
    }
}

Or by using an interaction trigger as explained here:

MVVM Passing EventArgs As Command Parameter

You may also wrap the functionality defined in the event handler in an attached behaviour.

Upvotes: 2

Related Questions