Search code examples
c#wpfmultithreadinginvokedispatcher

"The calling thread cannot access this object because a different thread owns it" error when updating UI control from different thread in WPF


I've got method which I'am calling from main thread. This method is creating a new thread. Code looks like this:

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();
    }

In Display() method i'am trying update Image control. "Display()" method looks like this:

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;
        });
    }

The problem is when I'am trying update Image control using Dispatcher.Invoke I'am receiving error "The calling thread cannot access this object because a different thread owns it". I was trying a lot of different options, and only one works fine:

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;
        });

    }

Could You explain me why second option of "Display()" method works fine, but the first one throwing exception? What I'am doing wrong?


Solution

  • Both the DrawingImage and the DrawingGroup inherit from DispatcherObject, which means that they need to be accessed from the thread on which they were created. That is why your version where all of the work is invoked back to the dispatcher works correctly.

    As pointed out by Brian Reichle, these object also inherit from System.Windows.Freezable, which you can leverage to allow cross thread access to the objects.