How do I make cross-threaded calls to a ToolStripStatusLabel?
Asked Answered
M

5

17

I tend to use a StatusStrip at the bottom of most of my applications for simple status updates and occasionally a progress bar.

However, it appears ToolStripStatusLabels do not inherit from control, so they have no .Invoke, or .InvokeRequired. So how would I thread-safe make a call to change it's text property?

Coded answers for posterity and others that come searching:

Action<string> test=(text) =>
            {
                if (this._statusStrip.InvokeRequired) this._statusStrip.Invoke(
               new MethodInvoker(() => this._lblStatus.Text = text));
                else this._lblStatus.Text = text;
            };

or

private void TestInvoker(string text)
    {
        if (this._statusStrip.InvokeRequired) 
            this._statusStrip.Invoke(
                   new MethodInvoker(() => this._lblStatus.Text = text));
        else this._lblStatus.Text = text;
    }
Meijer answered 14/7, 2009 at 20:53 Comment(2)
@Maslow: Be careful in your logic around Control.Invoke. The rules laid out in the docs are subtle and prone to error if they aren't read carefully. Please check the code that I have in the link in my answer for a more properly-guarded use of Invoke. E.g., your implementation isn't verifying that the ToolStrip isn't disposed and it isn't verifying that the ToolStrip's handle has been created.Blaisdell
Besides the handle not being created yet, It looks like those cases are going to do nothing besides throw exceptions if they occur anyhow, right? And if the handle is not created yet, I'm not sure it's useful to pretend nothing happened and not throw an exception.Meijer
B
29

This is a good question!

While ToolStripStatusLabel does not inherit from control, the containing ToolStrip does! Use the containing ToolStrip's Invoke to make calls against the ToolStripStatusLabel.

This is because the ToolStrip manually handles the drawing of its component bits, much the same way that WPF manages the drawing of all of its component bits, without generating a separate handle for each one. This is useful because it's easy to forget that every Control has an associated HANDLE and the system only has a finite number of those to dish out, e.g..

(I've run into this before also. I mentioned it in a sidebar on another question, for example. I should update that text to reflect my more recent understanding.)

Blaisdell answered 14/7, 2009 at 20:54 Comment(2)
Yeah, and you could even use the Forms Invoke method as that is created on the same thread. But I would go with Greg D's solution as it's more logical.Faena
@sindre, not necessarily, but probably... whereas the ToolStripStatusLabels are necessarilly created on the same thread as the StatusStripBeecher
B
2

Also, in general, you do not Have to use the InvokeRequired and BeginInvoke on the exact same control that you are manipulating in code, as long as you can guarantee that the control you are manipulating was created on the same thread (e.g., in the forms' initialization routine), as the UI element you are calling InvokeRequired /BeginInvoke on.

Beecher answered 14/7, 2009 at 20:58 Comment(0)
H
1

You can do this by using the delegate key word and the Control.Invoke() method. This example shows how you manage a thread safe .Text and .ForeColor ajustment.

private delegate void SetToolStripDelegate(string text, Color color);

private void SetToolStrip(string text, Color color)
{
    statusBar.Text = text;
    statusBar.ForeColor = color;
}

Inside the thread context you can make a thread-safe call of this method like this:

{ // thread begin...

    // somewhere inside the thread
    Invoke(new SetToolStripDelegate(SetToolStrip), "Connected.", Color.Green);

} // thread end...
Houlihan answered 20/6, 2016 at 9:12 Comment(0)
S
0

In simple words, while passing the StatusStrip Items, pass the StatusStrip too along in parameter and use as StatusStrip.BeginInvoke or Invoke method and place the Status strip items inside it.

Below code should help you how to call and update the StatusStrip not only from other Task/Thread, but also from other Class.

//Coded by Chandraprakash [2017-07-18]
//frozenprakash.com

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace UIUpdateFromOtherClass
{

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        FN_Execute();
    }

    async void FN_Execute()
    {
        Second s = new Second();
        await Task.Run(() => s.Execute(lbl1,
                                        pb1,
                                        ss1,
                                        ss1Lbl1,
                                        ss1Pb1)
                        );
        MessageBox.Show("End");
    }

}

public class Second
{
    public void Execute(Label lbl1,
                        ProgressBar pb1,

                        StatusStrip ss1,
                        ToolStripLabel tsLbl1,
                        ToolStripProgressBar tsPb1)
    {
        lbl1.BeginInvoke(new Action(() =>
            lbl1.Text = "Second"
        ));

        pb1.BeginInvoke(new Action(() =>
        {
            pb1.Style = ProgressBarStyle.Marquee;
            pb1.MarqueeAnimationSpeed = 10;
        }));

        ss1.BeginInvoke(new Action(() =>
        {
            tsLbl1.Text = "Second";

            tsPb1.Style = ProgressBarStyle.Marquee;
            tsPb1.MarqueeAnimationSpeed = 10;
        }));

        Thread.Sleep(3000);
    }
}

}

Windows Forms Screenshot

Stomatology answered 18/7, 2017 at 16:27 Comment(0)
Z
0

Have you tried GetCurrentParent()? This worked for me!

private delegate void SetToolStripStatusLabelTextDelegate(ToolStripStatusLabel label, string text);
public static void SetToolStripStatusLabelText(ToolStripStatusLabel label, string text)
{
    if (label.GetCurrentParent().InvokeRequired)
    {
        label.GetCurrentParent().Invoke(new SetToolStripStatusLabelTextDelegate(SetToolStripStatusLabelText), label, text);
    }
    else
    {
        label.Text = text;
        label.Invalidate();
    }
}
Zondra answered 25/3, 2021 at 15:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.