5

I have a WinForms program where, whenever you change your selection, the RichTextBox needs to change the colour of certain other text. In order to do this, it has to select that text, and therefore I lose my current selection.

I can save and load the SelectionStart and SelectionLength properties, but I can't keep the "selection direction": if the user was highlighting forwards or backwards from the cursor.

Any ideas about how I can either save the selection direction, or colour the text without having to change the selection?

4

3 回答 3

2

我刚刚遇到了同样的问题,现在我通过使用 EM_EXSETSEL 解决了这个问题。当 cpMin > cpMax 时,它的工作方式类似于“向后选择”(插入符号位于所选文本的开头)。然而我还没有找到任何其他方法来找出当前的选择方向(EM_EXGETSEL 总是返回 cpMin < cpMax),但是随着 SelectionStart/Length 的变化......

编辑:

这就是我用来解决这个问题的方法。可能有一些更简单的方法,但至少以下对我有用。

using System.Runtime.InteropServices;

//********************
//SendMessage stuff for EM_EXSETSEL
//********************

[StructLayout(LayoutKind.Sequential)]
public struct CHARRANGE
{
    public int cpMin;
    public int cpMax;
}

[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, ref CHARRANGE lParam);

private const UInt32 WM_USER = 0x0400;
private const UInt32 EM_EXSETSEL = WM_USER + 55;
private const UInt32 EM_EXGETSEL = WM_USER + 52;

//********************
//event handlers
//********************

//locking variable to avoid stack overflow while setting selection in code
private bool richTextBox1_SelectionChanged_lock = false;

//handler for richTextBox selection change event
private void richTextBox1_SelectionChanged(object sender, EventArgs e)
{
    if (richTextBox1_SelectionChanged_lock) return;
    richTextBox1_SelectionChanged_lock = true;

    //detect selection changes and store information needed for restoring
    TrackRTBSelection(richTextBox1.SelectionStart, richTextBox1.SelectionLength);

    //here do whatever you want with selection (some analysis to show font name in font selection comboBox etc.)
    //...

    //restore selection from saved informations
    SetRTBSelectionBasedOnTracking();

    richTextBox1_SelectionChanged_lock = false;
}

//sample button click handler for changing fore color of selected text
private void buttonSetForeColor_Click(object sender, EventArgs e)
{
    if (colorDialog1.ShowDialog() == DialogResult.Cancel)
        return;

    //prevent selection change events while we are changing font colors
    if (richTextBox1_SelectionChanged_lock) return;
    richTextBox1_SelectionChanged_lock = true;

    //save selection parameters for use in loop
    int selStart = richTextBox1.SelectionStart;
    int selLength = richTextBox1.SelectionLength;

    for (int i = 0; i < selLength; i++)
    {
        richTextBox1.SelectionLength = 1;
        richTextBox1.SelectionStart = selStart + i;

        richTextBox1.SelectionColor = colorDialog1.Color;
    }

    //restore selection from saved informations
    SetRTBSelectionBasedOnTracking();

    richTextBox1_SelectionChanged_lock = false;
}

//********************
//selection tracking utilities
//********************

//false - caret at the beginning; true - caret at the end
private bool caretPosition = false;
private int lastSelectionStart = -1;
private int lastSelectionLength = -1;

//initialize selection informations. this must be called during Form_Load
private void InitRTBSelection()
{
    richTextBox1.SelectionStart = 0;
    richTextBox1.SelectionLength = 0;

    caretPosition = false;
    lastSelectionStart = 0;
    lastSelectionLength = 0;

    //force "selection changed" to detect "selection changes" for the first time
    richTextBox1_SelectionChanged(richTextBox1, new EventArgs());
}

//this method detects changes in selection, based on selection parameters received from richTextBox
private void TrackRTBSelection(int newSelectionStart, int newSelectionLength)
{
    int condition = 0;

    int s_change = (newSelectionStart - lastSelectionStart > 0) ?
                    1 :
                    (newSelectionStart - lastSelectionStart < 0) ? -1 : 0;
    int l_change = (newSelectionLength - lastSelectionLength > 0) ?
                    1 :
                    (newSelectionLength - lastSelectionLength < 0) ? -1 : 0;

    //these conditions where created over change table for all user-achievable scenarios
    condition = (newSelectionLength == 0 ||
                (l_change == 1 && s_change == -1) ||
                (l_change == -1 && s_change == 1 && caretPosition == false)) ? 1 : condition;
    condition = (s_change == 0 && (l_change == 1 || (caretPosition == true && l_change == -1))) ? 2 : condition;

    switch (condition)
    {
        case 1: caretPosition = false; break;
        case 2: caretPosition = true; break;
        default: break; //if no condition was satisfied then maintain current information
    }

    lastSelectionStart = newSelectionStart;
    lastSelectionLength = newSelectionLength;
}

//set richTextBox selection using EM_EXSETSEL
private void SetRTBSelectionBasedOnTracking()
{
    CHARRANGE chrrange = new CHARRANGE
    {
        cpMin = caretPosition ? lastSelectionStart : lastSelectionStart + lastSelectionLength,
        cpMax = caretPosition ? lastSelectionStart + lastSelectionLength : lastSelectionStart
    };
    SendMessage(richTextBox1.Handle, EM_EXSETSEL, IntPtr.Zero, ref chrrange);
}
于 2015-07-02T09:39:58.967 回答
1

哎呀,丑陋的问题。不,EM_SETPARAFORMAT 只能对当前选择起作用。并且 EM_EXSETSEL 总是将插入符号放在选择的末尾。您可以通过观察 SelectionStart 中的变化来检测选择方向,但您无法将插入符号放在正确的位置。编辑控件也有同样的问题。

这通常不是问题,因为重新着色仅在用户修改文本时发生,而不是在她选择文本时发生。我能想到的唯一解决方法是通过注入击键来恢复选择。那是丑陋的。

于 2010-09-27T07:23:46.390 回答
0

另一种方法是直接设置 Rtf 属性。您确实需要知道 rtf 语言的语法。

PS。这也会使选择无效。我自己进行了击键注入。

于 2012-05-09T11:45:15.210 回答