1

我在下面对我的问题进行了英文解释,但这是一个视觉问题,所以如果您不想全部阅读,请查看底部的图片)。

我正在为我的班级构建一个反向波兰符号计算器,我刚刚完成让我的 GUI 上的按钮控件能够将它们的值附加到工作正常的编辑控件,但是插入符号正在做一些奇怪的事情,我可以'找不到任何关于它的信息。

我向编辑控件发送一条自定义消息,它在其中找到控件中当前文本的长度,然后将插入符号放在文本的末尾,这样我就可以添加需要添加的文本(它与ES_RIGHT),它再次工作得很好,但是当插入符号位于最正确的位置时,它实际上被放置在大多数任意数字的中间。

这似乎只发生在插入符号可以出现的最正确的位置(即插入符号直接位于前一个字符右侧的任何其他位置,因为它应该)并且我尝试使用代码将插入符号一直替换到右侧,使用我的键盘/鼠标放置它,并尝试调整窗口的尺寸,希望它只是我为它定义的宽度的偏移量,导致最后一个位置略微偏离,但问题仍然存在并且它很难读取文本字段中的最后一个字符。

相关代码:

LRESULT CALLBACK EditBoxClass::WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
    case WM_COMMAND:
        break;
    case WM_APPEND_EDIT:
        /* Get current length of text in the box */
        index = new int( GetWindowTextLength (hWnd) );
        SetFocus( hWnd );
        /* Set the caret to the end of the text in the box */
        SendMessage( hWnd, EM_SETSEL, (WPARAM)index, (LPARAM)index );
        /* "Replace" the selection (the selection is actually targeting 
            nothing and just sits at the end of the text in the box) 
            with the passed in TCHAR* from the button control that 
            sent the WM_APPEND_EDIT message */
        SendMessage( hWnd, EM_REPLACESEL, 0, lParam );
        break;
    }
    return CallWindowProc( EditClassStruct.GetOldProc(), hWnd, msg, wParam, lParam );
}

问题图片:

图片

4

2 回答 2

1

这可能是也可能不是原因,但您正在滥用EM_SETSEL. 您正在int堆上动态分配(和泄漏) an 并将指向它的指针作为消息参数传递,但EM_SETSEL不期望或使用指针开头。所以摆脱动态分配。

此外,默认窗口 proc 不会知道如何处理您的WM_APPEND_EDIT消息,因此将消息传递给CallWindowProc().

试试这个:

case WM_APPEND_EDIT:
{
    /* Get current length of text in the box */
    int index = GetWindowTextLength( hWnd );
    SetFocus( hWnd );
    /* Set the caret to the end of the text in the box */
    SendMessage( hWnd, EM_SETSEL, (WPARAM)index, (LPARAM)index );
    /* "Replace" the selection (the selection is actually targeting 
        nothing and just sits at the end of the text in the box) 
        with the passed in TCHAR* from the button control that 
        sent the WM_APPEND_EDIT message */
    SendMessage( hWnd, EM_REPLACESEL, 0, lParam );
    return 0;
}

话虽如此,尝试使用EM_GETRECT/将编辑控件的格式化矩形EM_SETRECT的右边缘扩大几个像素。这应该给插入符号一些额外的工作空间。

于 2014-05-31T21:02:56.877 回答
1

在面对同样的问题并在这个答案中提出我的第一种方法之后,我现在将提供两个运行良好的解决方案。我认为没有其他方法可以正确修复此故障(除非您是负责这部分 WinAPI 的 Microsoft 程序员)。

我想知道如何在创建的编辑控件上解决这个问题,ES_MULTILINE但这个故障似乎只是单行编辑控件的问题(在 Windows 7 64 位上测试)。启用视觉样式也很有帮助,但问题仍然存在(偏移至少不那么明显)。

解释

通常,当插入符号位于最右侧位置时,它的 x 值(由 提供GetCaretPos ())应等于rect.rightEM_GETRECT(当使用 创建编辑控件时ES_RIGHT)提供的值。由于未知原因,情况并非如此。所以你必须检查插入符号的位置是否至少在rect.right值的附近(但不比最后一个字母的宽度更远)。所以你有两种可能性来完成这个任务:

  1. 您必须使用 计算右外字符的宽度,从调用with提供GetTextExtentPoint32 ()的值中减去它,并检查插入符号的 x 位置是否大于结果或rect.rightSendMessage ()EM_GETRECT
  2. 您必须计算该rect.right值与右外插入符号位置(3在我的情况下)之间的边距,并将该值用作硬编码偏移量以进行简单检查。

在这些步骤之后(无论您选择了哪一个),您必须在必要时重新定位插入符号。

1. 方法(推荐)

    case WM_LBUTTONDOWN: {
        TRACKMOUSEEVENT tme = {sizeof (tme), TME_LEAVE, hwnd, HOVER_DEFAULT};
        TrackMouseEvent (&tme);
    }
    break;

    case WM_KEYDOWN:
    case WM_MOUSELEAVE:
    case WM_SETCURSOR: {
        DefSubclassProc (hwnd, message, wParam, lParam);

        DWORD end;
        SendMessage (hwnd, EM_GETSEL, (WPARAM) NULL, (LPARAM) &end);
        int len = GetWindowTextLength (hwnd);
        if (end < len || len <= 0)
            return TRUE;

        wchar_t *buffer = new wchar_t[len + 1];
        GetWindowText (hwnd, buffer, len + 1);
        wchar_t lastChar[] = {buffer[len - 1], '\0'};
        delete[] buffer;

        SIZE size;
        HDC hdc = GetDC (hwnd);
        if (hdc == NULL)
            return TRUE;

        GetTextExtentPoint32 (hdc, lastChar, 1, &size);
        ReleaseDC (hwnd, hdc);

        POINT pt;
        RECT rect;

        GetCaretPos (&pt);
        SendMessage (hwnd, EM_GETRECT, (WPARAM) 0, (LPARAM) &rect);
        if ((rect.right - size.cx) <= pt.x)
            SetCaretPos (rect.right, pt.y);

        return TRUE;
    }
    break;

2. 方法(改进的原始版本)

    case WM_LBUTTONDOWN: {
        TRACKMOUSEEVENT tme = {sizeof (tme), TME_LEAVE, hwnd, HOVER_DEFAULT};
        TrackMouseEvent (&tme);
    }
    break;

    case WM_KEYDOWN:
    case WM_MOUSELEAVE:
    case WM_SETCURSOR: {
        DefSubclassProc (hwnd, message, wParam, lParam);

        POINT pt;
        RECT rect;

        GetCaretPos (&pt);
        SendMessage (hwnd, EM_GETRECT, (WPARAM) 0, (LPARAM) &rect);
        if ((rect.right - pt.x) <= 3)
            SetCaretPos (rect.right, pt.y);

        return TRUE;
    }
    break;

您必须对编辑控件进行子类化。然后在他们的窗口程序中使用这段代码并享受。在这两种情况下,跟踪鼠标事件并不是绝对必要的,但建议完全避免这种故障。调用DefSubclassProc ()将确保光标在鼠标悬停时改变。

于 2015-11-16T22:33:00.230 回答