gpt4 book ai didi

c++ - 如何在带有 TButtons 的 VCL 表单应用程序中使用键盘箭头进行输入

转载 作者:太空狗 更新时间:2023-10-29 21:12:55 25 4
gpt4 key购买 nike

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

但是,当试图捕捉箭头键(VK_UP 和 VK_DOWN)时,它不起作用,因为它们似乎只是在改变主窗体上控件的焦点,而不是触发主窗体的 OnKeyDown 处理程序。

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

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

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 放置在表单上,​​就不会再触发 case。

我正在使用 C++ Builder XE3。

最佳答案

Windows 保留箭头键用于导航目的。如果一个窗口想要处理箭头键消息,它必须响应 WM_GETDLGCODE消息并在其返回值中包含 DLGC_WANTARROWS(或 DLGC_WANTALLKEYS)标志。

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

TColorGridTToolBarTCustomGridTMediaPlayerTCustomRibbonTRibbonSpinButtonTSpinButtonTTabbedNotebookTCustomComboTTabSet

在您的 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 消息:

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

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

  • Form 窗口本身有输入焦点。

  • 子 VCL 窗口接收键消息,并且窗体的 KeyPreview 属性为真。

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

默认情况下按钮不要求箭头键消息,因此您必须子类化按钮以手动处理 WM_GETDLGCODE 消息,例如:

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

如果您只有几个控件可以子类化,那很好,但是如果您有很多控件,另一种解决方案是让您的 Form 类处理 CM_DIALOGKEY 消息:

void __fastcall TMyForm::WndProc(TMessage &Message)
{
TForm::WndProc(Message);
if (Message.Msg == CM_DIALOGKEY)
{
switch (reinterpret_cast<TCMDialogKey&>(Message).CharCode)
{
case VK_LEFT:
case VK_RIGHT:
case VK_UP:
case VK_DOWN:
// process key as needed...
// set Message.Result to suppress the message further...
Message.Result = 1;
break;
}
}
}

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

A Key’s Odyssey

关于c++ - 如何在带有 TButtons 的 VCL 表单应用程序中使用键盘箭头进行输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45845465/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com