-1

I am developing the GUI on C# as windows form Application. I am receiving the data stream from Client (as a console App) on GUI Server (as windows form App) using TCP/IP protocol. The stream pattern receives like:

enter image description here

The trouble I am facing in the graphics plotting. I plot two graphics objects on an image in a picture box. While plotting the graphics the second shows flickering while plotting. I really don't understand why it produces flicker in second (Red) graphic plot. I am sharing my Server code here, Please tell me where I am mistaken.

Server Code (GUI): 
    using System;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Net;
    using System.Net.Sockets;
    using System.Collections;
    using System.Threading; 

        namespace GUI_Server
         {
          public partial class Form1 : Form
          {
            public Form1()
            {
              InitializeComponent();           
            }

          Thread listener; 
          Int32 port = 3000;
          IPAddress ip = IPAddress.Parse("127.0.0.1");
          ArrayList nSockets;
          String[] PART = null;
          String data = null;

          private System.Drawing.Graphics g, path;

          Double w, x, y, z;

          private void Form1_Load(object sender, EventArgs e)
           {
              pictureBox1.Image = new Bitmap("Image");
              g = pictureBox1.CreateGraphics();
              path = Graphics.FromImage(pictureBox1.Image);

              nSockets = new ArrayList();
              label1.Text = "IP Address:" + ip;
              listener = new Thread(listen);
              listener.Start();
           }

          public void listen()//thread
           {
               TcpListener tcpListener = new TcpListener(ip, port);
               tcpListener.Start();

               while (true)
               {
                   Socket handlerSocket = tcpListener.AcceptSocket();
                   if (handlerSocket.Connected)
                {
                    Control.CheckForIllegalCrossThreadCalls = false;
                    label2.Text = "Connected";
                    lock (this)
                    {
                        nSockets.Add(handlerSocket);
                    }
                    ThreadStart thdstHandler1 = new ThreadStart(handlerThread1);
                    Thread thdHandler1 = new Thread(thdstHandler1);
                    thdHandler1.Start();
                   }
               }
           }

           public void handlerThread1() // thread
           {
            Socket handlerSocket = (Socket)nSockets[nSockets.Count - 1];
            NetworkStream networkStream = new NetworkStream(handlerSocket);
            Byte[] bytes = new Byte[1024];

            int k;

            lock (this)
            {
                while ((k = networkStream.Read(bytes, 0, bytes.Length)) != 0)
                {
                    data = Encoding.ASCII.GetString(bytes, 0, k);
                    PART = data.Split('\t');

                    if (Convert.ToDouble(part[0]) == 1)
                    {
                        w = (Convert.ToDouble(part[1]) / 0.0347) + 60;
                        x = (Convert.ToDouble(part[2]) / 0.0335) + 656;
                        textBox1.Text = ("Tag ID_Blue: " + part[0] + "\t" + "X: " + part[2] + "\t" + "Y: " + part[1]);                       
                    }
                    g.DrawRectangle(new Pen(Color.Blue, 3), Convert.ToInt32(x), 
                    Convert.ToInt32(w), 6, 6); // first graphic

                    if (Convert.ToDouble(part[0]) == 2)
                    {
                        y = (Convert.ToDouble(part[1]) / 0.0347) + 60;
                        z = (Convert.ToDouble(part[2]) / 0.0335) + 656;
                        textBox2.Text = ("Tag ID_Red: " + part[0] + "\t" + "X: " + part[2] + "\t" + "Y: " + part[1]);                       
                    }
                    g.DrawRectangle(new Pen(Color.Red, 3), Convert.ToInt32(z), Convert.ToInt32(y), 6, 6); // second graphic

                     Thread.Sleep (50);
                     pictureBox1.Refresh();


                    byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
                    networkStream.Write(msg, 0, msg.Length);
                }               
            }
            handlerSocket = null;
        }

is there any way to refresh just graphics, not the whole picture box. Does the flickering occur due to refresh the picture box?

SN25
  • 69
  • 1
  • 12
  • 1
    Well you should remove this line `Control.CheckForIllegalCrossThreadCalls = false;` (see [here](https://stackoverflow.com/questions/13345091/is-it-safe-just-to-set-checkforillegalcrossthreadcalls-to-false-to-avoid-cross-t) and instead make `Invoke` or `BeginInvoke` calls from your background thread to update the UI. Not sure if that is part of your problem, but is certainly a candidate. Also, I may not be understanding what all you are doing, but it looks like you have the potential to be launching a lot of threads. This seems like a classic Producer/Consumer, consider a `ConcurrentQueue` – pstrjds Dec 26 '17 at 07:06
  • Side note - `ArrayList` is not recommended for new code, use a `List` instead and also you should avoid calling `lock(this)` see [this](https://stackoverflow.com/a/251668/416574) SO answer for a good explanation of why you shouldn't. – pstrjds Dec 26 '17 at 07:16

1 Answers1

0

Let's try to clean this up a bit and see if we can't solve the flicker. I am not definite that it is related to the setting Control.CheckForIllegalCrossThreadCalls to false, but there are other things to clean up and we will remove that call as well and hopefully it will make things run better for you.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();           
    }

    const int port = 3000;
    ManualResetEvent _closing = new ManualResetEvent(false);

    private void Form1_Load(object sender, EventArgs e)
    {
        pictureBox1.Image = new Bitmap("Image");
        var ip = IPAddress.Parse("127.0.0.1");
        label1.Text = "IP Address:" + ip;
        Task.Run(() => Listen());
    }

    private void Listen()
    {
        var tcpListener = new TcpListener(ip, port);
        tcpListener.Start();

        while (!_closing.WaitOne(100))
        {
            if (tcpListener.Pending())
            {
                tcpListener.AcceptSocketAsync().ContinueWith( (socket) =>
                {
                    NetworkStream ns = null;
                    MemoryStream ms = null;
                    try
                    {
                        if (!socket.Connected) return;
                        label2.Invoke(() => label2.Text = "Connected");

                        ns = new NetworkStream(socket);
                        ms = new MemoryStream();
                        ns.CopyTo(ms); // Copies from the NetworkStream to the MemoryStream. Should use a default buffer size of 81920
                        var msg = ms.ToArray();
                        var parts = Encoding.ASCII.GetString(msg).Split('\t');
                        if (parts.Length != 3) return; // simple error check

                        // You could use TryParse for error checking                     
                        var w = (double.Parse(part[1]) / 0.0347) + 60.0; 
                        var x = (double.Parse(part[2]) / 0.0335) + 656.0;                              

                        string tag = "";
                        Color color;
                        if (part[0].Equals("1", StringComparison.Ordinal))
                        {
                            tag = $"Tag ID_Blue: {part[0]}\tX {part[2]}\tY: {part[1]};
                            color = Color.Blue;
                        }
                        else if (part[0].Equals("2", StringComparison.Ordinal))
                        {
                            tag = $"Tag ID_Red: {part[0]}\tX {part[2]}\tY: {part[1]};
                            color = Color.Red;
                        }
                        else return; // Shouldn't hit this

                        UpdateUiDrawRectangle(tag, color, (int)x, (int)w);

                        // Your code was writing the same message back
                        ns.Write(msg, 0, msg.Length);
                    }
                    finally
                    {
                        // Need to cleanup resources
                        ns?.Dispose();
                        ms?.Dispose();
                        socket.Close();
                    }
                }
            }
        }
    }

    private void UpdateUiDrawRectangle(string tag, Color color, int x, int w)
    {
        if (this.InvokeRequired)
        {
            this.Invoke(() => UpdateUiDrawRectange(tag, color, x, w));
            return;
        }
        Graphics g = null;
        Pen pen = null;
        try
        {
            pen = new Pen(color, 3);
            g = pictureBox1.CreateGraphics();
            g.DrawRectangle(pen, x, w, 6, 6);
            textBox2.Text = tag;
        }
        finally
        {
            g?.Dispose();
            pen?.Dispose();
        }
        pictureBox1.Invalidate(); // Instead of Refresh
    }

Explanation of the code and changes.
First, instead of a while(true) loop we are using a wait handle (in this case a ManualResetEvent). This will allow you to add code into your FormClosing or FormClosed event to set the event which will cause your loop to exit. The WaitOne call will check to see if the handle has been signaled. If it hasn't, it will block for 100 milliseconds (adjust as necessary) and if it still isn't signaled, will execute the code in the loop. Next we check to see if there is a pending connection, then we use the async accept method which returns a Task object with the socket, once the connection is made it will return. We can use the ContinueWith pattern to make use of the socket once it is accepted. In this code we handle opening the stream and reading the message, and then preparing to draw the rectangle. I have added a new method that will draw the rectangle. Notice that in the method it does a check for InvokeRequired and if needed, will call the method with the Invoke so that we can perform all of our UI operations on the UI thread. I also have added Dispose calls for the resources. You will notice that we no longer need an ArrayList or collection of sockets at all, the ContinueWith takes care of using the socket that we opened. We also no longer need to lock around parts of the code and we have been able to remove most of the global/class wide variables and use local variables instead. We create the Graphics objects when we need them and then dispose of them.

There is more cleanup possible, but this should move you in the right direction. Note, there may be typos as I wrote this by hand and haven't tried to compile it, but anything like that should be minor and easy to cleanup.

pstrjds
  • 16,840
  • 6
  • 52
  • 61
  • Thank you so much... for your reply and such great effort.I will implement my code like this way... but can you tell me the possible ways to avoid flickering of graphics drawn on image ina picture box? – SN25 Dec 26 '17 at 09:16
  • @SN25 - I think this code will help remove the flickering. I haven't tested it so I can't say for sure, but try this out and see. It should at least be a little bit better. – pstrjds Dec 26 '17 at 09:17
  • The current issue I am facing in my program is the plotting delay... is it due to the TCP/ IP communication? does the processing slow at the server end? – SN25 Dec 28 '17 at 08:44
  • @SN25 - What do you mean by the `plotting delay`. Do you mean that things aren't being drawn fast enough? It is hard to say what would be the cause. It could be the communication. You would have to add logging/tracing or whatever and narrow down where the time is being spent. If you have implemented the above code, you are only pausing for 100 milliseconds between checks for communication, you could adjust that down to 50 or something like that and see if that makes a difference. First I would probably add some tracing to see if you are actually getting a connection that fast. – pstrjds Dec 28 '17 at 09:01
  • The Tags (graphics) movement does not synchronize with the real-time movement. Like if I hold the Tag and I walk for a while, and suddenly stop so the tag stop on the display after some seconds delay. – SN25 Dec 29 '17 at 00:50
  • Thank you for your reply... The Tags (graphics) movement does not synchronize with the real-time movement. Like if I hold the Tag and I walk for a while, and suddenly stop so the tag stop on the display after some seconds delay. One additional question I want to ask If I use "DrawImage(new Bitmap...)" instead of "DrawRectangle ()" does it cause flickering or affect the plotting speed? (Sorry I ask so many questions :( ) – SN25 Dec 29 '17 at 01:06
  • @SN25 - In regards to the plotting not stopping for a few seconds, I don't think dropping the loop time will help that but you could always set `WaitOne(0)` and see. I would verify how fast you are getting the tcp/ip messages (add a log/trace statement with a time stamp after checking `Pending()`). Whether `DrawImage` will be better than `DrawRectangle`, you would have to test it and find out. My guess is it would be a little slower, but try it and find out. If you get stuck, ask a new question (not in comments, I mean an actual question). – pstrjds Dec 29 '17 at 06:06