检测箭头键 - 一次

问题描述:

我试图让简单的应用程序多个按键,让用户能够走动箭头。到目前为止,我设法让用户上下左右重写ProcessCmdKey()方法。检测箭头键 - 一次

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 
     { 
      if (keyData == Keys.Up) 
      { 
       statek.Y--; 
       return true; 
      } 
      if (keyData == Keys.Left) 
      { 
       statek.X--; 
       return true; 
      } 
      if (keyData == Keys.Right) 
      { 
       statek.X++; 
       return true; 
      } 
      if (keyData == Keys.Down) 
      { 
       statek.Y++; 
       return true; 
      } 
      return base.ProcessCmdKey(ref msg, keyData); 
     } 

问题是,我很乐意让用户沿着对角线走。喜欢在同一时间。我试图寻找一些解决方案,但不幸的是没有任何运气。 我会尝试修改它,使左/右按键转/旋转,所以向上键将意味着“向前”,而不是“涨”。但首先,我需要立即制作两把钥匙。

所以问题是,我怎样才能使一次使用2个键来对角移动?

PS。 ATM,我在PictureBox中移动一个椭圆,改变Point statek = new Point(250, 470);线,所以每次计时器滴答声,则检查X或Y改变,在新的地方画出来。经常蜱 - 这就是我现在试图实现实时运动的方式。这不会是一个复杂的应用程序。

首先创建位枚举持有箭头键指出:

[Flags] 
enum ArrowsPressed 
{ 
    None = 0x00, 
    Left = 0x01, 
    Right = 0x02, 
    Up = 0x04, 
    Down = 0x08, 
    All = 0x0F 
} 

然后一成员跟踪状态,并改变它的函数;

ArrowsPressed arrowsPressed; 

void ChangeArrowsState(ArrowsPressed changed, bool isPressed) 
{ 
    if (isPressed) 
    { 
     arrowsPressed |= changed; 
    } 
    else 
    { 
     arrowsPressed &= ArrowsPressed.All^changed; 
    } 
} 

覆盖的KeyDown和KeyUp(不要忘记设置形式到真正的KeyPreview属性,让形式收到的情况下,密钥的子控件想偷他们;

protected override void OnKeyDown(KeyEventArgs e) 
{ 
    base.OnKeyDown(e); 
    switch (e.KeyCode) 
    { 
     case Keys.Down: 
      ChangeArrowsState(ArrowsPressed.Down, true); 
      break; 
     case Keys.Up: 
      ChangeArrowsState(ArrowsPressed.Up, true); 
      break; 
     case Keys.Left: 
      ChangeArrowsState(ArrowsPressed.Left, true); 
      break; 
     case Keys.Right: 
      ChangeArrowsState(ArrowsPressed.Right, true); 
      break; 
     default: 
      return; 
    } 
    HandleArrows(); 
    e.Handled = true; 
} 
protected override void OnKeyUp(KeyEventArgs e) 
{ 
    base.OnKeyUp(e); 
    switch (e.KeyCode) 
    { 
     case Keys.Down: 
      ChangeArrowsState(ArrowsPressed.Down, false); 
      break; 
     case Keys.Up: 
      ChangeArrowsState(ArrowsPressed.Up, false); 
      break; 
     case Keys.Left: 
      ChangeArrowsState(ArrowsPressed.Left, false); 
      break; 
     case Keys.Right: 
      ChangeArrowsState(ArrowsPressed.Right, false); 
      break; 
     default: 
      return; 
    } 
    e.Handled = true; 
} 

最后,使用一个Point结构握住你的地位测试的所有跟踪键和方向移动一点上,他们表示,使用GetKeyboardState

Point position; 
private void HandleArrows() 
{ 
    if ((arrowsPressed & ArrowsPressed.Down) != ArrowsPressed.None) 
    { 
     position.Y++; 
    } 
    if ((arrowsPressed & ArrowsPressed.Up) != ArrowsPressed.None) 
    { 
     position.Y--; 
    } 
    if ((arrowsPressed & ArrowsPressed.Left) != ArrowsPressed.None) 
    { 
     position.X--; 
    } 
    if ((arrowsPressed & ArrowsPressed.Right) != ArrowsPressed.None) 
    { 
     position.X++; 
    } 
    // Do whatever is needed using position 
} 
+0

这是一个很好的答案。 – 2012-03-06 01:13:13

+0

不起作用,关键停止重复。计时器是必需的。 – 2012-03-06 01:29:14

+0

@Hans是的,你是绝对正确的。 Windows为最后一个按键发送WM_KEYDOWN消息。当这个键被释放时,移动停止。在这种情况下,计时器肯定会有所帮助,但如果使用计时器,它将支付Pinvoke用于GetKeyboardState。 – 2012-03-06 02:20:14

您需要保留当前处于Key_Down状态的所有键的某种类型的列表。

这允许您跟踪哪些按键被按下并决定哪些组合是有效的。

每个事件触发一次按键向下/向上事件。因此,在一个事件中检测两个键是不可能的,但您可以在事件范围外的集合中跟踪此信息。

改进版这一个围绕定时器的Wi时间。将调用一个函数来检查按下的键,构造ArrowsPressed枚举并调用一个函数来执行移动。如果你想使用一些其他的键把它放在virtualKeyToArrowsPressed Dictionary中。

const int VK_LEFT = 0x25; 
const int VK_UP = 0x26; 
const int VK_RIGHT = 0x27; 
const int VK_DOWN = 0x28; 

[DllImport("user32.dll")] 
[return: MarshalAs(UnmanagedType.Bool)] 
static extern bool GetKeyboardState(byte[] lpKeyState); 

byte[] keys = new byte[256]; 

[Flags] 
enum ArrowsPressed 
{ 
    None = 0x00, 
    Left = 0x01, 
    Right = 0x02, 
    Up = 0x04, 
    Down = 0x08, 
    All = 0x0F 
} 
Dictionary<int, ArrowsPressed> virtualKeyToArrowsPressed = new Dictionary<int, ArrowsPressed> 
{ 
    { VK_LEFT, ArrowsPressed.Left }, 
    { VK_RIGHT, ArrowsPressed.Right }, 
    { VK_UP, ArrowsPressed.Up }, 
    { VK_DOWN, ArrowsPressed.Down }, 
}; 

protected override void OnLoad(EventArgs e) 
{ 
    base.OnLoad(e); 

    timer1.Tick += timer1_Tick; 
    timer1.Interval = 100; 
    timer1.Start(); 
} 

void timer1_Tick(object sender, EventArgs e) 
{ 
    if (GetKeyboardState(keys)) 
    { 
     ArrowsPressed arrowsPressed = ArrowsPressed.None; 
     foreach (KeyValuePair<int, ArrowsPressed> kvp in virtualKeyToArrowsPressed) 
     { 
      if ((keys[kvp.Key] & 0x80) != 0) 
      { 
       arrowsPressed |= kvp.Value; 
      } 
     } 
     if (arrowsPressed != ArrowsPressed.None) 
     { 
      HandleArrows(arrowsPressed); 
     } 
    } 
} 

Point position; 
private void HandleArrows(ArrowsPressed arrowsPressed) 
{ 
    if ((arrowsPressed & ArrowsPressed.Down) != ArrowsPressed.None) 
    { 
     position.Y++; 
    } 
    if ((arrowsPressed & ArrowsPressed.Up) != ArrowsPressed.None) 
    { 
     position.Y--; 
    } 
    if ((arrowsPressed & ArrowsPressed.Left) != ArrowsPressed.None) 
    { 
     position.X--; 
    } 
    if ((arrowsPressed & ArrowsPressed.Right) != ArrowsPressed.None) 
    { 
     position.X++; 
    } 
    // Do whatever is needed using position 
}