我有一个带有树视控制的对话框,用户可以在其中编辑项目标签。我希望用户能够通过按ESC键取消标签编辑。
问题是按ESC立即关闭对话框窗口。
我已经尝试通过 TreeView_GetEditControl()
呼叫在tvn_beginlabeledit消息上获取handerbox控件,然后将其子类串联以捕获ESC键,但是当我这样做时,在编辑框中输入不可能。
有什么问题?
这是相关代码:
INT_PTR CALLBACK DlgProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam) {
switch(message) {
//...
case WM_NOTIFY:
{
LPNMHDR pNmHdr = (LPNMHDR)lParam;
switch(pNmHdr->code) {
case TVN_BEGINLABELEDIT:
{
HWND hwndTV = (HWND)GetWindowLongPtr(hWnd, GWLP_USERDATA); // stored handle to Tree-View ctl
HWND hWndEditBox = TreeView_GetEditControl(hwndTV);
// subclass edit box
TreeViewGlobals::g_wpOrigEditBoxProc =
(WNDPROC)SetWindowLongPtr(hWndEditBox,
GWLP_WNDPROC, (LONG_PTR)EditBoxCtl_SubclassProc);
break;
}
case TVN_ENDLABELEDIT:
{
SetWindowLongPtr(hWnd, DWLP_MSGRESULT, (LONG)TRUE); // accept edit
return TRUE;
}
default:
break;
}
}
default:
break;
}
return FALSE;
}
INT_PTR CALLBACK EditBoxCtl_SubclassProc(HWND hWndEditBox, UINT message,
WPARAM wParam, LPARAM lParam) {
switch(message) {
HANDLE_MSG(hWndEditBox, WM_GETDLGCODE, EditBoxCtl_OnGetDlgCode);
HANDLE_MSG(hWndEditBox, WM_KEYDOWN, EditBoxCtl_OnKey); // does not receive WM_KEYDOWN for ESC unless I handle WM_GETDLGCODE above
default:
break;
}
return CallWindowProc(TreeViewGlobals::g_wpOrigEditBoxProc,
hWndEditBox, message, wParam, lParam);
}
UINT EditBoxCtl_OnGetDlgCode(HWND hWndEditBox, LPMSG lpmsg) {
if(lpmsg) {
if(lpmsg->message == WM_KEYDOWN && lpmsg->wParam == VK_ESCAPE) {
return DLGC_WANTMESSAGE;
}
}
return 0;
}
void EditBoxCtl_OnKey(HWND hWndEditBox, UINT vk, BOOL fDown,
int cRepeat, UINT flags) {
switch(vk) {
case VK_ESCAPE:
Beep(4000, 150); // never beeps
break;
default:
break;
}
}
P.S。我注意到,当我在EditBoxCtl_SubclassProc()
中删除WM_GetDlgCode处理程序时,可以再次在编辑框中输入,但是我无法从该过程中捕获ESC键的WM_KEYDOWN。
下面是我发现的解决方案。技巧似乎是用wm_getDlgcode键入子类Proc中的原始控制Proc,存储返回值,然后用DLGC_WANTALLKEYS或DLGC_WANTMESSAGE FLAG设置返回,以防止系统进一步处理密钥。
这种方法的好处是,按ESC取消编辑并将项目标签恢复为其原始文本, and 按ENTER,编辑不再仅关闭对话框(这是另一个问题),没有<<任何其他代码来处理这些情况。
这是有效的代码:
INT_PTR CALLBACK EditBoxCtl_SubclassProc(HWND hWndEditBox, UINT message,
WPARAM wParam, LPARAM lParam) {
switch(message) {
//HANDLE_MSG(hWndEditBox, WM_GETDLGCODE, EditBoxCtl_OnGetDlgCode); // can't use this: need wParam and lParam for CallWindowProc()
case WM_GETDLGCODE: {
INT_PTR ret = CallWindowProc(TreeViewGlobals::g_wpOrigEditBoxProc,
hWndEditBox, message, wParam, lParam);
MSG* lpmsg = (MSG*)lParam;
if(lpmsg) {
if(lpmsg->message == WM_KEYDOWN &&
(lpmsg->wParam == VK_ESCAPE || lpmsg->wParam == VK_RETURN) )
{
return ret | DLGC_WANTALLKEYS;
}
}
return ret;
}
default:
break;
}
return CallWindowProc(TreeViewGlobals::g_wpOrigEditBoxProc,
hWndEditBox, message, wParam, lParam);
}
问题是模态对话框具有其自己的消息循环,并带有isDialogMessage自己的翻译。我想说的是使用MFC,只需使用prestlanslatemessage,但这在Plain Winapi中不可用。您无法访问内部消息循环和键盘接口。
因此,在消息循环内处理了逃生键。并导致带有IDCancel的WM_Command消息发送。(请参阅有关对话框的MSDN规格)
也许最简单的方法是与发送到对话框的WM_COMMAND消息相关联,检查谁是否具有焦点,以及Intplote Control是否具有焦点,您只需将重点放在树上,然后吃掉忘记IDCancel和Don't关闭对话框。
您需要在接收 TVN_BEGINLABELEDIT
(在类成员中,与对话框关联)时请记住树视图HWND,并在接收TVN_ENDLABELEDIT
时为零。当用户按 esc 或 enter 在模态对话框中 - 您将带有IDCANCEL
(ON ESC )或IDOK
(ON ent Enter <)接收WM_COMMAND
/em>)。您需要检查保存的树视图HWND,如果不是0-呼叫TreeView_EndEditLabelNow
switch (uMsg)
{
case WM_INITDIALOG:
m_hwndTV = 0;
break;
case WM_NOTIFY:
switch (reinterpret_cast<NMHDR*>(lParam)->code)
{
case TVN_BEGINLABELEDIT:
m_hwndTV = reinterpret_cast<NMHDR*>(lParam)->hwndFrom;
return TRUE;
case TVN_ENDLABELEDIT:
m_hwndTV = 0;
//set the item's label to the edited text
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
}
break;
case WM_CLOSE:
EndDialog(hwndDlg, 0);
break;
case WM_COMMAND:
switch (wParam)
{
case IDCANCEL:
if (m_hwndTV)
{
TreeView_EndEditLabelNow(m_hwndTV, TRUE);
}
else
{
EndDialog(hwndDlg, IDCANCEL);
}
break;
case IDOK:
if (m_hwndTV)
{
TreeView_EndEditLabelNow(m_hwndTV, FALSE);
}
else
{
EndDialog(hwndDlg, IDOK);
}
break;
}
break;
}