我有一个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。
答案 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_WANTALLKEYS
,TColorGrid
,TToolBar
,TCustomGrid
,TMediaPlayer
,TCustomRibbon
,TRibbonSpinButton
,TSpinButton
,TTabbedNotebook
和TCustomCombo
在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如何实现可用于拦截关键消息的各种钩子:
答案 1 :(得分:1)
我这样做有点不同(不需要 WinAPI )。如果您的 VCL 应用不关注任何子组件(例如TButton,TEdit,TMemo
...),则会触发主窗体键盘事件(OnKeyDown
和OnKeyUp
)箭头键使用。我知道有两种方法:
不要使用有焦点的组件
听起来可能很傻但你可以TSpeedButton
而不是TButton
。但基本上你可以将所有可聚焦组件放在某个子窗口或页面中,如果不需要则使其不可见(如设置窗口)。
需要箭头时禁用焦点
我只需要制作一个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
之类的任何此类组件上点击转义时调用它,因为它们通常在我的应用程序中共享相同的事件处理程序。全部取决于您需要/想要的功能。
PS 希望您将KeyPreview
属性设置为true
。