I'm having a lot of pain porting my picturebox from Windows Form to WPF.
My problem is not about the picturebox itself, but I'm having troubles displaying an image gotten from NetworkStream from a Thread into an Image in WPF.
I got this NetworkStream from a TcpClient connection client-server.
This is my Windows Form code (relevant part):
Thread getImage;
void StartReceiving(){
getImage = new Thread(ReceiveImage);
getImage.Start();
}
private void ReceiveImage()
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
while (client.Connected)
{
mainStream = client.GetStream();
try
{
pictureBox1.Image = (Image)binaryFormatter.Deserialize(mainStream);
}
catch (Exception ex) { }
}
}
This code works, I get image in loop every 100 ms and I need to update that source.
In WPF I tried with an Image and setting it's source (I tried both UriSource and StreamSource) but no success. I got thread exceptions, "nothing appens" errors, ...
This is my last code, it results into a Thread error but I don't know what else to try to make it thread compliant. (I only posted the relevant part)
private readonly Thread getImage;
public SecondWindow(int port)
{
InitializeComponent();
client = new TcpClient();
getImage = new Thread(new ThreadStart(ReceiveImage));
while (!client.Connected)
{
server.Start();
client = server.AcceptTcpClient();
}
getImage.Start();
}
private void ReceiveImage()
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
BitmapImage imgSrc = new BitmapImage();
while (client.Connected)
{
var mainStream = client.GetStream();
int loop = 0;
while (!mainStream.DataAvailable && loop < 500)
{
loop++;
Thread.Sleep(10);
}
if (mainStream.DataAvailable)
{
try
{
imgSrc = new BitmapImage();
imgSrc.BeginInit();
imgSrc.StreamSource = mainStream;
imgSrc.CacheOption = BitmapCacheOption.OnLoad;
imgSrc.EndInit();
if (imgSrc.CanFreeze && !imgSrc.IsFrozen)
imgSrc.Freeze();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
if (displayImage.Dispatcher.CheckAccess())
{
displayImage.Source = imgSrc;
}
else
{
Action act = () => { displayImage.Source = imgSrc; };
displayImage.Dispatcher.BeginInvoke(act);
}
}
}
}
In the above code I have 2 problems: 1st that imgSrc can never freeze
2nd (probably a direct consequence of 1st problem), I get InvalidOperationException The calling thread cannot access this object because a different thread owns it
Thanks all for the support
I solved!! the solution was using some other methods in order to deserialize the full stream and then apply it to the image source!
This might not be the best solution but this answer pointed me to the end of this agony
This is my working code:
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeleteObject(IntPtr value);
private void ReceiveImage()
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
System.Drawing.Image imgSrc;
while (client.Connected)
{
var mainStream = client.GetStream();
int loop = 0;
while (!mainStream.DataAvailable && loop < 500)
{
loop++;
Thread.Sleep(10);
}
if (mainStream.DataAvailable)
{
try
{
imgSrc = (System.Drawing.Image)binaryFormatter.Deserialize(mainStream);
var bitmap = new Bitmap(imgSrc);
IntPtr bmpPt = bitmap.GetHbitmap();
BitmapSource bitmapSource =
System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
bmpPt,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
//freeze bitmapSource and clear memory to avoid memory leaks
if (bitmapSource.CanFreeze && !bitmapSource.IsFrozen)
bitmapSource.Freeze();
DeleteObject(bmpPt);
if (displayImage.Dispatcher.CheckAccess())
{
displayImage.Source = bitmapSource;
}
else
{
Action act = () => { displayImage.Source = bitmapSource; };
displayImage.Dispatcher.BeginInvoke(act);
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}