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

线程安全的 ToolStripStatusLabel 控件

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.88/5 (7投票s)

2007年7月6日

3分钟阅读

viewsIcon

79092

downloadIcon

1291

本文演示了如何扩展 ToolStripStatusLabel,以便可以从另一个线程更新它。

引言

最近,我遇到了一个常见问题:从处理 ToolStripStatusLabel 控件的线程之外的其他线程设置其 Text 值。当您从独立的线程方法触发事件并想要在客户端应用程序中显示消息时,通常会发生这种情况。处理此问题的常用方法是使用控件的 Invoke 方法来委托操作,使其成为线程安全的。不幸的是,ToolStripStatusLabel 没有这样的方法。我在网上搜索了一段时间,看看是否有人对此问题有解决方案,但找不到任何令人满意的方案。

因此,我自己搜索解决方案,认为 Microsoft 肯定考虑过这一点。解决方案位于包含所有 ToolStrip 控件的 StatusStrip 控件中。此类具有 InvokeRequiredInvoke 方法,这些方法对于在正确的线程上委托调用是必需的。我想出了两种解决方案,这两种方案都有效。

扩展 StatusStrip 控件

第一种解决方案包括向 StatusStrip 控件添加一个方法,以安全地设置其包含的 ToolStripStatusLabel 的 Text 属性。实现此目的的代码如下所示。

public void SafeSetText(ToolStripLabel toolStripLabel, string text)
{
    if (InvokeRequired)
    {
        SetText setTextDel = 
            delegate(ToolStripLabel toolStrip, string textVal)
        {
            foreach (ToolStripItem item in base.Items)
            {
                if (item == toolStrip)
                {
                    item.Text = textVal;
                }
            }
        };

        try
        {
            Invoke(setTextDel, new object[] 
            { 
                toolStripLabel, text 
            });
        }
        catch
        {
        }
    }
    else
    {
        foreach (ToolStripItem item in base.Items)
        {
            if (item == toolStripLabel)
            {
                item.Text = text;
            }
        }
    }
}

这个想法很简单。由于 ToolStripStatusLabel 中没有 Invoke 方法,因此我使用 StatusStrip 的 Invoke 方法并找到必须应用 Text 更改的项目。这种技术的缺点是我无法直接调用 ToolStripStatusLabel 的 Text 属性,因此此属性仍然不安全。但是,此解决方案有效。

扩展 ToolStripStatusLabel 控件

第二种解决方案更令我满意。我重写了 ToolStripStatusLabel 的 Text 属性,并使其完全安全,因此不再可能调用原始的 Text 属性。我认为此解决方案更优雅,可以与 StatusStrip 的其他 ToolStrip 项目一起使用。此解决方案的代码如下所示。

public override string Text
{
    get
    {
        // Make sure that the container is already built
        if ((base.Parent != null) &&        
            (base.Parent.InvokeRequired))   // Is Invoke required?
        {
            GetString getTextDel = delegate()
            {
                return base.Text;
            };
            string text = String.Empty;
            try
            {
                // Invoke the SetText operation from the 
                // Parent of the ToolStripStatusLabel
                text = (string)base.Parent.Invoke(getTextDel, null);
            }
            catch
            {
            }
            return text;
        }
        else
        {
            return base.Text;
        }
    }    
    set
    {
        // Get from the container if Invoke is required
        // Make sure that the container is already built
        if ((base.Parent != null) &&       
            (base.Parent.InvokeRequired))   // Is Invoke required?     
        {
            SetText setTextDel = delegate(string text)
            {        
                base.Text = text;
            };

            try
            {
                // Invoke the SetText operation from the
                // Parent of the ToolStripStatusLabel
                base.Parent.Invoke(setTextDel, new object[] { value });
            }

            catch
            {
            }
        }
        else
        base.Text = value;
    }
}

正如您在代码中看到的那样,此解决方案更直接,不需要单独的方法来设置 Text 属性。诀窍是获取 ToolStrip 项目的 Parent,然后使用委托调用 Invoke 方法来设置 Text 属性。我发现此方法的唯一缺点是您无法从设计器中使用 SafeToolStripStatusLabel,并且必须修改设计器生成的代码。但是,可能有一种方法可以告诉设计器在我们将其添加到 StatusStrip 控件时使用我们的 SafeToolStripStatusLabel。

演示应用程序

为了演示这两种方法,我构建了一个简单的演示应用程序,该应用程序可以触发一个使用 SafeStatusStrip 控件显示的事件,以及一个使用 SafeToolStripLabel 控件显示的事件。此应用程序使用一个线程,该线程等待在按下按钮时发送一个或另一个事件。事件处理程序使用先前描述的两种方法之一在状态栏中显示消息。

Screenshot - Safetoolstrip1.jpg

Screenshot - Safetoolstrip2.jpg

关注点

此代码可用于您可能在 StatusStrip 容器中使用的任何 ToolStrip 控件。您可以通过构建一个包含所有安全控件的组件 DLL,在您的应用程序中自由使用它。

历史

  • 2007 年 7 月 6 日 -- 发布原始版本
© . All rights reserved.