3

The exception is this:

System.InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.

First I'll explain the relations in my app. There's a form named MainForm and another form named AssetsForm. The MainForm is creating an instance of the AssetsForm in the MainForm's constrcutor but doesn't AssetsForm.Show() it yet.

There is class named AssetsSource which implements the IObservable and sends data for display to the AssetsForm which implements the IObserver. When AssetsForm receives data to display, it creates a BackgroundWorker which handles the data and update a TreeView.

I've implemented the following wrong code to handle the UI updates from the BackgroundWorker:

    private void Invoke(Control control, Action action)
    {
        if (control.InvokeRequired)
        {
            control.BeginInvoke(action);
        }
        else
        {
            control.Invoke(action);
        }
    }

It's wrong because instead of Invoke(action) I should have written action(); But I will refer to this later. Anyway, an InvalidOperationException was thrown from the Invoke(action) line of code. I can infer that the InvokeRequired evaluated to FALSE, although I update the TreeView from a BackgroundWorker !!

In MSDN it is written about Control.Invoke:

The Invoke method searches up the control's parent chain until it finds a control or form that has a window handle if the current control's underlying window handle does not exist yet. If no appropriate handle can be found, the Invoke method will throw an exception.

What is the parent chain and what is the window handle ? When the window handle is created ? I guess all this had to do with the fact that the AssetsForm is closed.

When I removed that line and uses only action(); as it should be, the program doesn't crash.

When the AssetsForm is opened before the AssetsSource sends updates to AssetsForm, by debugging I can see that InvokeRequired is evaluated to TRUE and BeginInvoke of the TreeView updates itself.

To sum everything up, I don't understand why when the AssetsForm is closed, then the InvokeRequired is false and the UI update (TreeView) is allowed to be from the thread that didn't create the TreeView.

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364

1 Answers1

0

As long as the window is not shown, Winforms doesn't need to stick on the UI-thread mechanismn. Therefore InvokeRequired returns false.

If you call Show() the window is opened and all UI activities need to be run through the event loop and therefore through the UI-thread.

Background: The restriction to handle UI activities only through the main thread is due to the fact that only one (windows) event loop is handling all UI related activities. To ensure that all activities are running in the correct order, all actions need to be run through one thread (at least in winforms). As long as the form is not shown, no events are triggered and therefore there's no need to enforce that all actions are run through the main thread.

EDIT: Add some background description.

Stephen Reindl
  • 5,659
  • 2
  • 34
  • 38
  • Are you saying that when the window isnt shown, modifying the TreeView is done through a thread which isn't the UI thread ? In the following discussion [link](http://stackoverflow.com/questions/3794420/why-is-only-the-ui-thread-allowed-to-modify-the-ui) it said that a control is only allowed to be accessed from the UI thread, where it was created. Can you be clearer in your answer ? – Moti Berger Jan 23 '16 at 19:02
  • One more thing: from what I know, when accessing a control outside the thread which created it, a message is sent to the message queue and the message pump of the UI thread processes that message. So from this, I must deduce that the action() was handled on the UI thread. – Moti Berger Jan 23 '16 at 19:02
  • added some background description. – Stephen Reindl Jan 23 '16 at 19:31
  • If events aren't triggered when the form is not shown, then how action() is modifying the TreeView ? The BackgroundWorker is accessing the TreeView and changing it ? What if there are more than one BackgroundWorker that is modifying that specific TreeView ? – Moti Berger Jan 23 '16 at 20:07
  • If the form is not displayed, the TreeView is neither. Therefore all activities are done internally. If you want to modify data through several threads, you need to ensure by yourself that no conflicting access is performed on the TreeView (by using `lock` or other synchronization mechanismns. – Stephen Reindl Jan 23 '16 at 20:14