views:

55

answers:

3

Hi,

I came across a code which was using BeginInvoke to update label text in a certain way, which I found strange and I can't understand why would somebody do that.

If I want to use threading to allow good user experience, I would create a thread and update the labels in the new thread using Thread.Start nethod. Why would I use the below approach?

If you guys can tell me what is the benefit, if any, of using this particular approach, that would be great.

    public delegate void loadCustomersDelegate();
    private delegate void updateLabelDelegate(Label lb, string text);
    public void updateLabel(Label lb, string text)
    {
        lb.Text = text;
    }
    public void loadCustomerDetails()
    {
        loadCustomersDelegate del = new loadCustomersDelegate(loadCustomerDetailsAction);
        IAsyncResult res = del.BeginInvoke(null, null);

        while (!res.IsCompleted)
        {
        }

    }
    public void loadCustomerDetailsAction()
    {
            BeginInvoke(new updateLabelDelegate(updateLabel), new object[] { lblCustomerName, resp.AddressItems[0].ADName.ToString() });
            BeginInvoke(new updateLabelDelegate(updateLabel), new object[] { lblAddress1, resp.AddressItems[0].AD1.ToString() });
            BeginInvoke(new updateLabelDelegate(updateLabel), new object[] { lblAddress2, resp.AddressItems[0].AD2.ToString() });
            BeginInvoke(new updateLabelDelegate(updateLabel), new object[] { lblAddress3, resp.AddressItems[0].AD3.ToString() });
            BeginInvoke(new updateLabelDelegate(updateLabel), new object[] { lblAddress4, resp.AddressItems[0].ADCode.ToString() });
            BeginInvoke(new updateLabelDelegate(updateLabel), new object[] { lblPCode, resp.AddressItems[0].PCode.ToString() });
            BeginInvoke(new updateLabelDelegate(updateLabel), new object[] { lblTelephone, resp.AddressItems[0].Tele.ToString() });
            BeginInvoke(new updateLabelDelegate(updateLabel), new object[] { lblFax, resp.AddressItems[0].Fax.ToString() });
            BeginInvoke(new updateLabelDelegate(updateLabel), new object[] { lblSLCode, resp.AddressItems[0].SLCode.ToString() });
            BeginInvoke(new updateLabelDelegate(updateLabel), new object[] { lblSLBalance, "£" + resp.AddressItems[0].SLBalance.ToString() });
            BeginInvoke(new updateLabelDelegate(updateLabel), new object[] { lblSLMonthsOld, resp.AddressItems[0].SLMonthsOld.ToString() });
            BeginInvoke(new updateLabelDelegate(updateLabel), new object[] { lblSLCreditRating, "£" + resp.AddressItems[0].SLCreditRating.ToString() });
    }

Update

I wrote an application to compare this approach with other ways. While running the application in debug mode I got the error "Invoke or BeginInvoke cannot be called on a control until the window handle has been created." on the first BeginInvoke in method UpdateCustDetails. Although, it doesn't give any runtime error while running the code without debug. Any ideas??

Below is my code:-

public delegate void UpdateLabelDelegate(Label lb, string text);
public delegate void loadCustomersDelegate();

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

        loadCustomersDelegate del = new loadCustomersDelegate(UpdateCustDetails);
        IAsyncResult ar = del.BeginInvoke(null, null);

        while (!ar.IsCompleted)
        {
        }

    }

    public void updateLabel(Label lb, string text)
    {

        lb.Text = text;

    }

    public void UpdateCustDetails()
    {
        BeginInvoke(new UpdateLabelDelegate(updateLabel), new object[] { label1, "Test" });
        BeginInvoke(new UpdateLabelDelegate(updateLabel), new object[] { label2, "Test1234" });
        BeginInvoke(new UpdateLabelDelegate(updateLabel), new object[] { label3, "Test5678" });
        BeginInvoke(new UpdateLabelDelegate(updateLabel), new object[] { label4, "Test0000" });
    }
}

Thanks, Abhi.

+1  A: 

You must update UI controls in the thread that owns them, otherwise you'll get a runtime exception, which is why that code uses BeginInvoke. There's really no benefit, it's just how you have to do it to make .NET happy :)

Adam
+1  A: 

You can't access controls in thread that they were not created at. Read carefully description here:

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.begininvoke.aspx

Executes a delegate asynchronously on the thread that the control's underlying handle was created on.

It does the magic: executes delegate on needed thread. More info here: http://stackoverflow.com/questions/142003/cross-thread-operation-not-valid-control-accessed-from-a-thread-other-than-the-t

Andrey
+3  A: 

There is an enormous amount of code that runs when you assign a Label's Text property. Both in the .NET framework and in Windows itself. Very nice of Windows Forms to not force you to write that kind of code, or even see it. But it is there and the golden rule for enormous amounts of code is that it is enormously not thread-safe. There is no GUI framework in the world that is. Cold hard rule is that you must only ever touch the properties of a control on the thread that created it. Which is what Control.Begin/Invoke() helps you do.

What you ended up with is however quite fugly. There's no benefit to BeginInvoke for each individual label. You only need one, a single method that sets all labels. It will be not only cleaner, but also a lot less expensive. Your delegate declaration should look like

 private delegate void updateLabelsDelegate(Mumble resp);

where Mumble is the class of the AddressItems[0] element. Using the BackgroundWorker class usually makes you write code like that automatically. Recommended.

Hans Passant
Well said -------
Adam
Thanks Adam for a nice explaination. So as I understood there was no need to update labels inside individual BeginInvokes. Only the outer BeginInvoke was enough.
Abhi
You've got it. Please don't call me Adam.
Hans Passant
Sorry Hans.... My bad!!! The earlier comment was from Adam, so got all mingled... My apologies :-)
Abhi
Not a problem, glad he didn't say "surely".
Hans Passant