StyleZ
StyleZ

Reputation: 1273

The KeyDown event will not fire, but the exact same code with PreviewMouseLeftButtonDown event will

I am currently working with WPF to create a simple minigame which requires pressing keys. I have done something similar with mouse clicking, yet I am struggling with keys. I have searched for a quite some time and I have found out that the most common way to work with keys is to define each key to its own event. But thats not my case and I want it to be able to fire it everytime any key is pressed. I have found out that this is possible to be done with MVVMLight and EventToCommand, but for some uknown reason to me, the KeyDown event will not fire (neighter KeyUp), but PreviewMouseLeftButtonDown will do.

xaml file:

    <i:Interaction.Triggers>

        // will not fire

        <i:EventTrigger EventName="KeyDown">
            <cmd:EventToCommand Command="{Binding onKeyDown, Mode=OneWay}" PassEventArgsToCommand="True" />
        </i:EventTrigger>

        // will not fire

        <i:EventTrigger EventName="PreviewKeyDown">
            <cmd:EventToCommand Command="{Binding onKeyDown, Mode=OneWay}" PassEventArgsToCommand="True" />
        </i:EventTrigger>

        // will fire

        <i:EventTrigger EventName="PreviewMouseLeftButtonDown">
            <cmd:EventToCommand Command="{Binding onKeyDown, Mode=OneWay}" PassEventArgsToCommand="True" />
        </i:EventTrigger>
    </i:Interaction.Triggers>

ViewModel:

        public DelegateCommand onKeyDown 
        { 
            get
            {
                MessageBox.Show("Down");
                return new DelegateCommand(() => MessageBox.Show("Down"));
            }
        }

(full xaml file)

<UserControl x:Class=".......AsteroidsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Platform"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             Background="{DynamicResource ThemeBackgroundColor}">


    <i:Interaction.Triggers>
        <i:EventTrigger EventName="KeyDown">
            <cmd:EventToCommand Command="{Binding onKeyDown, Mode=OneWay}" PassEventArgsToCommand="True" />
        </i:EventTrigger>
        <i:EventTrigger EventName="PreviewKeyDown">
            <cmd:EventToCommand Command="{Binding onKeyDown, Mode=OneWay}" PassEventArgsToCommand="True" />
        </i:EventTrigger>
        <i:EventTrigger EventName="PreviewMouseLeftButtonDown">
            <cmd:EventToCommand Command="{Binding onKeyDown, Mode=OneWay}" PassEventArgsToCommand="True" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary  Source=".......Module.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>

    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="100" />
            <RowDefinition Height="200*" />
            <RowDefinition Height="50" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50" />
            <ColumnDefinition Width="100*" />
            <ColumnDefinition Width="100*" />
            <ColumnDefinition Width="50" />
        </Grid.ColumnDefinitions>

        <Button Grid.Row="0" Grid.Column="0"
                Style="{StaticResource ReturnBackButtonStyle}"
                Command="{Binding ReturnFromSearchingCommand, Mode=OneWay}" />

        <TextBlock Grid.Row="1" Grid.Column="1"
                   Text="Game"
                   FontSize="48"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center" />

        <TextBlock Grid.Row="1" Grid.Column="2"
                   Text="Controls"
                   FontSize="48"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center" />

        <Canvas Grid.Row="2" Grid.Column="1"
                Background="LightBlue" 
                Name="MyCanvas" 
                Focusable="True">

            <Rectangle Name="player" Height="50" Width="60" Fill="Yellow" Canvas.Left="222" Canvas.Top="495"/>

            <Label Name="scoreText" Content="Score: 0" FontSize="18" FontWeight="Bold" Foreground="White"/>
            <Label Name="damageText" Content="Damage: 0" FontSize="18" FontWeight="Bold" Canvas.Right="0" Foreground="White"/>

        </Canvas>

    </Grid>
</UserControl>

Thanks in advance!

EDIT + UPDATE 1:

I have tried to force it and I have noticed something strange.

<UserControl.InputBindings>
        <KeyBinding Key="Delete"
                    Command="{Binding onKeyDown, Mode=OneWay}" />
    </UserControl.InputBindings>

Using the code above, I am still UNABLE to fire the onKeyDown event. I have no idea why, but I think its something a way way deeper in the code

Upvotes: 0

Views: 1698

Answers (2)

StyleZ
StyleZ

Reputation: 1273

Just in case someone needs the "handle" part, I have noticed that the core problem I had was that I had a different window focused. So, what I have done is that I have focused the keyboard to the Canvas that I needed and those events work like a charm. Here is a code:

    public partial class AsteroidsView : UserControl
    {
        public AsteroidsView()
        {
            InitializeComponent();
            Loaded += AsteroidsView_Loaded;
        }

        private void AsteroidsView_Loaded(object sender, RoutedEventArgs e)
        {
            Window parentWindow = Window.GetWindow(this);
            parentWindow.PreviewKeyDown += ParentWindow_PreviewKeyDown;
        }

        private void ParentWindow_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            Keyboard.Focus(this.MyCanvas);  // `this` keyword is redundant
        }

    }

So, mm8 gave a correct solution, there was just this small piece of a missing puzzle for me. The missing piece of this puzzle was found here -> https://social.msdn.microsoft.com/Forums/vstudio/en-US/20d7dc78-53a0-494a-a3cc-b463a23b8196/keydown-does-not-get-fired-up?forum=wpf where I noticed that you need to have the element you want to use focused and visible

Upvotes: 0

mm8
mm8

Reputation: 169280

There is a PreviewKeyDown event that you can try. The most common reason why it won't work with KeyDown and MouseDown is that the control already handles key presses and mouse interactions internally.

The PreviewKeyDown event will only be fired when the control is focused so you'll also have to set the Focusable property of the UserControl to true.

A better way to make sure that you always capture keypresses in a UserControl would be to handle the keypress event of the parent window programmatically:

public partial class AsteroidsView : UserControl
{
    public AsteroidsView()
    {
        InitializeComponent();
        Loaded += AsteroidsView_Loaded;
    }

    private void AsteroidsView_Loaded(object sender, RoutedEventArgs e)
    {
        Window parentWindow = Window.GetWindow(this);
        parentWindow.PreviewKeyDown += ParentWindow_PreviewKeyDown;
    }

    private void ParentWindow_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        //TODO: handle...
    }
}

Upvotes: 2

Related Questions