“调用线程无法访问此对象,因为不同的线程拥有它”从 WPF 中的不同线程更新 UI 控件时出现错误

Posted

技术标签:

【中文标题】“调用线程无法访问此对象,因为不同的线程拥有它”从 WPF 中的不同线程更新 UI 控件时出现错误【英文标题】:"The calling thread cannot access this object because a different thread owns it" error when updating UI control from different thread in WPF 【发布时间】:2017-06-14 09:14:23 【问题描述】:

我有从主线程调用的方法。此方法正在创建一个新线程。代码如下所示:

MouseCursorWallObject MouseCursorWall = null;
List<MovingRectangle> MovingRectangles = null;

DrawingImage RenderedImage;


public MainWindow()
    
        InitializeComponent();
        PrepareObjects();            
        GameLoopRun();                       
    

private void GameLoopRun()
    
        Thread thread = new Thread(() =>
        
            while (true)
            
                DateTime dtStart = DateTime.Now;

                Events();
                Update();
                Display();

                DateTime dtEnd = DateTime.Now;
                TimeSpan ts = dtEnd - dtStart;
                if (SkipTicks - ts.TotalMilliseconds >= 0)
                
                    Thread.Sleep((int)(SkipTicks - ts.TotalMilliseconds));
                
            
        );
        thread.Start();
    

在 Display() 方法中,我正在尝试更新图像控件。 “Display()”方法如下所示:

private void Display()
    
        DrawingGroup imageDrawings = new DrawingGroup();

        // Drawing main canvas
        imageDrawings.Children.Add(DrawingObject(500, 350, 0, 0, new Uri(@"Images\gameCanvas.jpg", UriKind.Relative)));

        // Drawing mouse cursor wall
        imageDrawings.Children.Add(DrawingObject(MouseCursorWall.Width, MouseCursorWall.Height, MouseCursorWall.GetLocX, MouseCursorWall.GetLocY, MouseCursorWall.DisplayTexture));

        for (int i = 0; i < MovingRectangles.Count; i++)
        
            MovingRectangle o = MovingRectangles[i];

            // Drawing moving object
            imageDrawings.Children.Add(DrawingObject(20, 20, o.GetLocX, o.GetLocY, o.TextureUri));
        

        if (GamePause == true)
        

        
        RenderedImage = new DrawingImage(imageDrawings);

        // Image control on main UI thread
        renderImage.Dispatcher.Invoke(() =>
        
            renderImage.Source = RenderedImage;
        );
    

问题是当我尝试使用 Dispatcher.Invoke 更新图像控件时,我收到错误“调用线程无法访问此对象,因为不同的线程拥有它”。我尝试了很多不同的选项,但只有一个可以正常工作:

private void Display()
    
        this.Dispatcher.Invoke(() => 
            DrawingGroup imageDrawings = new DrawingGroup();

            // Drawing main canvas
            imageDrawings.Children.Add(DrawingObject(500, 350, 0, 0, new Uri(@"Images\gameCanvas.jpg", UriKind.Relative)));

            // Drawing mouse cursor wall
            imageDrawings.Children.Add(DrawingObject(MouseCursorWall.Width, MouseCursorWall.Height, MouseCursorWall.GetLocX, MouseCursorWall.GetLocY, MouseCursorWall.DisplayTexture));

            for (int i = 0; i < MovingRectangles.Count; i++)
            
                MovingRectangle o = MovingRectangles[i];

                // Drawing moving object
                imageDrawings.Children.Add(DrawingObject(20, 20, o.GetLocX, o.GetLocY, o.TextureUri));
            

            if (GamePause == true)
            

            

            RenderedImage = new DrawingImage(imageDrawings);
            renderImage.Source = RenderedImage;
        );

    

你能解释一下为什么“Display()”方法的第二个选项可以正常工作,但第一个选项会抛出异常吗?我做错了什么?

【问题讨论】:

我已经找到那个话题了。 好的,如果您首先 .Freeze() DrawingImage,您可能能够让第一个示例工作,但您可能应该在 UI 线程上创建它(根据您的第二个示例)。 【参考方案1】:

DrawingImageDrawingGroup 都继承自DispatcherObject,这意味着它们需要从创建它们的线程中访问。这就是为什么您的所有工作都被调用回调度程序的版本可以正常工作的原因。

正如 Brian Reichle 所指出的,这些对象也继承自 System.Windows.Freezable,您可以利用它来允许跨线程访问对象。

【讨论】:

感谢您的回复!我现在要检查一下。 "... 继承自 DispatcherObject,这意味着它们需要从创建它们的线程中访问。" ...除非它们碰巧被冻结了。

以上是关于“调用线程无法访问此对象,因为不同的线程拥有它”从 WPF 中的不同线程更新 UI 控件时出现错误的主要内容,如果未能解决你的问题,请参考以下文章

调用线程无法访问此对象,因为不同的线程拥有它

调用线程无法访问此对象,因为不同的线程拥有它 - WPF [重复]

Grid.children.clear 线程错误 xmpp wpf

从 NIB 与从代码加载自定义滑块:从代码加载时不存在子视图

如何从其他面板从 JTextField 获取输入

从PRISM开始学WPFMVVMViewModel?