代码之家  ›  专栏  ›  技术社区  ›  Migwell

RichTextBox保存“选择方向”

  •  4
  • Migwell  · 技术社区  · 14 年前

    我有一个 桌面应用程序 当你改变你的选择时,RichTextBox需要改变某些其他文本的颜色。为了做到这一点,它必须选择该文本,因此我失去了当前的选择。

    我可以保存并加载SelectionStart和SelectionLength属性,但不能保留“选择方向”:如果用户从光标向前或向后突出显示。

    关于如何保存选择方向,或者在不改变选择的情况下为文本着色有什么想法吗?

    3 回复  |  直到 14 年前
        1
  •  1
  •   Hans Passant    14 年前

    恶心,丑陋的问题。不,EM_SETPARAFORMAT只能对当前选择有效。EM峈exsetel总是将插入符号放在选择的末尾。您可以通过观察SelectionStart中的变化来检测选择方向,但无法将插入符号放在正确的位置。编辑控件也有相同的问题。

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

        2
  •  1
  •   Waldemar Bienert    9 年前

    我刚刚碰到了同样的问题,现在我用EM_EXSETSEL解决了这个问题。当cpMin>cpMax时,它的工作方式类似于“反向选择”(在选定文本的开头插入符号)。然而,我还没有找到任何其他方法来找出当前的选择方向(EM_EXGETSEL总是返回cpMin<cpMax),但是在选择开始/长度更改之后。。。

    编辑:

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

    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);
    }
    
        3
  •  0
  •   Mr. TA    13 年前

    这也会使选择无效。我自己做了按键注射。