-1

我创建了一个 RichTextBox,它为输入文本中的某些语法着色。在输入文本框、粘贴大量文本或滚动大量文本时,我没有问题。

当我在有人粘贴到文本框后调用函数 ProcessAllLines 时出现了我的问题,该函数会遍历并处理每一行并按照我的定义为正确的字母着色。有没有一种方法可以在后台使用线程执行此操作,并且能够滚动浏览到目前为止已着色的文本?

还有其他方法可以加快我粘贴文本的着色速度吗?

代码:

protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        if (m.Msg == 0x0f)
        {
            if (m_bPaint)
                base.WndProc(ref m);
            else
                m.Result = IntPtr.Zero;
        }
        else
            base.WndProc(ref m);
    }

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 
    {
        if((keyData == (Keys.Control | Keys.V)))
        {
            Console.WriteLine("Pasted!");
            ProcessAllLines();
            return base.ProcessCmdKey(ref msg, keyData);
        } 
        else 
        {
            return base.ProcessCmdKey(ref msg, keyData);
        }
    }

    protected override void OnTextChanged(EventArgs e)
    {
        if (m_bTxtChgOk)
        {
            // Calculate stuff here.
            m_nContentLength = this.TextLength;

            int nCurrentSelectionStart = SelectionStart;
            int nCurrentSelectionLength = SelectionLength;

            m_bPaint = false;

            // Find the start of the current line.
            m_nLineStart = nCurrentSelectionStart;
            while ((m_nLineStart > 0) && (Text[m_nLineStart - 1] != '\n'))
                m_nLineStart--;

            // Find the end of the current line.
            m_nLineEnd = nCurrentSelectionStart;
            while ((m_nLineEnd < Text.Length) && (Text[m_nLineEnd] != '\n'))
                m_nLineEnd++;

            // Calculate the length of the line.
            m_nLineLength = m_nLineEnd - m_nLineStart;

            // Get the current line.
            m_strLine = Text.Substring(m_nLineStart, m_nLineLength);

            // Process this line.
            ProcessLine();

            m_bPaint = true;
        }
    }

    public void ProcessAllLines()
    {
        m_bPaint = false;
        m_bTxtChgOk = false;

        int nStartPos = 0;
        int i = 0;
        int nOriginalPos = SelectionStart;
        while (i < Lines.Length)
        {
            m_strLine = Lines[i];
            m_nLineStart = nStartPos;
            m_nLineEnd = m_nLineStart + m_strLine.Length;
            m_nLineLength = m_nLineEnd - m_nLineStart;

            ProcessLine();
            i++;

            nStartPos += m_strLine.Length + 1;
        }
        SelectionStart = nOriginalPos;

        m_bPaint = true;
        m_bTxtChgOk = true;
    }


    private void ProcessLine()
    {
        // Save the position
        int nPosition = SelectionStart;
        // Next three code lines turn the whole current line to black text to begin with
        SelectionStart = m_nLineStart;
        SelectionLength = m_nLineLength;
        SelectionColor = Color.Black;

        // Get us a copy of the current line to play around with
        String lcpy_strLine = new String(m_strLine.ToCharArray());
        // Make it uppercase so we dont have to look for "upper" and "LOWER" cases
        lcpy_strLine = lcpy_strLine.ToUpper();

        // Make each letter green first, then we can change its color later.
        int x = 0;
        while (x < m_nLineLength)
        {
            if (Char.IsLetter(lcpy_strLine[x]) || lcpy_strLine[x].Equals('\\'))
            {
                SelectionStart = m_nLineStart + x;
                SelectionLength = 1;
                SelectionColor = Color.Green;
            }
            x++;
        }

        // These color from the specified char until a non-num is encountered
        lcpy_strLine = ColorTilNoNumFromChar(":", "~", Color.DarkBlue, lcpy_strLine);
        lcpy_strLine = ColorTilNoNumFromChar("M", "m", Color.Red, lcpy_strLine);
        lcpy_strLine = ColorTilNoNumFromChar("N", "n", Color.Maroon, lcpy_strLine);
        lcpy_strLine = ColorTilNoNumFromChar("G", "g", Color.Blue, lcpy_strLine);

        //These color this char is not followed by a letter && !(a num || a symbol)
        lcpy_strLine = ColorCharIfNotFollowedByLetter("X", Color.Green, lcpy_strLine);
        lcpy_strLine = ColorCharIfNotFollowedByLetter("Y", Color.Green, lcpy_strLine);
        lcpy_strLine = ColorCharIfNotFollowedByLetter("Z", Color.Green, lcpy_strLine);

        // Make sure #11.11=11.11 is blue where 1 is a number and . are optional
        while (m_nLineLength >= 3 &&
            lcpy_strLine.Contains("#") && lcpy_strLine.Contains("=") &&
            lcpy_strLine.IndexOf('#') < lcpy_strLine.IndexOf('='))
        {
            int j = 0;
            int indx1 = lcpy_strLine.IndexOf("#");
            int indx2 = lcpy_strLine.IndexOf('=');
            for (j = indx1 + 1; j < m_nLineLength; j++)
                if (!Char.IsDigit(lcpy_strLine[j]) && !lcpy_strLine[j].Equals('.'))
                    break;
            if (lcpy_strLine[j].Equals('=') && !lcpy_strLine[j - 1].Equals('#'))
            {
                for (j = j + 1; j < m_nLineLength; j++)
                    if (!Char.IsDigit(lcpy_strLine[j]) && !lcpy_strLine[j].Equals('.'))
                        break;
                SelectionStart = m_nLineStart + indx1;
                SelectionLength = j - indx1;
                SelectionColor = Color.Blue;
            }
            lcpy_strLine = CopyOverAtIndex("~", indx1, lcpy_strLine);
            lcpy_strLine = CopyOverAtIndex("~", indx2, lcpy_strLine);
        } // endwhile

        if (lcpy_strLine.Contains("P"))
        {
            SelectionStart = m_nLineStart + lcpy_strLine.IndexOf('P') + 1;
            SelectionLength = m_nLineLength - lcpy_strLine.IndexOf('P');
            SelectionColor = Color.Black;
        }

        // These two are for [XXXXX] and Comments
        lcpy_strLine = ColorInsideTwoChars("[", "]", Color.Black, lcpy_strLine);
        lcpy_strLine = ColorInsideTwoChars("(", ")", Color.Purple, lcpy_strLine);

        //This is for single quote comments
        if (lcpy_strLine.Contains("'"))
        {
            SelectionStart = m_nLineStart + lcpy_strLine.IndexOf('\'');
            SelectionLength = m_nLineLength - lcpy_strLine.IndexOf('\'');
            SelectionColor = Color.Purple;
        }

        // Set the postion to the saved position
        SelectionStart = nPosition;
        SelectionLength = 0;
        SelectionColor = Color.Black;
    }

    private String ColorInsideTwoChars(String car1, String car2, Color clr, String lineRef)
    {
        while ((lineRef.Contains(car1) && lineRef.Contains(car2)) &&
            lineRef.IndexOf(car1) < lineRef.IndexOf(car2))
        {
            int indx1 = lineRef.IndexOf(car1);
            int indx2 = lineRef.IndexOf(car2);
            SelectionStart = m_nLineStart + indx1;
            SelectionLength = (indx2 - indx1) + 1;
            SelectionColor = clr;
            lineRef = CopyOverAtIndex("~", indx1, lineRef);
            lineRef = CopyOverAtIndex("~", indx2, lineRef);
        }
        return lineRef;
    }
    private String ColorTilNoNumFromChar(String car, String rplcar, Color clr, String lineRef)
    {
        while (lineRef.Contains(car))
        {
            int j = 0;
            int indx1 = lineRef.IndexOf(car);
            for (j = indx1 + 1; j < m_nLineLength; j++)
            {
                if (!Char.IsDigit(lineRef[j]))
                    break;
            }
            SelectionStart = m_nLineStart + indx1;
            SelectionLength = j - indx1;
            SelectionColor = clr;
            lineRef = CopyOverAtIndex(rplcar, indx1, lineRef);
        }
        return lineRef;
    }
    private String ColorCharIfNotFollowedByLetter(String car, Color clr, String lineRef)
    {
        while (lineRef.Contains(car) &&
            (lineRef.IndexOf(car) + 1 < m_nLineLength))
        {
            int indx1 = lineRef.IndexOf(car);
            SelectionStart = m_nLineStart + indx1;
            SelectionLength = 1;
            if (!Char.IsLetter(lineRef[lineRef.IndexOf(car) + 1]))
                SelectionColor = clr;
            else
                SelectionColor = Color.Black;
            lineRef = CopyOverAtIndex("~", indx1, lineRef);
        }
        return lineRef;
    }
    private String CopyOverAtIndex(String car, int index, String refStr)
    {
        return refStr.Remove(index, 1).Insert(index, car);
    }
4

1 回答 1

1

试试这个:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        if ((keyData == (Keys.Control | Keys.V)))
        {
            Debug.WriteLine("Pasted!");
            this.SuspendLayout();
            ProcessAllLines();
            this.ResumeLayout();
            return base.ProcessCmdKey(ref msg, keyData);
        }
        else
        {
            return base.ProcessCmdKey(ref msg, keyData);
        }
    }

我还建议通过更改此行来重构您在 ProcessLine 中的代码

String lcpy_strLine = new String(m_strLine.ToCharArray());

 char[] lcpy_strLine = m_strLine.ToUpper().ToCharArray();

并从那里开始。您几乎在每次调用令牌时都会获得行字符串的新副本,从长远来看,这将是一个内存负担。

于 2012-08-14T20:43:43.517 回答