WinForms RichTextBox:如何异步重新格式化,而不触发TextChanged事件

时间:2009-09-21 23:28:49

标签: .net winforms multithreading richtextbox manualresetevent

这是对的后续行动 WinForms RichTextBox: how to perform a formatting on TextChanged?

我有一个带有RichTextBox的Winforms应用程序,该应用程序会自动突出显示所述框的内容。因为大型文档的格式化需要很长时间,10秒或更长时间,我已经设置了BackgroundWorker来重新格式化RichTextBox。 它遍历文本并执行一系列这些:

rtb.Select(start, length);
rtb.SelectionColor = color;

在执行此操作时,UI仍保持响应。

BackgroundWorker从TextChanged事件开始。像这样:

private ManualResetEvent wantFormat = new ManualResetEvent(false);
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
    xpathDoc = null;
    nav = null;
    _lastChangeInText = System.DateTime.Now;
    if (this.richTextBox1.Text.Length == 0) return;
    wantFormat.Set();
}

后台工作者方法如下所示:

private void DoBackgroundColorizing(object sender, DoWorkEventArgs e)
{
    do
    {
        wantFormat.WaitOne();
        wantFormat.Reset();

        while (moreToRead())
        {
            rtb.Invoke(new Action<int,int,Color>(this.SetTextColor,
                      new object[] { start, length, color} ) ;
        }                

    } while (true);
}

private void SetTextColor(int start, int length, System.Drawing.Color color)
{
   rtb.Select(start, length);
   rtb.SelectionColor= color;
}

但是,每个对SelectionColor的赋值都会导致TextChanged事件触发:无限循环。

如何区分源自外部来自执行格式化的BackgroundWorker的文本更改的文本更改?

如果我可以独立于文本格式更改检测到文本内容更改,我也可以解决此问题。

2 个答案:

答案 0 :(得分:6)

答案 1 :(得分:3)

通常当我在事件处理程序中以可能导致再次触发相同事件的方式做出反应时,我设置一个标志,指示我已经在处理事件处理程序,检查事件处理程序顶部的标志,如果设置了标志,则立即返回:

bool processing = false;

TextChanged(EventArgs e)
{
    if (processing) return;

    try
    {
        processing = true;
        // You probably need to lock the control here briefly in case the user makes a change
        // Do your processing
    }
    finally
    {
        processing = false;
    }
}

如果在执行处理时锁定控件是不可接受的,您可以检查控件上的KeyDown事件并在收到时清除处理标志(如果可能还很长,可能还会终止当前的TextChanged处理)。 p>

编辑:

完整的工作代码

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;

namespace BgWorkerDemo
{
    public class FormatRichTextBox : RichTextBox
    {
        private bool processing = false;

        private BackgroundWorker worker = new BackgroundWorker();

        public FormatRichTextBox()
        {
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        }

        delegate void SetTextCallback(string text);
        private void SetText(string text)
        {
            Text = text;
        }

        delegate string GetTextCallback();
        private string GetText()
        {
            return Text;
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                GetTextCallback gtc = new GetTextCallback(GetText);
                string text = (string)this.Invoke(gtc, null);

                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < text.Length; i++)
                {
                    sb.Append(Char.ToUpper(text[i]));
                }

                SetTextCallback stc = new SetTextCallback(SetText);
                this.Invoke(stc, new object[]{ sb.ToString() });
            }
            finally
            {
                processing = false;
            }
        }

        protected override void OnTextChanged(EventArgs e)
        {
            base.OnTextChanged(e);

            if (processing) return;

            if (!worker.IsBusy)
            {
                processing = true;
                worker.RunWorkerAsync();
            }
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (processing)
            {
                BeginInvoke(new MethodInvoker(delegate { this.OnKeyDown(e); }));
                return;
            }

            base.OnKeyDown(e);
        }

    }
}