【问题标题】:Win32 Edit Control Caret Placement OffsetWin32 编辑控件插入符号放置偏移
【发布时间】:2014-05-31 18:14:52
【问题描述】:

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

我正在为我的班级构建一个反向波兰符号计算器,我刚刚完成让我的 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 );
}

问题图片:

【问题讨论】:

  • index = new int( GetWindowTextLength (hWnd) ); 创建一个指向 int 的指针,然后泄漏,而且可能不是您想要的。 index 的声明在哪里?

标签: winapi win32gui windows-messages windows-controls


【解决方案1】:

这可能是也可能不是原因,但您误用了EM_SETSEL。您正在堆上动态分配(和泄漏)int 并将指向它的指针作为消息参数传递,但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 将编辑控件的formatting rectangle 的右边缘扩大几个像素。这应该给插入符号一些额外的工作空间。

【讨论】:

  • 我想我没有放入所有相关代码,因为我将指针存储在我在框架中创建的类中。指针不会导致内存泄漏,因为它在对象存在时存在,并且对象的析构函数会清除它。我将取消引用的指针传递给它,因为它指向的值是当前窗口文本的大小。我将它设置为指针,因此我不必在创建对象期间对其进行初始化,但我会尝试将其更改为变量,看看是否可行。我想矩形的扩展更有可能起作用。
  • 我尝试将索引设为直接 int 而不是 int*,在编辑从 EM_GETRECT 返回的 RECT 后发送 EM_SETRECT,并使用 EM_SETMARGINS 手动调整边距。我看到更改生效,因此我知道它们已正确实施,但插入符号的最右侧位置仍直接位于数字“4”以及其他几个数字的中间。我想我只需要处理它。
  • 您确实存在内存泄漏,因为您每次收到WM_APPEND_TEXT 消息时都会分配一个新的int,而不是释放之前的int。但是这个问题没有实际意义,因为你不应该首先使用指针,因为 EM_SETSEL 不支持 int* 指针。
  • 很公平,但由于某种原因,该函数似乎与 int* 一起使用。但是,是的,这是一个没有实际意义的问题,因为在您第一次回复后,我也只用一个 int 变量尝试过它,但它什么也没改变。正如我在第二个回复中所说,我尝试调整矩形大小、边距大小、手动放置插入符号,并使用 int 和 int* 放置插入符号,但没有解决问题。尝试的每种方法仍然会留下插入符号可以占据的最右边的位置,直接覆盖在最右边字符位置的大多数字符上。
【解决方案2】:

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

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

说明

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

  1. 你必须用GetTextExtentPoint32 ()计算右外字符的宽度,用EM_GETRECT调用SendMessage ()提供的rect.right值减去它,并检查插入符号的x位置是否大于结果或不或
  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 () 将确保光标在鼠标悬停时改变。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-24
  • 2014-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多