开发者

RichTextBox Save "Selection Direction"

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?


I just hit the same problem and by now I solved this by using EM_EXSETSEL. When cpMin > cpMax, it works like "backward selection" (caret at the beginning of selected text). Yet I haven't found any other way to find out current selection direction (EM_EXGETSEL always returned cpMin < cpMax) but following SelectionStart/Length changes...

EDIT:

That's what i'm using to solve this. There might be some easier way but at least the following works for me.

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


Yuck, ugly problem. No, EM_SETPARAFORMAT can only work on the current selection. And EM_EXSETSEL always puts the caret at the end of the selection. You could detect the selection direction by observing the change in SelectionStart but you can't get the caret in the right spot. An edit control has the same problem.

It isn't normally a problem because re-coloring happens only when the user modifies text, not when she's selecting text. The only workaround I can think of is restoring the selection by injecting keystrokes. That's fugly.


Another way could be setting the Rtf property directly. You do need to know the syntax of the rtf language.

PS. That will invalidate the selection, too. I did the keystroke injection myself.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜