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

问题描述 投票:0回答:1

我想在不同的线程上创建一个

Grid
元素(创建整个网格很昂贵)并通过
StackPanel
更新
Dispatcher
。但无论我做什么,调用
Dispatcher
总是会抛出
InvalidOperationException

这是我的代码:

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

我尝试过的:

  1. 关闭变量[不起作用]

    Grid grid = new Grid();
    Grid closedOverGrid = grid;
    Dispatcher.Invoke(new Action(() => stackPanel.Children.Add(closedOverGrid)));
    
  2. 使用 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. 使用 DispatcherPriority 的完整声明 [不起作用]

    Grid grid = new Grid();
    
    System.Windows.Application.Current.Dispatcher.BeginInvoke(
        DispatcherPriority.Background,
        new Action(() => stackPanel.Children.Add(grid)));
    
  4. 尝试了.Freeze()方法[没用]

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

这真的不可能吗,还是我错过了什么?感谢您的回答/评论。

c# wpf dispatcher invalidoperationexception
1个回答
1
投票

您只能在最初创建的线程上访问控件,因此在后台线程上创建控件,然后尝试在 UI 线程上使用它或修改它并不是一种选择。这就是为什么你会收到 InvalidOperationException。

这真的不可能吗,还是我错过了什么?

确实可以在

STA 线程上创建控件:

调用线程必须是STA,因为很多UI组件都需要这个

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

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

此规则有一些非常有限的例外情况。有关详细信息,请参阅以下博客文章:

https://blogs.msdn.microsoft.com/dwayneneed/2007/04/26/multithreaded-ui-hostvisual/。如果您的控件不需要某种交互性,您可以使用本文中描述的 HostVisual。

您还可以在自己的线程中启动整个顶级窗口:

https://web.archive.org/web/20230128060513/http://reedcopsey.com/2011/11/28/launching-a-wpf -单独线程中的窗口部分-1/

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

© www.soinside.com 2019 - 2024. All rights reserved.