Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Updating a label from a background process

Status
Not open for further replies.

JScannell

Programmer
Jan 9, 2001
306
US
I have a module that is doing several things. As each thing is started I want to update a label on the main form with some text.

Doing this in vb.net is absolutely no problem. For some reason, however, C# doesn't work the same way.

Developer forums have said to do a form Update() or an Application.DoEvents() after updating it.

Well I have gone even further than that. I have a public method on my form that looks like this:

public void updateStatusDisplay ( string strMessage )
{
lblPleaseWait.Text = strMessage;

this.Update ();
Application.DoEvents();
}

Then in the module that is performing the processes:

frmRptSel_AcctMgrAvg newReport = new frmRptSel_AcctMgrAvg(myCodeTables);

newReport.updateStatusDisplay ( "Report: Pending Inventory" );

The updateStatusDisplay method is being called (I set a breakpoint in it), but the display doesn't update. What the heck is going on???

Why did Microsoft make the behaviour of C# be so different than vb.net in this? In VB, all I have to do in the module is: frmRptSel_AcctMgrAvg.lblPleaseWait.Text = "new text" and the form is updated and you can see it change. I even do something like this utilizing a progress bar in VB the same way. All I have to do is: frmRptSel_AcctMgrAvg.progressBar1.PerformStep()

Thanks in advance,

Jerry Scannell
 
.NET does not like updating controls from a different thread than the one it was created on.

You need to check the InvokeRequired field of the control you are updating, and if it is true use the control's Invoke method to call a delegate that will then update the control.

I found a nice little gem somewhere online that makes this really easy for me via an extension method.

Code:
public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
    if (control.InvokeRequired) control.Invoke(action);
    else action();
}
Then to call it via your example:
Code:
public void updateStatusDisplay ( string strMessage )
{
    lblPleaseWait.InvokeIfRequired(() =>
    {
        lblPleaseWait.Text = strMessage;
    });
}
Of course this assumes you are using a version of the .NET framework that supports extension methods, and inline delegates.
 
The control that I am attempting to update (lblPleaseWait) doesn't have a property called InvokeRequired, so I don't know what to do with your example.

Also, the code you provided:

public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
if (control.InvokeRequired) control.Invoke(action);
else action();
}


What am I supposed to use for "this Control control" and "MethodInvoker action"?

Thanks in advance,

Jerry Scannell
 
What version of the .NET Framework are you using? Is lblPleaseWait a System.Windows.Forms.Label?

For access to the Control and MethodInvoker types, you need to be using the namespace: System.Windows.Forms.

The "this" is what makes that function an extension of Control - this requires the .NET framework >= 3.5.

The InvokeIfRequired function should be placed in a static class:
Code:
    public static class Extensions
    {
        public static void InvokeIfRequired(this Control control, MethodInvoker action)
        {
            if (control.InvokeRequired) control.Invoke(action);
            else action();
        }
    }

Pretty much all controls placed on a form (Labels, and even the form itself) are all derived from the Control class. As such the InvokeRequired property is available on all controls, along with the Invoke method. If the answer to the lblPleaseWait is yes, then it does indeed have the InvokeRequired property. If it's no... we'll see, it should still derive from Control and therefore inherit the property - unless it was purposefully hidden.

You can also accomplish this in .NET Framework 2 or 3 like this:
Code:
public void updateStatusDisplay (string strMessage)
{
    if (lblPleaseWait.InvokeRequired)
    {
        lblPleaseWait.Invoke(new MethodInvoker(() =>
        {
            updateStatusDisplay(strMessage);
        }));
    }
    else
    {
        lblPleaseWait.Text = strMessage;
    }
}
 
I'm using .net 3.5

It is a standard win form label.

If I load and run a class from within a form, why can't I reference any of the controls on that form from within the class?

Do I have to create a constructor that passes in the main form (e.g. abc def = new abc(this); and then save 'this' as a frmMain type in the module?

Thanks in advance,

Jerry Scannell
 
That would be correct. Scope prevents that from happening.
Code:
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
}
public class MyClass
{
    //this class has to have the form passed to it
    private Form1 theForm;
    public MyClass(Form1 f)
    {
        theForm = f;
    }

    public doAction()
    {
        theForm.lblPleaseWait.Text = "test";
    }
}
That would give your class access to the PUBLIC members of your form, but I believe by default most controls have their modifier set to PRIVATE. You would need to change the Modifiers property of your label to something that would permit that access.
 
At is exactly what I just did and it works perfectly. I thought of it and implemented it as soon as I posted it.

Jerry Scannell
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top