Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A thread-safe ToolStripStatusLabel control

0.00/5 (No votes)
6 Jul 2007 5  
This article demonstrates how to extend a ToolStripStatusLabel so that it can be updated from another thread

Introduction

Recently, I had the known problem of setting the Text value of a ToolStripStatusLabel control from a thread other than the one that was handling it. This basically can happen when you fire an event from an independent thread method and want to display a message in the client application. The usual way to deal with this is to use the Invoke method of the control to delegate the operation so that it is thread-safe. Unfortunately, ToolStripStatusLabel doesn't have such a method. I searched awhile on the internet to see if someone had a solution to this problem, but I couldn't find any satisfying one.

So, I searched by myself for a solution thinking that Microsoft had surely thought about this. The solution is in the StatusStrip control that contains all of the ToolStrip controls. This class has the InvokeRequired and Invoke methods that are necessary to delegate the call on the right thread. I came out with two solutions, both of which work.

Extending the StatusStrip control

The first solution consists of adding a method to the StatusStrip control to safely set the Text property of the ToolStripStatusLabel it contains. The code to achieve this is given below.

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;
            }
        }
    }
}

The idea is quite simple. As there is no Invoke method in ToolStripStatusLabel, I use the one of StatusStrip and find on which item I must apply the Text change. The drawback of this technique is that I cannot directly call the Text property of ToolStripStatusLabel and so this property remains unsafe. However, this solution works.

Extending the ToolStripStatusLabel control

The second solution satisfies me better. I override the Text property of ToolStripStatusLabel and make it totally safe so that it is no longer possible to call the original Text property. I think this solution is more elegant and it can be used with other ToolStrip items of a StatusStrip. The code of this solution is given below.

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;
    }
}

As you can see in the code, this solution is more straightforward and doesn't need a separate method to set the Text property. The trick is to get the Parent of the ToolStrip item and call the Invoke method with the delegate to set the Text property. The only drawback I found to this method is that you cannot use SafeToolStripStatusLabel from the designer and must modify the code generated by the designer. However, there may be a way to tell the designer to use our SafeToolStripStatusLabel when we add it to the StatusStrip control.

Demo application

In order to demonstrate both methods, I built a simple demo application that can fire one event displayed with the SafeStatusStrip control and one event displayed with the SafeToolStripLabel control. This application uses a thread that waits to send either one event or the other when a button is pressed. The event handlers display a message in the status bar using one of the two methods described previously.

Screenshot - Safetoolstrip1.jpg

Screenshot - Safetoolstrip2.jpg

Points of interest

This code can be used for any ToolStrip control that you might use in a StatusStrip container. You can use it freely in your application by building a component DLL that will contain all your safe controls.

History

  • 6 July, 2007 -- Original version posted

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here