繁体   English   中英

如何在WinAPI中的ESC keydown上取消Tree-View控件中的项目标签编辑

[英]How to cancel item label editing in Tree-View control upon ESC keydown in WinAPI

我有一个带有Tree-View控件的对话框,用户可以在其中编辑项目标签。 我希望用户能够通过按ESC键取消标签编辑。

问题是按ESC会立即关闭对话窗口。

我试图通过TVN_BEGINLABELEDIT消息上的TreeView_GetEditControl()调用来获取EditBox控件的句柄,并将其子类化以捕获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;
    }
}

PS我注意到当我在EditBoxCtl_SubclassProc()删除WM_GETDLGCODE处理程序时,可以再次输入编辑框,但是我不能从该程序中为ESC键捕获WM_KEYDOWN。

以下是我找到的解决方案。 诀窍似乎是调用原始控制proc,WM_GETDLGCODE在子类proc中截获,存储返回值,然后返回DLGC_WANTALLKEYS或DLGC_WANTMESSAGE标志设置,以防止系统进一步处理击键。

这种方法的好处是按ESC取消编辑并将项标签恢复为原始文本, 在编辑时按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,只需使用PreTranslateMessage,但在普通的WinApi中不可用。 您无权访问内部消息循环和键盘界面。

因此,Escape键在消息循环内处理。 并导致发送带有IDCANCEL的WM_COMMAND消息。 (请参阅有关对话框MSDN规范

也许最简单的方法是拦截发送到对话框的WM_COMMAND消息,检查是否有焦点,如果就地编辑控件有焦点,你只需将焦点设置回树控件并吃掉忘记IDCANCEL并且不要关闭对话框。

当你收到TVN_BEGINLABELEDIT (在类成员中,与对话框相关联)时,你需要记住树视图hwnd,并在收到TVN_ENDLABELEDIT时将其TVN_ENDLABELEDIT 当用户按下esc进入模态对话框时 - 你会收到带有IDCANCEL (在esc上 )或IDOK (在输入时 )的WM_COMMAND 你需要检查保存的树视图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;
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM