如何使用键盘箭头输入带有TButton的VCL表单应用程序

时间:2017-08-23 16:57:39

标签: c++ c++builder vcl

我有一个VCL表单应用程序,我正在拦截在OnKeyPress和OnKeyDown处理程序中按下的键。这适用于常规字符和键,例如VK_ESC。

但是,当尝试捕获箭头键(VK_UP和VK_DOWN)时,它不起作用,因为它们似乎只是更改主窗体上控件的焦点,并且不会触发主窗体OnKeyDown处理程序。 / p>

如何捕获/处理这些按键?

更新: 根据以下答案尝试以下内容,但仍然没有成功。

void __fastcall TMain::WndProc(TMessage& Message)
{
  switch (Message.Msg)
  {
    case WM_GETDLGCODE:
        Log(lInfo) << "Got WM_GETDLGCODE";
    break;
    case CM_WANTSPECIALKEY:
        Log(lInfo) << "Got WM_WANTSPECIALKEY";
    break;

使用我的应用程序中的向上/向下箭头不会触发上述任何情况。

我做了以下观察。如果创建一个空的vcl表单,上面的情况触发,首先触发CM_WANSPECIALKEY,然后是WM_GETDLGCODE。

但是,只要将TButton放置在表单上,​​就不会再触发案例了。

我正在使用C ++ Builder XE3。

2 个答案:

答案 0 :(得分:2)

Windows保留箭头键用于导航。如果窗口想要处理箭头键消息,则它必须响应WM_GETDLGCODE消息并在其返回值中包含<style> #nav_logo { width: 100%; margin-top: 20px; } </style> (或DLGC_WANTARROWS)标记。

大多数VCL UI组件(包括DLGC_WANTALLKEYS)都为TForm返回0。只有少数标准VCL组件会回复WM_GETDLGCODE(或DLGC_WANTARROWS):

DLGC_WANTALLKEYSTColorGridTToolBarTCustomGridTMediaPlayerTCustomRibbonTRibbonSpinButtonTSpinButtonTTabbedNotebookTCustomCombo

TTabSet类中,覆盖其虚拟TForm方法,或声明WndProc()处理程序,以处理message,然后您可以返回任何标记需要。

WM_GETDLGCODE

更新:如果void __fastcall TMyForm::WndProc(TMessage &Message) { TForm::WndProc(Message); if (Message.Msg == WM_GETDLGCODE) Message.Result |= DLGC_WANTARROWS; } 无效,请尝试回复WM_GETDLGCODE消息:

CM_WANTSPECIALKEY

更新:您正在尝试处理表单本身的关键事件,该事件仅在以下情况下有效:

  • “表单”窗口本身具有输入焦点。

  • 子VCL窗口接收关键消息,且Form void __fastcall TMyForm::WndProc(TMessage &Message) { TForm::WndProc(Message); if (Message.Msg == CM_WANTSPECIALKEY) { switch (reinterpret_cast<TCMWantSpecialKey&>(Message).CharCode) { case VK_LEFT: case VK_RIGHT: case VK_UP: case VK_DOWN: Message.Result = 1; break; } } } 属性为true。

当窗口化的子控件(如按钮)具有输入焦点时,键消息将转到该窗口,而不是窗体窗口。为了使KeyPreview属性适用于箭头键,焦点子节点必须响应KeyPreview消息,要求操作系统发送箭头键消息。只有这样,孩子才能将消息转发给表单进行处理。

默认情况下,按钮不会询问箭头键消息,因此您必须将按钮子类化为手动处理WM_GETDLGCODE消息,例如:

WM_GETDLGCODE

如果你只有几个控件来进行子类化,那很好,但如果你有很多控件,另一种解决办法就是让你的Form类处理class TMain : public TForm { __published: TButton *Button1; void __fastcall FormKeyDown(TObject *Sender, Word &Key, TShiftState Shift); void __fastcall FormKeyPress(TObject *Sender, Char &Key); private: TWndMethod PrevBtnWndProc; void __fastcall BtnWndProc(TMessage &Message); public: __fastcall TMain(TComponent *Owner); }; __fastcall TMain::TMain(TComponent *Owner) : TForm(Owner) { PrevBtnWndProc = Button1->WindowProc; Button1->WindowProc = &BtnWndProc; } void __fastcall TMain::BtnWndProc(TMessage &Message { PrevBtnWndProc(Message); if (Message.Msg == WM_GETDLGCODE) Message.Result |= DLGC_WANTARROWS; } void __fastcall TMain::FormKeyDown(TObject *Sender, Word &Key, TShiftState Shift) { // works now! } void __fastcall TForm7::FormKeyPress(TObject *Sender, Char &Key) { // works now! } 消息:

CM_DIALOGKEY

你应该阅读关于EDN的以下文章,它很好地解释了VCL如何处理关键消息,以及VCL如何实现可用于拦截关键消息的各种钩子:

A Key’s Odyssey

答案 1 :(得分:1)

我这样做有点不同(不需要 WinAPI )。如果您的 VCL 应用不关注任何子组件(例如TButton,TEdit,TMemo ...),则会触发主窗体键盘事件(OnKeyDownOnKeyUp)箭头键使用。我知道有两种方法:

  1. 不要使用有焦点的组件

    听起来可能很傻但你可以TSpeedButton而不是TButton。但基本上你可以将所有可聚焦组件放在某个子窗口或页面中,如果不需要则使其不可见(如设置窗口)。

  2. 需要箭头时禁用焦点

    我只需要制作一个unfocus函数:

    void main_unfocus()
        {
        Main->bt_unfocus->Visible=true;
        Main->bt_unfocus->SetFocus();
        Main->bt_unfocus->Visible=false;
        }
    

    其中bt_unfocus不可见2x2 TButton位于主窗口Main的左上角。现在每当我需要捕获主窗体事件中的鼠标滚轮,箭头键等事件时,我只需调用main_unfocus();

    我通常在鼠标点击某个查看区域或鼠标移动它等等时调用它。此外,我在TButton,TMemo,TEdit之类的任何此类组件上点击转义时调用它,因为它们通常在我的应用程序中共享相同的事件处理程序。全部取决于您需要/想要的功能。

  3. PS 希望您将KeyPreview属性设置为true