How to start WPF in separate thread and still updating it continuously

Magnus Vinterhav 61 Reputation points
2021-01-08T02:28:34.823+00:00

Hi.

C#, .Net 3.1, WPF

I want to have a WPF window that takes data from a BlockingCollection<ColorAddress>(ConcurrentQueue<ColorAddress>) continuously.

I have managed to start the window in it's own thread, but while doing this with the following code.
The thread is created as this method "RunTheGraphics" from the class "GraphicalThreadHandler"

public void RunTheGraphics()
{
    VisualWorld vw = new VisualWorld(ref ags, 3840, 2160, ref logger, ref graphQueue);
    vw.Show();
    System.Windows.Threading.Dispatcher.Run();

    while (threadIsRunning == true)
    {
        vw.RunGraphics();
        vw.Show();
    }
}

And this is the code starting it:

graphicalQueue = new BlockingCollection<ColorAddress>(new ConcurrentQueue<ColorAddress>());
graphHandler = new GraphicalThreadHandler(ref ags, ref logger, ref graphicalQueue);

graphThread = new Thread(graphHandler.RunTheGraphics);
graphThread.SetApartmentState(ApartmentState.STA);
graphThread.IsBackground = true;
graphThread.Start();

I don't know how to make the method vw.RunGraphics execute. This method is where I am looping through the queue to fetch the data contained in the object "ColorAddress".
In the above code, the code never get to anything after Dispatcher.Run(); is called. To put it in another way, how do I get the method RunGraphics to run when the Dispatcher takes over?

If there would be a way to start vw.RunGraphics with Dispatcher.Run() or something similar, it would work, but I don't know how.

So I'm stuck between not updating the graphics, which renders the window itself unusable, or to not start Dispatcher.Run(), which also makes the window unusable.
I tried calling RunGraphics from the constructor in the VisualWorld class, but then the Dispatcher also do not seem to start, and now window is ever visible.
Please advice on how to solve this.

Here is the code for the VisualWorld window:

using System;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Collections.Generic;
using System.Collections.Concurrent;
using StructureLibrary;
using System.ComponentModel;

namespace EvolutionSimulator
{
    public partial class VisualWorld : Window
    {

        private WriteableBitmap imgBmp;
        private static Random rand = new Random();
        private AreaGroupStructure ags;

        int objectPixelSize;
        int squareSize;
        int areaSize;

        public BlockingCollection<ColorAddress> graphHandler;
        public BlockingCollection<string> logger;
        string loggerPrefix;

        public VisualWorld(ref AreaGroupStructure ags, int x, int y, ref BlockingCollection<string> logger, ref BlockingCollection<ColorAddress> graphHandler)
        {
            InitializeComponent();
            this.logger = logger;
            loggerPrefix = TypeDescriptor.GetClassName(this);

            logger.Add(loggerPrefix + " VisualWorld is now being set up.");
            this.ags = ags;

            List<System.Windows.Media.Color> listOfColors = new List<System.Windows.Media.Color>();
            for (int i = 0; i < 256; i++)
            {
                listOfColors.Add(System.Windows.Media.Color.FromArgb((byte)0, (byte)i, (byte)i, (byte)i));
            }
            // BitmapPalette palette = new BitmapPalette(listOfColors);

            imgBmp = BitmapFactory.New(x, y);
            daCanvas.Source = imgBmp;

            objectPixelSize = ags.GetObjectPixelSize();
            squareSize = ags.GetSquareSize();
            areaSize = ags.GetAreaSize();
            this.graphHandler = graphHandler;

            logger.Add(loggerPrefix + " GraphHandler has been set up in VisualWorld.");
            // RunGraphics();
        }

        public ref BlockingCollection<ColorAddress> GetQueue()
        {
            return ref graphHandler;
        }

        public void RunGraphics()
        {
            foreach (ColorAddress colorAddress in graphHandler.GetConsumingEnumerable())
            {
                logger.Add(loggerPrefix + " Now trying to dequeue the graphQueue");
                ColorAddressPainter(colorAddress);
            }
        }

        public void ColorAddressPainter(ColorAddress inCA)
        {
            logger.Add(loggerPrefix + " Now starting ColorAddressPainter. But will there be blood?");
            ColorStruct[,] allColors = inCA.GetColorStruct();
            int[] address = inCA.GetAddress();
            int areaAddressX = address[0];
            int areaAddressY = address[1];
            int locationInAreaX = address[2];
            int locationInAreaY = address[3];
            int addressX = objectPixelSize * squareSize * areaSize * areaAddressX + objectPixelSize * squareSize * locationInAreaX;
            int addressY = objectPixelSize * squareSize * areaSize * areaAddressY + objectPixelSize * squareSize * locationInAreaY;

            for(int x = 0; x < squareSize; x++)
            {
                for(int y = 0; y < squareSize; y++)
                {
                    using (imgBmp.GetBitmapContext())
                    {
                        //imgBmp.Lock();
                        byte[] temp = allColors[x, y].GetColor();
                        byte a = temp[0];
                        byte r = temp[1];
                        byte g = temp[2];
                        byte b = temp[3];
                        imgBmp.FillRectangle(addressX + x * objectPixelSize, addressY + y * objectPixelSize, addressX + x * objectPixelSize + objectPixelSize, addressY + y * objectPixelSize + objectPixelSize, System.Windows.Media.Color.FromArgb(a, r, g, b));
                        logger.Add(loggerPrefix + ": Now painting " + Convert.ToString(areaAddressX) + ", " + Convert.ToString(areaAddressY) + ", " + Convert.ToString(locationInAreaX) + ", " + Convert.ToString(locationInAreaY));
                        //imgBmp.Unlock();
                    }
                }
            }
        }
    }
}
Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,685 questions
.NET Runtime
.NET Runtime
.NET: Microsoft Technologies based on the .NET software framework.Runtime: An environment required to run apps that aren't compiled to machine language.
1,130 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
768 questions
{count} votes

2 answers

Sort by: Most helpful
  1. DaisyTian-1203 11,616 Reputation points
    2021-01-08T07:52:40.53+00:00

    System.Windows.Threading.Dispatcher.Run(); is used to create a new thread and starts it asynchronously, it should not make the unsafe. You can refer to Threading Model to get more details about Dispatcher. With the above code , I can't reproduce the error, the below code may give you some help, could try it to test? If I misunderstand your question, please give me more detailed steps to reproduce the error.

     public void RunTheGraphics()  
            {  
                var newVisualWorldThread = new Thread(ThreadStartingPoint);  
                newVisualWorldThread.SetApartmentState(ApartmentState.STA);  
                newVisualWorldThread.IsBackground = true;  
                newVisualWorldThread.Start();  
            }  
      
            private void ThreadStartingPoint()  
            {  
                VisualWorld vw = new VisualWorld(ref ags, 3840, 2160, ref logger, ref graphQueue);  
                vw.Show();  
                System.Windows.Threading.Dispatcher.Run();  
                while (threadIsRunning == true)  
                {  
                    vw.RunGraphics();  
                    vw.Show();  
                }  
            }  
    

    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


  2. Peter Fleischer (former MVP) 19,231 Reputation points
    2021-01-11T11:24:23.8+00:00

    Hi,
    see my answer in other thread. In these demo I start the VisualWorld window in main thread an fill the BlockingCollection in other thread. In VisualWorld window I get the items from BlockingCollection in separate thread and write items from BlockingCollection via Dispatcher (in main thread). I get following result:

    55355-x.gif

    0 comments No comments