Reputation: 10454
What I'm trying to do is I draw an image in the gui thread, and I have another thread that desializes the object that has the image and sends it to tcp socket every second. The image in the paint event is changing, but it is not changing in the tcp thread. Here is the trimmed down code:
// GUI class:
private static readonly object lock_obj = new object();
private GraphicsServerReceiver server;
private Bitmap gl_image;
private void checkBoxActivateBroadcast_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxActivateBroadcast.Checked)
{
this.server = new GraphicsServerReceiver(SERVER_PORT, gl_image);
}
}
private void glControl1_Paint(object sender, PaintEventArgs e)
{
// do some drawing
lock (lock_obj)
{
gl_image = TakeScreenshot();
}
}
public Bitmap TakeScreenshot()
{
if (GraphicsContext.CurrentContext == null)
throw new GraphicsContextMissingException();
int w = glControl1.ClientSize.Width;
int h = glControl1.ClientSize.Height;
Bitmap bmp = new Bitmap(w, h);
System.Drawing.Imaging.BitmapData data =
bmp.LockBits(glControl1.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
GL.ReadPixels(0, 0, glControl1.ClientSize.Width, glControl1.ClientSize.Height, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
bmp.UnlockBits(data);
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
return bmp;
}
#----------------
# the GraphicsServerReceiver class:
private Bitmap gl_image;
private static readonly object lock_obj = new object();
public GraphicsServerReceiver(int port, Bitmap gl_image)
{
this.port = port;
this.gl_image = gl_image;
this.tcpListener = new TcpListener(IPAddress.Loopback, port);
this.tcpListener.Start();
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
while (true)
{
try
{
TcpClient client = this.tcpListener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
catch (Exception ex)
{
Console.WriteLine("Error: Server was stopped so program failed to listen to clients. " + ex.Message);
}
}
}
private void HandleClientComm(object client)
{
byte[] buffer;
lock(lock_obj)
{
databag.img = gl_image;
buffer = Serializer.ObjectToByteArray(databag);
}
// send the data in buffer...
}
It must be a multi-threading problem but I do not see it. I've used different lock objects in the 2 classes. Could that be causing this?
Added new info: Here is a complete code in a single file that reproduces the problem without the paint event:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private static readonly object lock_obj = new object();
private Bitmap gl_image;
private GraphicsServerReceiver server;
private bool server_running = false;
public Form1()
{
InitializeComponent();
}
private void btn1_Click(object sender, EventArgs e)
{
if (!server_running)
{
lock (lock_obj)
{
gl_image = new Bitmap("C:/temp/x.bmp");
}
this.server = new GraphicsServerReceiver(gl_image);
btn1.Text = "stop";
}
else
{
server.stop();
btn1.Text = "start";
}
server_running = !server_running;
}
private void btn2_Click(object sender, EventArgs e)
{ // change the image
lock (lock_obj)
{
// draw a line:
using (var graphics = Graphics.FromImage(gl_image))
{
Pen blackPen = new Pen(Color.Black, 3);
graphics.DrawLine(blackPen, 0, 0, 50, 50);
}
gl_image.Save("C:/temp/changed.bmp");
}
}
}
class GraphicsServerReceiver
{
private Bitmap gl_image;
private static readonly object lock_obj = new object();
private Thread listenThread;
public void stop()
{
listenThread.Abort();
listenThread.Join(1);
}
public GraphicsServerReceiver(Bitmap gl_image)
{
this.gl_image = gl_image;
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
try
{
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start("some param");
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
}
private void HandleClientComm(object useless_obj)
{
while (listenThread.IsAlive)
{
lock (lock_obj)
{
gl_image.Save("C:/temp/out.bmp");
}
Thread.Sleep(1000);
}
}
}
}
Upvotes: 1
Views: 294
Reputation: 10454
Workaround: Instead of using the gl_image and then putting a lock on it, I made a update_image method in my server and I call that whenever I draw something new. This works.
However, I still do not know the reason why my reference was not getting updated in the other approach that I first tried so if anyone can explain that correctly, I'd appretiate and up-vote.
Upvotes: 0
Reputation: 581
You're using the OnPaint event to redraw the image. Unless you minimize your window, move or do something that internally fires WM_PAINT, you're image will never be draw.
A suggestion is after you receive the image from the server, you call:
yourControl.Invoke(InvalidateControl);
public void InvalidateControl(){
yourControl.Invalidate();
}
The "Invoke" is needed to change something in the GUI from other thread. You can also use an Image Control and just update the image instead of using the OnPaint event.
Upvotes: 1