Dispatcher.Invoke() 带参数总是抛出 InvalidOperationException

Dispatcher.Invoke() with parameter always throws an InvalidOperationException

我想在另一个线程上创建一个 Grid 元素(创建整个网格很昂贵)并通过 [=18] 更新一个 StackPanel =].但是无论我做什么,调用 Dispatcher 总是会抛出一个 InvalidOperationException

这是我的代码:

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

我尝试过的:

  1. Closing over a variable[没用]

    Grid grid = new Grid();
    Grid closedOverGrid = grid;
    Dispatcher.Invoke(new Action(() => stackPanel.Children.Add(closedOverGrid)));
    
  2. Using BeginInvoke[没用]

    //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[没用]

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

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

真的不可能吗,还是我漏掉了什么?感谢您的 answers/comments.

您只能在 最初创建 的线程上访问控件,因此在后台线程上创建控件,然后尝试使用它或在后台线程上修改它UI 线程不是一个选项。这就是您得到 InvalidOperationException 的原因。

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

确实可以在 STA 线程上创建控件:

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

...但是您仍然无法在另一个线程上使用该控件,所以我猜这在您的场景中毫无意义。

所以不,您应该在同一线程上创建所有控件。这是创建父线程 window 的线程,即通常是主线程。

此规则有一些非常有限的例外情况。请参阅以下博客 post 了解更多相关信息:https://blogs.msdn.microsoft.com/dwayneneed/2007/04/26/multithreaded-ui-hostvisual/。如果您的控件不需要某种交互性,您可以使用 post.

中描述的 HostVisual。

您还可以在自己的线程中启动整个顶级 window:http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/

但除此之外,在 WPF 应用程序中创建多个 UI 线程没有任何意义。您只需以某种方式使您的控件渲染得更快,而不是尝试将渲染工作卸载到另一个线程,因为这是行不通的。