停止程序继续WM_KEYDOWN过程

时间:2016-11-25 14:07:40

标签: winapi

我不确定它是否是我问题的正确措辞,但问题围绕着它。我有2个盒子都在KillFocus上验证。如果用户按下Next按钮,则调用另一个方法,该按钮调用评估是否可以继续的方法,验证这些字段。

由于这个代码库的年龄有多大,修改它会导致其他地方出现问题所以我需要找到一种解决方法而不改变调用can continue序列的方式。这是一些场景。

用户在字段1中输入无效值,按Enter键,程序触发kill focus方法并显示错误消息,回车键按下下一个按钮,然后再次验证它再次显示错误(不同的MsgBox同样错误)。意思是除非他们手动取消对焦,然后按回车键,它们将始终显示两个消息框。

我认为这是由于上述原因,因为他们已经按下了输入而导致焦点消失,而不是仅仅调用下一个。

如果在KillFocus方法中失败,是否有办法停止整个WM_KEYDOWN路径?

对不起,如果这有点含糊不清,这就是我认为正在发生的事情。

2 个答案:

答案 0 :(得分:1)

  

@DavidHeffernan你知道以WM_KILLFOCUS的方式验证字段的任何其他方法吗?

请允许我提出建议。您可以在EN_CHANGE处理程序中验证编辑控件的输入。来自文档:

当用户执行可能在编辑控件中更改文本的操作时发送。

每次用户输入内容时,您都会收到此通知,这似乎是验证数据的好地方。

如果数据无效,您将使用EnableWindow 禁用下一步按钮并以某种方式指示错误。

您可以使用EM_SHOWBALLOONTIP弹出工具提示并显示错误消息,或者只是将编辑控件的背景颜色更改为红色。

以下是说明我的观点的小例子。你当然应该添加更好的错误检查,但主要的想法是:

#include <windows.h>
#include <CommCtrl.h>

#define IDC_BTN_NEXT    1000
#define IDC_BOX1        2000
#define IDC_BOX2        3000

// enable Visual Styles
#pragma comment( linker, "/manifestdependency:\"type='win32' \
                         name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
                         processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
                         language='*'\"")

// link with Common Controls library
#pragma comment( lib, "comctl32.lib") 

void onBtnNext()
{
    MessageBeep(0);
}

void onKillFocus(HWND box)
{
    //==================== these are needed to disable Next button
    HWND hwnd = ::GetParent(box);

    if (NULL == hwnd)   // critical error
        return;         // TODO: add error handling

    HWND btnNext = ::GetDlgItem(hwnd, IDC_BTN_NEXT);

    if (NULL == btnNext)    // critical error
        return;             // TODO: add error handling
    //==============================================================

    int len = ::GetWindowTextLength(box);

    if (0 == len)   // it is ok, empty text, just return
        return;

    // if possible, use std::wstring here, I assumed you can't...
    wchar_t *txt = new wchar_t[len +1];     

    if (0 == ::GetWindowText(box, txt, len + 1))    // critical error, according to documentation
    {
        // TODO: add error handling
        delete[] txt;
        return;                                     
    }

    //====== simple validation for illustration only, treat uppercase letter as error 
    int isTextValid = ::isupper(txt[0]);

    for (int i = 1; 0 == isTextValid && i < (len + 1); isTextValid = ::isupper(txt[++i]));

    delete[] txt;
    //==============================================

    if (isTextValid)
    {
        EDITBALLOONTIP ebt;

        ebt.cbStruct = sizeof(EDITBALLOONTIP);
        ebt.pszText = L" Tooltip text";
        ebt.pszTitle = L" Tooltip title";
        ebt.ttiIcon = TTI_ERROR_LARGE;   

        if (!::SendMessage(box, EM_SHOWBALLOONTIP, 0, (LPARAM)&ebt))
        {
            //TODO: tooltip won't show, handle error
        }

        EnableWindow(btnNext, FALSE);   // disable Next button

        return;     // our work is successfully done
    }

    if (!::SendMessage(box, EM_HIDEBALLOONTIP, 0, 0))   
    {
        //TODO: tooltip won't hide, handle error
    }

    EnableWindow(btnNext, TRUE);    // enable Next button
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE: 
    {
        HWND hwndBox1 = CreateWindowEx(0, WC_EDIT, L"", 
            WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
            20, 20, 250, 20, hwnd, (HMENU)IDC_BOX1, 
            ((LPCREATESTRUCT)lParam)->hInstance, 0);

        if (NULL == hwndBox1)   // add better error handling, this is for illustration only
            return -1;

        HWND hwndBox2 = CreateWindowEx(0, WC_EDIT, L"", 
            WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
            20, 50, 250, 20, hwnd, (HMENU)IDC_BOX2, 
            ((LPCREATESTRUCT)lParam)->hInstance, 0);

        if (NULL == hwndBox2)   // add better error handling, this is for illustration only
            return -1;

        HWND hwndBtnNext = CreateWindowEx(0, WC_BUTTON, L"Next",
            WS_CHILD | WS_VISIBLE | BS_CENTER | BS_DEFPUSHBUTTON,
            20, 80, 50, 25, hwnd, (HMENU)IDC_BTN_NEXT, 
            ((LPCREATESTRUCT)lParam)->hInstance, 0);

        if (NULL == hwndBtnNext)    // add better error handling, this is for illustration only
            return -1;

    }
        return 0L;
    case WM_COMMAND:        
    {
        switch (HIWORD(wParam))
        {
        case BN_CLICKED:
        {
            if (LOWORD(wParam) != IDC_BTN_NEXT)
                break;

            onBtnNext();
        }
            break;
        case EN_CHANGE:
        {
            if (LOWORD(wParam) != IDC_BOX1 && (LOWORD(wParam) != IDC_BOX2))
                break;

            onKillFocus((HWND)lParam);
        }
            break;
        default:
            break;
        }
    }
        break;
    case WM_CLOSE:
        ::DestroyWindow(hwnd);
        return 0L;
    case WM_DESTROY:
    {
        ::PostQuitMessage(0);
    }
        return 0L;
    default:
        return ::DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
    int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = L"Main_Window";
    wc.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);

    if (!RegisterClassEx(&wc))
        return 0;

    INITCOMMONCONTROLSEX iccex;
    iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    iccex.dwICC = ICC_STANDARD_CLASSES;
    InitCommonControlsEx(&iccex);

    hwnd = CreateWindowEx(0, L"Main_Window", L"Test",
        WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION, 
        50, 50, 305, 160, NULL, NULL, hInstance, 0);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

答案 1 :(得分:0)

这个问题在一个非传统的&#34;方式,但它的工作原理。我注意到,通过调试,程序在框中失去了两次焦点,一次按下ENTER,一次弹出消息框。

我使用静态bool来避免我的程序执行错误检查两次。它看起来像这样 -

void onKillFocus()
{
    static bool isValidated = false;
    if(!isValidated)
    {
        isValidated = true;
        if(/*ValidationCheck*/)
        {
            //messagebox for error
        }
    }
}

通过使用它,验证仅在焦点被终止时运行一次,停止消息框出现两次,因为静态bool仅在方法运行时才存活,这意味着它每次都被重置叫做killfocus。