简体   繁体   English

Win32编辑控件插入符放置偏移

[英]Win32 Edit Control Caret Placement Offset

I gave an English explanation of my problem below but it is a visual issue so if you don't want to read it all just look at the picture at the bottom). 我在下面提供了关于我的问题的英语说明,但这是一个视觉问题,因此,如果您不想全部阅读,只需查看底部的图片即可。

I'm working on building a reverse polish notation calculator for my class and I just completed having the button controls on my GUI be able to append their values to the edit control which works fine, but the caret is doing something weird and I can't find any information on it. 我正在为我的班级构建一个反向修饰符号计算器,我刚刚完成了GUI上的按钮控件能够将其值附加到可以正常工作的edit控件的操作,但是插入符号的操作很奇怪,我可以找不到任何信息。

I send a custom message to the edit control in which it finds the length of the current text in the control and then places the caret at the end of the text so I can then add what text needs to be added (it is right aligned with ES_RIGHT ), which again works just fine, but when the caret is in the right most place it can be, it is placed practically right through the middle of most any number. 我向编辑控件发送一条自定义消息,在其中它会在控件中找到当前文本的长度,然后将插入号放在文本的末尾,这样我就可以添加需要添加的文本了(它与ES_RIGHT ),它仍然可以正常工作,但是当插入号位于可能的最右边时,它实际上被放置在几乎任何数字的中间。

This only seems to happen in the right most place the caret can be (ie anywhere else the caret sits directly to the right of the preceding char, as it should) and I have tried replacing the caret all the way to the right using code, placing it using my keyboard/mouse, and tried adjusting the dimensions of the window in hopes that it was just an offset of the width I defined for it that caused the last place to be off slightly, but the problem persists and it makes it hard to read the last char in the text field. 这似乎只发生在插入符号可能位于的最右边的位置(即插入符号直接位于前一个字符右侧的任何位置),我尝试使用代码将插入符号一直替换到正确的位置,使用键盘/鼠标将其放置,并尝试调整窗口的尺寸,希望它只是我为其定义的宽度的偏移量,从而导致最后一个位置略微偏离,但问题仍然存在并且使其变得困难读取文本字段中的最后一个字符。

Relevant Code: 相关代码:

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 );
}

Picture of problem: 问题图片:

图片

This may or may not be the cause, but you are misusing EM_SETSEL . 这可能是原因,也可能不是原因,但是您滥用EM_SETSEL You are dynamically allocating (and leaking) an int on the heap and passing a pointer to it as the message parameters, but EM_SETSEL does not expect or use pointers to begin with. 您是在堆上动态分配(和泄漏)一个int并将一个指针作为消息参数传递给它,但是EM_SETSEL并不期望或使用指针开头。 So get rid of the dynamic allocation. 因此摆脱动态分配。

Also, the default window proc is not going to know how to handle your WM_APPEND_EDIT message, so there is no point in passing the message to CallWindowProc() . 另外,默认的窗口proc不会知道如何处理WM_APPEND_EDIT消息,因此将消息传递给CallWindowProc()毫无意义。

Try this instead: 尝试以下方法:

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;
}

That being said, try using EM_GETRECT / EM_SETRECT to expand the right edge of the edit control's formatting rectangle by a few pixels. 话虽如此,请尝试使用EM_GETRECT / EM_SETRECT将编辑控件的格式矩形的右边缘扩展几个像素。 That should give the caret some extra room to work with. 那应该给插入符号更多的工作空间。

After facing the same problem and presenting my first approach in this answer, I'll now provide two well working solutions. 面对相同的问题并在此答案中提出我的第一种方法后,我现在将提供两个运行良好的解决方案。 I think there is no other way to fix this glitch properly (unless you're a Microsoft programmer who is responsible for this part of the WinAPI). 我认为没有其他方法可以正确解决此故障(除非您是负责WinAPI这一部分的Microsoft程序员)。

I was wondering how to fix this problem on edit controls created with ES_MULTILINE but this glitch seems to be only a problem on single-line edit controls (tested on Windows 7 64-bit). 我想知道如何在使用ES_MULTILINE创建的编辑控件上解决此问题,但是这种故障似乎仅是单行编辑控件(在Windows 7 64位上测试)上的问题。 Enabling Visual Styles is also helpful but the problem still remains (the offset is at least not so obvious). 启用“视觉样式”也很有帮助,但问题仍然存在(偏移至少不是那么明显)。

Explanation 说明

Normally, when the caret is at the farthest right position it's x value (provided by GetCaretPos () ) should be equal to the rect.right value provided by EM_GETRECT (when the edit control was created with ES_RIGHT ). 通常情况下,当插入符号是在最右边位置它的x值(由提供GetCaretPos ()应等于) rect.right由设置值EM_GETRECT (当编辑控制用创建ES_RIGHT )。 Due to unknown reasons this is not the case. 由于未知原因,情况并非如此。 So you have to check if the caret position is at least in the near of the rect.right value (but not farther away than the last letter is wide). 因此,您必须检查插入符号的位置是否至少在rect.right值的附近(但不比最后一个字母宽)。 So you have two possibilities to fulfill this task: 因此,您有两种可能性可以完成此任务:

  1. You must calculate the width of the outer right character using GetTextExtentPoint32 () , subtract it from the rect.right value provided by calling SendMessage () with EM_GETRECT and check whether the x position of the caret is bigger than the result or not OR 必须计算使用右侧的外字符的宽度GetTextExtentPoint32 ()中,从减去它rect.right通过调用提供的值SendMessage ()EM_GETRECT并检查插入符号的x位置是否大于结果更大或不OR
  2. You must calculate the margin between the rect.right value and the outer right caret position ( 3 in my case) and use this value as a hardcoded offset to do a simple check. 您必须计算rect.right值和右插入符位置之间的rect.right (在我的情况下为3 ),并将此值用作硬编码偏移量以进行简单检查。

After those steps (regardless which one you have chosen) you have to reposition the caret when necessary. 在执行这些步骤之后(无论您选择了哪一个),都必须在必要时重新放置插入标记。

1. Approach (recommended) 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. Approach (improved original version) 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;

You have to subclass the edit controls. 您必须将编辑控件子类化。 Then use this code in their window procedures and enjoy. 然后在他们的窗口程序中使用此代码并欣赏。 In both cases, tracking the mouse event is not absolutely necessary but recommended to completly avoid this glitch. 在这两种情况下,跟踪鼠标事件都不是绝对必要的,但建议您完全避免这种故障。 Calling DefSubclassProc () will ensure that the cursor is changed on mouse over. 调用DefSubclassProc ()将确保在鼠标悬停时更改光标。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM