带参数的Dispatcher.Invoke()始终抛出InvalidOperationException

时间:2016-12-30 09:59:38

标签: c# wpf dispatcher invalidoperationexception

我想在不同的线程上创建一个Grid元素(创建整个网格是昂贵的)并通过StackPanel更新Dispatcher。但是,无论我做什么,调用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.
    
  5. 真的不可能,或者我错过了什么?感谢您的回答/评论。

1 个答案:

答案 0 :(得分:1)

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

  

真的不可能,或者我错过了什么?

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

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

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

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

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

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

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