views:

34

answers:

1
+1  Q: 

BeginInvoke error

HI,

This quetion is in continuation to my question at this link.

I wrote an application to compare the approach, used there, 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??

Thanks, Abhi.

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" });
    }
}
+1  A: 

Firstly, there are a few things I'd say about the structure of the code posted that might help.

What the code does is this:

  • In the constructor for Form1, asynchronously invokes a method (on a different thread)
  • That method asynchronously invokes four calls to a separate method to update label controls. The invokes are pushed back to the UI thread (the thread we were in on Form1's constructor)
  • The constructor, meanwhile, waits for the original method to complete before continuing

There are a number of sillinesses in this code:

  • A method is invoked asynchronously (in a non-blocking fashion) and then the code waits for its completion. Unless there's a compelling reason for this code to be on a background thread, why not just call the method synchronously instead? It'll leave the ultimate behavior the same but make debugging and reading the code much easier.
  • Because of the while loop waiting on ar.IsCompleted, you'll find that the UI thread is actually quite busy - hammering the CPU on that empty loop, instead of sitting idle and allowing background threads to run. I suspect that calling the method synchronously would be more performant.
  • Because UpdateCustDetails uses BeginInvoke, it's going to send off the four asynchronous calls and return immediately. That means that - even though the constructor is waiting for ar.IsCompleted - you have no guarantee that the labels will be updated by the time the constructor completes - because the four calls to updateLabel aren't blocking.
  • In actual fact, because Form.BeginInvoke kicks execution back to the form's UI thread, what you're really doing is kicking off a background thread just to kick work back to the thread that created it.

So in short: ignore the error, take out all of the clever looking thread stuff, and just do this:

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

        UpdateCustDetails();
    }

    public void updateLabel(Label lb, string text)
    {
        lb.Text = text;
    }

    public void UpdateCustDetails()
    {
        updateLabel(label1, "Test");
        updateLabel(label2, "Test1234");
        updateLabel(label3, "Test5678");
        updateLabel(label4, "Test0000");
    }
}

You'll get the same result, with better performance, and more readable code.

Dan Puzey
Hi Dan,I would have done the same thing if I had written the same code myself, but I want to make this code work and see what it does and if possible compare it with the normal common-sense approach. Actually this code is part of an application which we have on our system and I am supposed to maintain it in future. :-( And believe you me, the application is filled with "smart" codes like this. Could you please advise me how to make this code work so that I can compare it with various other approaches. At least it will let me know what not to do and why.Thanks,Abhi.
Abhi
I was able to make the code work and I was not surprised to see that the normal approach was the more efficient approach than the BeginInvoke approach.
Abhi