Reputation: 1273
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
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
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