65.9K
CodeProject 正在变化。 阅读更多。
Home

一个独立的线程安全 TextBox 控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (10投票s)

2007年11月8日

CPOL

4分钟阅读

viewsIcon

56202

downloadIcon

860

此控件将消除从主程序确保更新是线程安全的需要。一切都按应有的方式自动处理。

Screenshot - ThreadSafeTextBoxDemoScreenShot.jpg

引言

在许多项目中,我发现我很快就厌倦了在多线程环境中编写委托来保持各种控件的线程安全。我还没有确定是创建单独的实用程序类来处理所有非线程安全的功能,还是通过扩展控件本身使其默认具有线程安全性。在编写此示例时,看到使控件具有线程安全性是多么的简单,我对微软没有从一开始就内置此功能感到困惑。

扩展 TextBox 控件

在规划此项目时,我想,我可以简单地从 TextBox 派生,用线程安全版本重写必要的属性和方法,然后就完成了。首先,让我们看看 Text 属性,我能够为此使用此方法。在派生的 TextBox 中,我们重写 Text 如下:

public override string Text
{
  get
  {
    return base.Text;
  }
  set
  {
    if (InvokeRequired)
    {
      TextDelegate callback = SafeSetText;
      BeginInvoke(callback, new object[] { value });
    }
    else
      base.Text = value;
  }
}

以及我们的带有委托的辅助方法

private delegate void TextDelegate(string text);
private void SafeSetText(string text)
{
  base.Text = text;
}

如果您只需要设置控件的 Text 属性,那么上面的代码就足够了。我在这里使用的方法*几乎*与在主应用程序代码中完成的方式完全相同,每次需要此功能时都会这样做。当 Text 属性被赋值时,我们检查 InvokeRequired 来查看我们是否在 UI 线程上。如果我们在,只需设置文本即可。如果我们不在,我们则调用 BeginInvoke 并将值传递给我们的辅助方法 SafeSetText,然后该方法在 UI 线程上赋值。

更进一步

自以为是,我以为我的工作完成了。错了!
接下来是 AppendText。我已经遇到了 AppendText 的问题。如果您还不知道,AppendText 是在 TextBoxBase 中实现的。本能地,我的第一个尝试是也继承 TextBoxBase,但事实证明,这个类不能被派生。然而,并非所有希望都已破灭。在稍微研究了一下之后,这很容易解决了,如下所示:

public new void AppendText(string AppendingText)
{
  Text += AppendingText;
}

此方法有效地隐藏了内部的 AppendText 方法。鉴于我们已经处理了 Text 属性,如上所示,此方法仅用一行代码实现。

正如 *sdahlbac 所指出的,虽然这样做有效,但使用 .Text += 会带来一些性能问题,尤其是在文本变得非常长时。在研究了 Framework 源代码并尝试了一些方法之后,我设法通过一个过于复杂的方法解决了这个问题,正如另一位读者指出的那样。让我们看看*正确*的做法。

public new void AppendText(string text)
{
    if (!string.IsNullOrEmpty(text))
    {
        if (this.InvokeRequired)
        {
            TextDelegate callback = base.AppendText;
            this.BeginInvoke(callback, new object[] { text });
        }
        else
        {
            base.AppendText(text);
        }
    }
}

就这样。

实现的其他成员有 ForeColorBackColorClear。实现与上面非常相似,因此我没有将其包含在文章中。

进一步扩展

这里使用的方法几乎可以应用于任何控件。唯一需要记住的是重写直接属于您正在扩展的控件的成员,并替换您无法重写的成员。我敢肯定我没有涵盖所有可能需要线程安全的成员,但这些是最常用的。

结论

我还没有机会测试该控件在从非 UI 线程实例化时的情况。我的初步假设是,我在这里的努力在这种情况下将是徒劳的。一旦我能够进行测试,我将更新我的发现。但是,对于在设计时将控件放置在窗体上,或者至少在 UI 线程上创建控件的所有情况,应该没有问题。

我开始考虑的一个想法是,以编程方式确定控件所属的窗体,并在该控件上调用,而不是在 TextBox 控件上调用。这样做可以消除 TextBox 在运行时创建时从 UI 线程外部调用 BeginInvoke 的可能性。到目前为止,我还没有成功实现这一点。一旦我弄清楚或者有人在评论中发布了解决方案,我可能会添加它。

历史

  • 2007 年 11 月 8 日 - 初次提交
  • 2007 年 11 月 9 日 - 解决了 AppendText 的潜在性能问题
  • 2007 年 11 月 16 日 - 替换了过于复杂的 AppendText 方法
© . All rights reserved.