EM_EXGETSEL与文本选择顺序无关.如何确定一段选定文本中的插入符号位置

EM_EXGETSEL does not relate to text selection order. How do I do determine the caret position in a piece of selected text?

本文关键字:文本 一段 位置 符号 插入 选择 EXGETSEL 顺序 何确定 EM      更新时间:2023-10-16

当我这样做时:

SendMessage(editControlHWND, EM_EXGETSEL, 0, (LPARAM)&charRange);

我得到了选定的文本范围。 但是,我想知道插入符号在此选择中的位置,即在末尾或开头。

即,用户是否选择了"向后"的文本,例如从右向左拖动。

EM_EXGETSEL总是在cpMin中拥有较小的数字,所以显然与选择顺序无关。

在这种情况下,我显然无法获得带有EM_EXGETSEL进行比较的插入符号位置,因为已经选择了大量内容。

有没有办法获取插入符号的当前个人位置(以便我可以将其与 cpMin/cpMax 进行比较(? 或者,有没有办法确定插入符号在选定文本块中的位置?

编辑:

我对为什么要这样做的解释:我以编程方式将文本插入到用户可以从中选择文本的只读 RichEdit 控件中。 但是,当在末尾添加文本时,它必须将插入符号移动到末尾并插入文本,当已选择文本/用户当前正在选择文本时,可能会发生这种情况。

最后一个是麻烦

。 我使用 EM_EXGETSELEM_EXSETSEL 在以编程方式输入文本之前和之后获取和设置所选文本。 默认情况下,EM_EXGETSEL总是将较小的数字放在cpMin中,这意味着如果用户当前正在向后(即从右到左(选择文本,并且将文本添加到控件中,则插入符号在选择区域中的位置会从开始到结束发生变化,因为我将这些数字直接输入到EM_EXSETSEL中。 我知道EM_EXSETSEL能够向后选择(我已经用cpMin中的较大数字和cpMax中较小的数字对此进行了测试(,但EM_EXGETSEL并没有给出任何指示用户已向后选择文本。

因此,我需要知道插入符号位置以将其与cpMincpMax进行比较,以检查它是在选择的开头还是结尾,并采取相应的行动。

在研究同样的问题时刚刚遇到了这篇文章。

我能够通过跟踪EN_SELCHANGE通知的选择更改并比较WM_LBUTTONUP的结果来解决。

没有

简单的方法可以做到这一点。 EM_GETSELEM_EXGETSEL 返回当前所选内容的范围。仅当没有选择时,它们才会返回插入符号的位置。

请注意,插入符号不能位于所选文本块 - 它始终位于末尾或开头。

通过对控件进行子类化,并使用EM_GETSEL在任何键或鼠标输入后查询和存储插入符号的位置,您可能可以相当容易地实现解决方案。 例如

LRESULT WINAPI EditControlSubclassProc(...)
{
    LRESULT lRes = CallWindowProc(...); // call original window procedure
    if ((uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST)
    ||  (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST))
    {
        DWORD dwStart, dwEnd;
        SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
        if (dwStart == dwEnd)
        {
            // no current selection, so simply store the position of the caret
            g_dwCaretPos = dwStart;
        }
    }
    return lRes;
}

这样,您将始终知道上次输入未导致选择时插入符号的位置。然后,您可以将其与所选内容的范围进行比较,以确定所选内容锚定在哪一端,从而知道插入符号位于另一端。

似乎可以使用 (WPARAM == -1( 的EM_LINEFROMCHAREM_LINEINDEX

我已经设法做到了这一点,尽管由于我对子类的概念缺乏了解,因此到达那里有点复杂。><</p>

我使用Spy++来查看在选择文本时发送了哪些消息。

这显然是专门EM_GETPASSWORDCHAR消息。

所以我做到了:

case EM_GETPASSWORDCHAR:
    {
        if(hwnd == editControlHwnd)
        {
            CHARRANGE tempCharRange;
            SendMessage(editControlHwnd, EM_EXGETSEL, 0, (LPARAM)&tempCharRange);
            SetSelectionDirection(tempCharRange.cpMin, tempCharRange.cpMax);
            return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam);
        }
    }

跟:

void SubWindow::SetSelectionDirection(int newCpMin, int newCpMax) //Set selectionDirection to false if selecting backwards, true if selecting forwards
{
    if((newCpMin != prevCpMin) && (newCpMax == prevCpMax))
        selectionDirection = false;
    else if((newCpMin == prevCpMin) && (newCpMax != prevCpMax))
        selectionDirection = true;
    prevCpMin = newCpMin;
    prevCpMax = newCpMax;
}

其中bool selectionDirection;int prevCpMin;int prevCpMax;是私有类成员变量。

这样,我将新选择的区域与先前选择的区域进行比较,以查看哪一端发生了变化,哪一端没有改变。

我不知道我在这里所做的是否是一种实际解决这个问题的糟糕方法,但如果有更好的方法来做到这一点,我还没有找到它。 这就是为什么我将其作为答案发布,以防它帮助其他人处于我的位置。