duilib : VT_TAB被屏蔽的折中解决方法

duilib391中,当焦点落在Edit中, WM_KEYDOWN就收不到了,wparam == VT_TAB自然也就收不到。

这就导致了Edit中不能用Tab键切换到另外一个Edit.

这看起来很怪. 比如一个登录对话框, 使用者在输入用户名之后,用鼠标点击另外一个口令输入框,才能继续输入口令.


手头有一个duilib1.0做的登录对话框,支持Tab键切换, 回车键执行登录.

将这个框框挪到duilib391的版本,全部失效。

记得看过duilib的svn日志,说是取消了Tab键的支持,原因是WebBrowser控件会有问题.


今天和同事讨论后,用了个折中的方法.

使用SetWindowHookEx, 检测TAB键, 回车键, ESC键, 使登录对话框Tab键可以切换, 回车键执行登录, ESC键退出登录框.

在SetWindowsHookEx中指定的键盘回调中,PostMessage到主程序, 主程序再PostMessage或直接调用登录对话框中的方法, 来执行TAB键,ENTER键,ESC键的处理.


typedef struct _tag_HOOKDATA 
{ 
    int nType; 
    HOOKPROC hkProc; 
    HHOOK hHook; 
} TAG_HOOK_DATA; 

/// Hook键盘产生的数据, 动机 : 当焦点在Edit中时, DuiLib不传递 WM_KEYxx + VT_TAB
/// 下全局钩子, 不采用DLL注入方式, 只监控本程序的键盘输入
/// 当有Tab键按下时, PostMessage 到主程序中处理, 
/// 当此时, 登录框出现时, 就切换用户名和口令的Edit焦点
TAG_HOOK_DATA g_KbHookData;
HWND g_hHwndMain = NULL;

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    static LONG_PTR lVkCodePrev_Tab = 0;
    static LONG_PTR lVkCode_Tab = 0;

    static LONG_PTR lVkCode_Enter = 0;
    static LONG_PTR lVkCode_Cancel = 0;

    LONG_PTR        lTemp = 0;

    /** when tab key press down and up
    KeyboardProc : nCode = 0x0, wParam = 0x9, lParam = 0xF0001
    KeyboardProc : nCode = 0x0, wParam = 0x9, lParam = 0xC00F0001
    */

    if (0 == nCode)
    {
        switch (wParam)
        {
        case VK_TAB:
            {
                lVkCode_Tab = (LONG_PTR)lParam;
                lTemp = lVkCode_Tab & 0xFFF00000;
                if (0 == lTemp)
                {
                    // tab key press down
                    lVkCodePrev_Tab = lVkCode_Tab;
                }
                else
                {
                    // tab key press up
                    if (lVkCode_Tab != lVkCodePrev_Tab)
                    {
                        lVkCodePrev_Tab = lVkCode_Tab;
                        if (NULL != g_hHwndMain)
                        {
                            ::PostMessageW(g_hHwndMain, WM_TAB_KEY_PRESS, 0, 0);
                        }
                    }
                }
            }
            break;
        case VK_RETURN:
            {
                lVkCode_Enter = (LONG_PTR)lParam;
                lTemp = lVkCode_Enter & 0xFFF00000;
                if (lTemp > 0)
                {
                    // key press up
                    if (NULL != g_hHwndMain)
                    {
                        ::PostMessageW(g_hHwndMain, WM_ENTER_KEY_PRESS, 0, 0);
                    }
                }
            }
            break;
        case VK_ESCAPE:
            {
                lVkCode_Cancel = (LONG_PTR)lParam;
                lTemp = lVkCode_Cancel & 0xFFF00000;
                if (0 == lTemp)
                {
                    // key press down
                    if (NULL != g_hHwndMain)
                    {
                        ::PostMessageW(g_hHwndMain, WM_CANCEL_KEY_PRESS, 0, 0);
                    }
                }
            }
            break;
        default:
            break;
        }
    }

    return CallNextHookEx(g_KbHookData.hHook, nCode, wParam, lParam);
}

窗口出来后,有消息循环时, 执行SetWindowsHookEx

    g_hHwndMain = this->GetHWND();
    g_KbHookData.nType = WH_KEYBOARD;
    g_KbHookData.hkProc = KeyboardProc;
    g_KbHookData.hHook = ::SetWindowsHookEx( 
        g_KbHookData.nType,
        g_KbHookData.hkProc, 
        (HINSTANCE)NULL,
        GetCurrentThreadId());

窗口销毁前, UnHook

    case WM_DESTROY:
        {
            /// 窗口临近销毁时, SendMessageW不到消息处理函数了, 阻塞了
            /// so 窗口销毁时, 做个标记, 不允许再执行 SendMessage的操作
            m_bWmDestroy = TRUE;
            if (NULL != g_KbHookData.hHook)
                UnhookWindowsHookEx(g_KbHookData.hHook); 
            ProcessBeforeQuit();
        }
        break;

消息的处理:

    case WM_TAB_KEY_PRESS:
        {
            if (NULL != m_pLoginDlg)
                m_pLoginDlg->TabPressUpProcess();
        }
        break;

    case WM_ENTER_KEY_PRESS:
        {
            if (NULL != m_pLoginDlg)
                m_pLoginDlg->EnterPressUpProcess();
        }
        break;

    case WM_CANCEL_KEY_PRESS:
        {
            if (NULL != m_pLoginDlg)
                m_pLoginDlg->CancelPressUpProcess();
        }
        break;

登录对话框消失后,要再设置一下鼠标光标.

void CMainDlg::DoLogin()
{
    if (NULL == m_pLoginDlg)
    {
        m_pLoginDlg = new CLoginDlg(XML_FILE_NAME_LOGIN_DLG, WND_CLASS_NAME_LOGIN_DLG);
        if (NULL != m_pLoginDlg)
        {
            m_pLoginDlg->SetOwner(this);
            m_pLoginDlg->Create(this->GetHWND(), WND_DISP_NAME_LOGIN_DLG, UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
            m_pLoginDlg->CenterWindow();
            m_pLoginDlg->ShowModal();
            m_pLoginDlg = NULL;

            /// 防止回车登录成功后, 鼠标光标必须右击一下才出现正常的鼠标光标.
            ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)));
        }
    }
}

登录对话框中的键处理:

BOOL CLoginDlg::TabPressUpProcess()
{
    if (m_peditUserName->IsFocused())
        m_peditUserPwd->SetFocus();
    else
        m_peditUserName->SetFocus();

    return TRUE;
}

BOOL CLoginDlg::EnterPressUpProcess()
{
    m_PaintManager.SendNotify(m_pbtnOk, DUI_MSGTYPE_CLICK);
    return TRUE;
}

BOOL CLoginDlg::CancelPressUpProcess()
{
    this->PostMessageW(WM_CLOSE, 0, 0);
    return TRUE;
}




  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值