Dispatcher.Invoke() with parameter always throws an InvalidOperationException

1k views Asked by At

I want to create a Grid element on a different thread (creating the whole Grid is expensive) and update a StackPanel via Dispatcher. But calling Dispatcher always throws an InvalidOperationException, no matter what I do.

Here is my code:

Grid grid = new Grid();
Dispatcher.Invoke(() => stackPanel.Children.Add(grid));

What I've tried:

  1. Closing over a variable [didn't work]

    Grid grid = new Grid();
    Grid closedOverGrid = grid;
    Dispatcher.Invoke(new Action(() => stackPanel.Children.Add(closedOverGrid)));
    
  2. Using BeginInvoke [didn't work]

    //declaring this in another thread.
    Grid grid = new Grid();
    AddToPanel(grid);
    
    private void AddToPanel(Grid grid)
    {
        Dispatcher.BeginInvoke((Action)(() =>
        {
            stackPanel.Children.Add(grid);
        }));
    }
    
  3. Using a full declaration with DispatcherPriority [didn't work]

    Grid grid = new Grid();
    
    System.Windows.Application.Current.Dispatcher.BeginInvoke(
        DispatcherPriority.Background,
        new Action(() => stackPanel.Children.Add(grid)));
    
  4. Tried the .Freeze() method [didn't work]

    Grid grid = new Grid();
    grid.Freeze(); // Not possible.
    

Is it really not possible, or am I missing something? Thank you for your answers/comments.

1

There are 1 answers

0
mm8 On BEST ANSWER

You can only access a control on the thread on which it was originally created so creating the control on a background thread and then try to use it or modify it back on the UI thread is not an option. That's why you get an InvalidOperationException.

Is it really not possible, or am I missing something?

It is indeed possible to create a control on a STA thread:

The calling thread must be STA, because many UI components require this

...but you still won't be able to use the control on another thread so this would be kind of pointless in your scenario I guess.

So no, you should create all your controls on the same thread. That is the thread on which the parent window was created, i.e. typically the main thread.

There are some very limited exceptions to this rule. Please refer to the following blog post for more information about this: https://blogs.msdn.microsoft.com/dwayneneed/2007/04/26/multithreaded-ui-hostvisual/. If your control doesn't require some kind of interactivity you could for example use a HostVisual as described in this post.

You could also launch an entire top-level window in its own thread: http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/.

But except from this it doesn't make any sense to create more than one UI thread in a WPF application. You will simply have to make your controls render faster somehow instead of trying to offload the rendering work to another thread because this won't work.