0

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);
                }
            }
        }
    }
Pier Giorgio Misley
  • 5,305
  • 4
  • 27
  • 66
  • I would post the exact error message. – Sorceri Aug 20 '19 at 22:29
  • Please note that your current code does not use the MemoryStream at all. You may however not need it at all, and directly assign `mainStream` to the StreamSource property. Then it should work without creating an intermediate Bitmap. – Clemens Aug 21 '19 at 06:43
  • @Clemens I just re-read my question and noticed that it was horrible, I'll never ask again when I'm sleeping on my keyboard ^^ btw I already tried both freezing and setting the stream directly but I always got `The calling thread cannot access this object because a different thread owns it`.. I'm gonna reorder my code and ask again in a good way. Thanks for the links! – Pier Giorgio Misley Aug 21 '19 at 07:51
  • Calling `GeneratedImage = bImg;` in the Dispatcher Action certainly makes no sense. You would instead assign an Image element's Source property. – Clemens Aug 21 '19 at 08:24
  • @Clemens I edited the question, now it has more sense. I added the two problems I get but I still find no solutions, I also tried waiting until mainStream has data available, but without any success. – Pier Giorgio Misley Aug 21 '19 at 08:31
  • @Clemens solved! maybe if you have the permissions for it, change the duplicated question link to the question I linked in my final notes. Thanks for your support – Pier Giorgio Misley Aug 21 '19 at 09:09
  • It shouldn't matter *how* you convert a Bitmap to BitmapSource (i.e. whether you transcode by an intermediate MemoryStream, or use CreateBitmapSourceFromHBitmap). The important point is that you most certainly do not need the intermediate Bitmap at all. You should be able to directly assign the client stream to the StreamSource property of a BitmapImage, or use an intermediate MemoryStream with [CopyTo](https://learn.microsoft.com/en-us/dotnet/api/system.io.stream.copyto?view=netframework-4.8). – Clemens Aug 21 '19 at 12:53
  • I don't really know why but it doesn't work. I thought it was couse the stream didn't finish obtaining data yet, but it wasn't this. The above method was the only one that worked in my tries.. If I find any other simpler way, i'll update here – Pier Giorgio Misley Aug 21 '19 at 13:28

0 Answers0