如何在对子控件进行子类化时告诉要处理哪些消息?

时间:2018-02-02 16:29:18

标签: winapi

我想将编辑控件子类化为蒙版编辑的特定情况 - 接受五个用户输入字符,并在第一个和第三个字符后显示冒号。我可以想象两种基本方法。

我可以将编辑控件存储的文本作为我想要显示的文本。在这种情况下,我需要将文本设置为L" ::"首先,并覆盖检测用户输入的消息,以便我可以将其复制到该字符串中的正确插槽中。但是,我不知道如何确定它们是哪些消息。我假设WM_KEYDOWN是一个,但是如果有其他人,并且我没有全部想到它们,那么触发我错过的消息的输入将错误地推迟到编辑控件的默认处理。

或者,我可以让编辑控件存储的文本是用户输入的文本 - 没有冒号。在这种情况下,我需要覆盖控件的显示方式,这样我就可以生成一个包含基于存储文本的冒号的字符串,并在绘制控件时绘制它。我认为这意味着要替换WM_PAINT处理。问题在于它似乎需要重新定义所有关于控件如何看待自己,当文本是我想要改变的唯一部分时。我不相信我能完美地做到这一点,我当然不会这样做。

我该如何处理?

编辑:我已尝试覆盖WM_PAINT,如下所示:

INT_PTR CALLBACK MaskedEditProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam,
    UINT_PTR  uIdSubclass, DWORD_PTR dwRefData)
{
    if (message == WM_PAINT)
    {
        WCHAR userInput[6];
        Edit_GetText(hwndDlg, userInput, 6);
        WCHAR displayString[]{L" :  :  "};
        int userInputLength{ Edit_GetTextLength(hwndDlg) };
        switch (userInputLength)
        {
        case 5:
            displayString[6] = userInput[4];
        case 4:
            displayString[5] = userInput[3];
        case 3:
            displayString[3] = userInput[2];
        case 2:
            displayString[2] = userInput[1];
        case 1:
            displayString[0] = userInput[0];
        }
        Edit_SetText(hwndDlg, displayString);
        DefSubclassProc(hwndDlg, message, wParam, lParam);
        Edit_SetText(hwndDlg, userInput);
        return TRUE;
    }
    return DefSubclassProc(hwndDlg, message, wParam, lParam);
}

这似乎基本上有效,除了某些原因导致显示的文字闪烁。

编辑2:我将控件的文本设置为L" 0:00:00"从其父窗口,并给它以下窗口过程:

INT_PTR CALLBACK MaskedEditProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam,
    UINT_PTR  uIdSubclass, DWORD_PTR dwRefData)
{
    switch (message)
    {
    case WM_CUT:
        return 0;
    case WM_PASTE:
        return 0;
    case WM_KEYDOWN:
        if (wParam == VK_DELETE)
        {
            WORD caretPosition{ LOWORD(SendMessage(hwndDlg,EM_GETSEL,0,0)) };
            switch (caretPosition)
            {
            case 7:
                return 0;
            case 1:
            case 4:
                SendMessage(hwndDlg, EM_SETSEL, caretPosition + 1, caretPosition + 2);
                break;
            default:
                SendMessage(hwndDlg, EM_SETSEL, caretPosition, caretPosition + 1);
            }
            return DefSubclassProc(hwndDlg, WM_CHAR, '0', 0);
        }
    case WM_CHAR:       
        if (wParam == '\b')
        {
            WORD caretPosition{ LOWORD(SendMessage(hwndDlg,EM_GETSEL,0,0)) };
            switch (caretPosition)
            {
            case 0:
                return 0;
            case 2:
            case 5:
                SendMessage(hwndDlg, EM_SETSEL, caretPosition - 2, caretPosition - 1);
                break;
            default:
                SendMessage(hwndDlg, EM_SETSEL, caretPosition - 1, caretPosition);
            }
            return DefSubclassProc(hwndDlg, WM_CHAR, '0', 0);
        }
        else if (iswdigit(wParam))
        {
            WORD caretPosition{ LOWORD(SendMessage(hwndDlg,EM_GETSEL,0,0)) };
            switch (caretPosition)
            {
            case 1:
            case 4:
                SendMessage(hwndDlg, EM_SETSEL, caretPosition + 1, caretPosition + 2);
                break;
            default:
                SendMessage(hwndDlg, EM_SETSEL, caretPosition, caretPosition + 1);
            }
        }
    }
    return DefSubclassProc(hwndDlg, message, wParam, lParam);
}

这似乎按预期工作,虽然我的退格键一直被打破,所以我还没有测试过那部分。控制也设置为不接受非数字输入,所以我不认为我不会处理这些事实应该打破任何事情。

1 个答案:

答案 0 :(得分:0)

我建议您只处理WM_CHAR(以及可能与您的屏蔽相关的自定义消息)。单独保留箭头键,让编辑控件处理它们,并在WM_CHAR处理程序中查询插入符号放置。在您的WM_CHAR中,当展示位置通常会到达冒号时,会发送一个跳过下一个字符位置的EM_SETSEL。