如何从另一个线程调用控制方法

How to call a control method from another thread

我想从另一个线程调用 RichTextBox.Find()。我怎样才能做到这一点? RichTextBox 位于我在表单中使用的 UserControl 中。 我想从另一个线程更新它。我能够使用 Invoke 更改其属性。但是无法弄清楚如何从我的线程中调用 _ucResultRich.rchResult.Find(word, startIndex, RichTextBoxFinds.None);

Thread thread=new Thread(thrHighlight);
thread.Start(e.RowIndex);

private void ThrHighlight(object obj)
{
    string[] words = ucSearchControls.rdbExact.Checked
          ? new string[] { ucSearchControls.txtSearch.Text.Trim() }
              : ucSearchControls.txtSearch.Text.Split(' ');
    foreach (string word in words)
    {
        int startIndex = 0;
        while (startIndex < _ucResultRich.rchResult.TextLength)
        {

            int wordStartIndex = _ucResultRich.rchResult.Find(word, startIndex, RichTextBoxFinds.None);
            if (wordStartIndex != -1)
            {
                _ucResultRich.rchResult.SelectionStart = wordStartIndex;
                _ucResultRich.rchResult.SelectionLength = word.Length;
                _ucResultRich.rchResult.SelectionBackColor = Color.Yellow;
            }
            else
            break;
            startIndex += wordStartIndex + word.Length;
        }
    }
}

我该怎么做?

P.S:这是 和那里的@varocarbas 评论

的后续行动

您需要将您的代码与 UI 控件稍微分离,并在外部线程上执行您的业务逻辑并在 Dispatcher.BeginInvoke 或 Invoke 上更新 UI 控件。

例如,您可以将文本框的文本保存在单独的 属性 中,然后在其他线程上执行查找,完成后 post UI 突出显示部分UI 个线程。

这个答案完全专注于展示如何正确使用(即通过最大化其内置功能)BackgroundWorker(这是我在 [=27= 中写的一些评论的延续) ]) 以提供预期的功能。

要使用这些行下面的代码,请启动一个新的 Winforms 项目并将以下控件添加到主窗体:Buttonbutton1 带有单击事件 button1), RichTextBox (richTextBox1) 和 BackgroundWorker (backgroundWorker1DoWork 事件 backgroundWorker1_DoWorkProgressChanged 事件 backgroundWorker1_ProgressChanged);另请注意 Form1_Load 是主窗体的 Load 事件。

要使用该应用程序,只需在 richTextBox1 中输入任何文本,包括一些硬编码词(即 "word1"、"word2"、"word3"、"word4", "word5"), 单击 button1 并确认它们按预期突出显示。

volatile int curWordStartIndex; //I use this global variable to communication between the progressChanged event and findBit, called from the DoWork event

private void Form1_Load(object sender, EventArgs e)
{
    backgroundWorker1.WorkerReportsProgress = true;
}

private void button1_Click(object sender, EventArgs e)
{
    //As far as richTextBox1.TextLength provokes a cross-thread error, I pass it as an argument
    backgroundWorker1.RunWorkerAsync(richTextBox1.TextLength);
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    findBit((int)e.Argument);
}

private void findBit(int textLength)
{
    string[] words = new string[] { "word1", "word2", "word3", "word4", "word5" };
    foreach (string word in words)
    {
        int startIndex = 0;
        while (startIndex < textLength)
        {
            //Rather than performing the actions affecting the GUI thread here, I pass all the variables I need to
            //the ProgressChanged event through ReportProgress and perform the modifications there.
            backgroundWorker1.ReportProgress(0, new object[] { word, startIndex, Color.Yellow });
            if (curWordStartIndex == -1) break;

            startIndex += curWordStartIndex + word.Length;
        }
    }
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    object[] curVars = (object[])e.UserState;

    richTextBox1.SuspendLayout(); 

    string word = (string)curVars[0];
    int startIndex = (int)curVars[1];
    Color curColor = (Color)curVars[2];
    curWordStartIndex = richTextBox1.Find(word, startIndex, RichTextBoxFinds.None);

    if (curWordStartIndex != -1)
    {
        richTextBox1.SelectionStart = curWordStartIndex;
        richTextBox1.SelectionLength = word.Length;
        richTextBox1.SelectionBackColor = curColor;
    }

    richTextBox1.ResumeLayout();
}