【问题标题】:The KeyDown event will not fire, but the exact same code with PreviewMouseLeftButtonDown event willKeyDown 事件不会触发,但与 PreviewMouseLeftButtonDown 事件完全相同的代码将
【发布时间】:2021-09-17 02:03:07
【问题描述】:

我目前正在使用 WPF 创建一个需要按键的简单小游戏。我用鼠标点击做了类似的事情,但我在按键上挣扎。我已经搜索了很长一段时间,我发现使用键的最常见方法是将每个键定义为自己的事件。但这不是我的情况,我希望它能够在每次按下任何键时触发它。我发现这可以使用 MVVMLight 和 EventToCommand 来完成,但由于某些我不知道的原因,KeyDown 事件不会触发(更接近的 KeyUp),但 PreviewMouseLeftButtonDown 会。

xaml 文件:

    <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>

视图模型:

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

(完整的 xaml 文件)

<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>

提前致谢!

编辑 + 更新 1:

我试图强迫它,但我发现了一些奇怪的东西。

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

使用上面的代码,我仍然无法触发onKeyDown 事件。我不知道为什么,但我认为它在代码中更深层次的东西

【问题讨论】:

  • 那么试试PreviewKeyDown?
  • @mm8 这个我也试过了,但是效果不好
  • 只有在控件获得焦点时才会触发该事件。尝试在UserControl 上将Focusable 设置为true。

标签: c# wpf mvvm mvvm-light


【解决方案1】:

以防万一有人需要“句柄”部分,我注意到我遇到的核心问题是我有一个不同的窗口焦点。所以,我所做的就是将键盘集中在我需要的画布上,这些事件就像一个魅力。这是一个代码:

    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
        }

    }

所以,mm8 给出了一个正确的解决方案,对我来说只有一小块缺失的拼图。在这里找到了这个难题的缺失部分 -> https://social.msdn.microsoft.com/Forums/vstudio/en-US/20d7dc78-53a0-494a-a3cc-b463a23b8196/keydown-does-not-get-fired-up?forum=wpf 我注意到您需要拥有要使用的元素 focused 和 visible

【讨论】:

    【解决方案2】:

    您可以尝试PreviewKeyDown 事件。它不能与KeyDown 和MouseDown 一起工作的最常见原因是控件已经在内部处理了按键和鼠标交互。

    PreviewKeyDown 事件只会在控件获得焦点时触发,因此您还必须将UserControl 的Focusable 属性设置为true。

    确保您始终在UserControl 中捕获按键的更好方法是以编程方式处理父窗口的按键事件:

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

    【讨论】:

    • 感谢您的快速回复。您提供的解决方案按预期工作,但是,我想问您,是否有可能直接在 xaml 中重新创建类似的东西。我问的原因是我需要在 ViewModel 中处理它,但是这样它是在 View 本身中处理的。提前感谢您的回复!
    • 没有。 XAML 是一种标记 语言。您可以创建自己的附加行为,或者只是在视图的PreviewKeyDown 中调用视图模型的命令。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-29
    • 1970-01-01
    • 2021-12-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多