0

I am creating a simple application which scrapes some XML relating to the status of some machine tools which are outputting live sensor data and uses the X/Y coordinates of the device to make a little rat dance around the screen. The rat is placed in the correct location the first time the machine is polled but doesn't move each time the draw function is called by subsequent timer driven events.

I assumed this was just due to machine being on standby and the only coordinate changes being little jitters of the servo but just to check I created a random number generator and had the system use the randomly generated coordinates instead of the scaled X/Y data coming in.

I then found that the rat doesn't move! This is the function where I am drawing the rat(s) (There are 2 systems but we are only worrying about 'bakugo' right now) We are looking particularly at if (dekuWake == false) and (bauwake == true); Here I have had the values printed to the console (Driven by a timer) and the "system.drawing.point(s)" are shown to be valid (in range and changing).

The timer is initiated by a button in form1. Timer event calls polling function which scrapes the XY variables from the site (See my question here for that function - What is wrong with my use of XPath in C#?) At this point it ascertains whether the status was 'AVAILABLE' (which it is) and sets the 'rat's' 'awake' bool to true (determines which images are drawn, if a machine is offline the 'rat' stays in its box) It then scales the coordinates to the resolution of the program window (Normally, right now it is stepping through 2 arrays of integers generated when the polling first begins. The update coordinate function sets the X,Y coords of ImageRat.Bakugo) and calls drawRats().

Why does changing the location of my images not actually relocate the pictureboxes?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Reflection;
using System.Drawing;
using System.Windows.Forms;
namespace XMLRats3
{
    public class Drawing 
    {
        private PictureBox HouseImage;
        private PictureBox DekuImage;
        private PictureBox BakuImage;

        public Drawing(PictureBox house, PictureBox deku, PictureBox baku)
        {
            HouseImage  = house;
            DekuImage   = deku;
            BakuImage   = baku;
        }
        public void ClearRats()
        {
            HouseImage.Hide();
            DekuImage.Hide();
            BakuImage.Hide();
        }

        public void DrawRats(bool DekuWake, bool BakuWake) // Call this function using active status of 2 machines
        {
            ClearRats();

            /*// This shows that the generated coordinates are reaching this point successfully
            Console.WriteLine("BAKU X: " + ImageRat.Bakugo.PosX);
            Console.WriteLine("BAKU Y: " + ImageRat.Bakugo.PosY);
            */

            System.Drawing.Point DekuCoord = new System.Drawing.Point(ImageRat.Deku.PosX, ImageRat.Deku.PosY);      // Create a 'System Point' for Deku
            System.Drawing.Point BakuCoord = new System.Drawing.Point(ImageRat.Bakugo.PosX, ImageRat.Bakugo.PosY);  // Create a 'System Point' for Bakugo


            if (DekuWake == false)
            {
                DekuImage.Hide();
                if (BakuWake == false)
                {
                    BakuImage.Hide();
                    HouseImage.Image = DesktopApp1.Properties.Resources.bothsleep;// set HouseImage to both sleep
                }
                else
                {
                    BakuImage.Location = BakuCoord;
                    Console.WriteLine("Point:" + BakuCoord);
                    //Console.WriteLine("Reaching Relocation condition"); // Ensure we are getting here as animation not working
                    BakuImage.Show();
                    //BakuImage.
                    HouseImage.Image = DesktopApp1.Properties.Resources.dekuSleep; //Set HouseImage to DekuSleep 
                }
            }
            else //DekuWake == true
            {
                DekuImage.Show();
                if (BakuWake == true)
                {
                    HouseImage.Image = DesktopApp1.Properties.Resources.nosleep;//Set House image to nosleep
                    BakuImage.Location = DekuCoord;
                    DekuImage.Show();
                    BakuImage.Location = BakuCoord;
                    BakuImage.Show();
                }    
                else
                {
                    BakuImage.Hide();
                    HouseImage.Image = DesktopApp1.Properties.Resources.bakusleep;// Set house image to bakusleep
                    DekuImage.Location = DekuCoord;
                    DekuImage.Show();
                }
            }

            HouseImage.Show(); // Out here as it should always happen
        }
    }
 }
GigaJoules
  • 53
  • 9
  • So the timer does what? – TaW Nov 16 '18 at 12:49
  • @TaW I added timer info to the question – GigaJoules Nov 16 '18 at 12:57
  • Can you verify if the picture box references you have are the correct ones? – Nico Schertler Nov 16 '18 at 21:13
  • Yeah they are definitely the correct boxes as they are being shown/ hidden correctly, just not moving @NicoSchertler – GigaJoules Nov 19 '18 at 09:05
  • Although there is a mistake where dekuImage is being set to the wrong coord, it shouldn't affect what I'm trying to do atm – GigaJoules Nov 19 '18 at 09:06
  • 1
    You never documented the Timer code you are using. – LarsTech Jan 23 '19 at 14:47
  • No, because I had no idea at the time that the type of the timer even mattered. All I knew was that I was able to successfully call functions from my timer, I just couldn't update the UI. I had certain onclick events for the pictureboxes which would successfully update the image box locations to coordinates which were calculated / generated by the timer function and stored elsewhere which only added to my confusion! Thankfully I see why now! :) – GigaJoules Jan 23 '19 at 14:53
  • I don't see any timer related code in your question. Since the problem is with the timer, we need to see it. – Olivier Jacot-Descombes Jan 23 '19 at 14:53
  • the function called by Timer "Tick" should be in another thread.. and should be able to change the UI per tick... – user1841243 Jan 23 '19 at 14:54
  • You'd think so, wouldn't you – GigaJoules Jan 23 '19 at 14:58
  • @user1841243, no no. In a winforms application use a winforms timer which fires a Tick event in the UI thread. This timer does **not** freeze the UI between ticks. You are not allowed to modify the UI from non-UI threads! – Olivier Jacot-Descombes Jan 23 '19 at 14:58
  • Yeah, this code uses system.timers, rather than the winform timer – GigaJoules Jan 23 '19 at 15:00
  • So, why don't you just drag and drop a timer on to your form and use that? – user1841243 Jan 23 '19 at 15:31
  • Mainly because the moving pictures were a 'cherry on top' - so to speak, and not necessary for what I was actually trying to do. Just haven't really had a reason to go back to it. I'm sure it would work though! – GigaJoules Jan 23 '19 at 15:37

2 Answers2

0

Ok so I don't have an exact answer on how to resolve this but I can tell you why it occurs and point you in the direction of some knowledge that will help you.

At the time I wrote this code I was (and still am) very new to C# and the concept of multithreaded applications in general.

It's a matter of poor software architecture. The problem here is that the UI can only be updated from a single thread in c#, and since the timer runs in another thread, anything called from the timer is not allowed to update the UI. I think it's possible to dip into the UI thread using delegates, though I haven't read into this enough yet to give you exact information on how this is done.

Hopefully this will help the people who have starred my question! How do I update the GUI from another thread?

GigaJoules
  • 53
  • 9
0

Assuming that you are using a timer form a different class, that occupies a different thread than the main one, for a timer to use an object, you should add the object as one of "Synchronizing Objects". So assuming that your timer is called timer1, in a method where you set the properties of the timer, you should write the following line of code

timer1.SynchronizingObject = yourPictureBox;

I hope that solves your problem

Srecko15
  • 3
  • 1