PictureBox doesn't work as expected

sir_pog

New member
Joined
Dec 18, 2021
Messages
1
Programming Experience
Beginner
Hello! I am trying to create an application that displays random images from Reddit every X seconds in new Form. Everything seems to be working fine except the PictureBox itself. In order to add the image to the PictureBox I am using PictureBox.LoadAsync() method. The whole process is being run using BackgroundWorker. Here is the whole code:

Form1.cs:
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;

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

        private void Form1_Load(object sender, EventArgs e)
        {
            InitializeComponent();

            backgroundWorker1.RunWorkerAsync();
            backgroundWorker1.DoWork += backgroundWorker1_DoWork;
            backgroundWorker1.WorkerReportsProgress = true;
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            Stopwatch timer = new Stopwatch();
            timer.Start();

            while (true)
            {
                int seconds = int.Parse($"{timer.Elapsed.TotalSeconds:F0}");
                if (seconds > 0 && seconds % 1 == 0)
                {
                    Form _newForm = new Form();
                    FormCreation FC = new FormCreation();
                    FC.CreateForm(ref _newForm);

                    this.BeginInvoke((MethodInvoker)_newForm.Show);

                    Thread.Sleep(1000);
                }
            }
        }
    }
}

FormCreation.cs:
using System;
using System.Net.Http;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
using System.Net;
using Newtonsoft.Json;

namespace VRDCT
{
    public class FormCreation
    {
        public class ImageInformation
        {
            WebClient wc = new WebClient();

            public int X { get; set; }

            public int Y { get; set; }

            public string URL { get; set; }

            public void DownloadImageAndGetDimensions(string url)
            {
                Random rnd = new Random();
                string chars = "abcdefghijklmnopqrstuvwxyz";
                string fileName = new string(Enumerable.Repeat(chars, 6)
                    .Select(s => s[rnd.Next(s.Length)]).ToArray());

                string path = $"A:\\{fileName}.jpeg";
                wc.DownloadFile(url, path);

                GetImageDimensionX(path);
                GetImageDimensionY(path);
            }

            public int GetImageDimensionX(string path)
            {
                Image imageInfo = Image.FromFile(path);
                return this.X = imageInfo.Width;
            }

            public int GetImageDimensionY(string path)
            {
                Image imageInfo = Image.FromFile(path);
                return this.Y = imageInfo.Height;
            }
        }

        private string GetImageUrl(string url)
        {
            using (HttpClient hp = new HttpClient())
            {
                var response = hp.GetStringAsync(url).Result;
                return response;
            }
        }

        public void CreateForm(ref Form _form)
        {
            var desirializedObject = JsonConvert.DeserializeObject<ImageInformation>(GetImageUrl("https://meme-api.herokuapp.com/gimme"));
            ImageInformation imageDimensions = new ImageInformation();
            imageDimensions.DownloadImageAndGetDimensions(desirializedObject.URL);

            _form.Width = imageDimensions.X;
            _form.Height = imageDimensions.Y;

            PictureBox PB = new PictureBox
            {
                Name = "catPictureBox",
                Size = new Size(_form.Width, _form.Height),
                Location = new Point(0, 0),
                Visible = true
            };
            PB.LoadAsync(desirializedObject.URL);
            _form.Controls.Add(PB);
        }
    }
}

Here's what I am facing every time running the application:
1639859756402.png
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,085
Location
Chesapeake, VA
Programming Experience
10+
Last edited:

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,085
Location
Chesapeake, VA
Programming Experience
10+
WTF?!?? Converting a double to a string without decimal points and then parsing the string?
C#:
int seconds = int.Parse($"{timer.Elapsed.TotalSeconds:F0}");
Why not just cast to an int?
C#:
int seconds = (int) timer.Elapsed.TotalSeconds;
or alternately:
C#:
int seconds = (int) (timer.ElapsedMilliseconds / 1000);
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,085
Location
Chesapeake, VA
Programming Experience
10+
WTF?!?!?!?
C#:
if (seconds > 0 && seconds % 1 == 0)
can simply be
C#:
if (seconds > 0)
because any positive integer modulus 1 will always be 0.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,085
Location
Chesapeake, VA
Programming Experience
10+
This:
C#:
while (true)
{
    int seconds = int.Parse($"{timer.Elapsed.TotalSeconds:F0}");
    if (seconds > 0 && seconds % 1 == 0)
    {
        Form _newForm = new Form();
        FormCreation FC = new FormCreation();
        FC.CreateForm(ref _newForm);

        this.BeginInvoke((MethodInvoker)_newForm.Show);

        Thread.Sleep(1000);
    }
}

could simply be:
C#:
while (true)
{
    Thread.Sleep(1000);

    Form _newForm = new Form();
    FormCreation FC = new FormCreation();
    FC.CreateForm(ref _newForm);
    this.BeginInvoke((MethodInvoker)_newForm.Show);
}
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,085
Location
Chesapeake, VA
Programming Experience
10+
Why do things this way?
C#:
DownloadImageAndGetDimensions(string url)
{
    :
    
    GetImageDimensionX(path);
    GetImageDimensionY(path);
}

public int GetImageDimensionX(string path)
{
    Image imageInfo = Image.FromFile(path);
    return this.X = imageInfo.Width;
}

public int GetImageDimensionY(string path)
{
    Image imageInfo = Image.FromFile(path);
    return this.Y = imageInfo.Height;
}
You basically load the image twice taking up 2 times the memory. Recall that the memory won't be cleared up until garbage collection happens later.

You should have just done:
C#:
DownloadImageAndGetDimensions(string url)
{
    :
    
    Image imageInfo = Image.FromFile(path);
    this.X = imageInfo.Width;
    this.Y = imageInfo.Height;
}

And since you've already downloaded the image and have an image object? Why not just hold on to it and pass it into the PictureBox instead of calling LoadAsync() to download the image yet again?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,085
Location
Chesapeake, VA
Programming Experience
10+
C#:
using (HttpClient hp = new HttpClient())

You might want to read:
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,085
Location
Chesapeake, VA
Programming Experience
10+
C#:
Random rnd = new Random();
string chars = "abcdefghijklmnopqrstuvwxyz";
string fileName = new string(Enumerable.Repeat(chars, 6)
                             .Select(s => s[rnd.Next(s.Length)]).ToArray());

string path = $"A:\\{fileName}.jpeg";

See:

Also most people don't have an A drive present or mapped.
 

JohnH

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
1,415
Location
Norway
Programming Experience
10+
Rewrote this just for fun, here using a background thread with a loop that show each image for two seconds then close it. Loads image only once.
Added some layout improvements to image display (size, placement, title). Images can be clicked to open related Reddit post.
The main form is set as TopMost, both for easier stopping the loop and to keep images in front when opening posts in browser.
C#:
private bool running;
private HttpClient client = new HttpClient();

private void StartStopButton_Click(object sender, EventArgs e)
{
    running = !running;
    if (running)
    {               
        var showImages = new Thread(ImageLoop) { IsBackground = true };
        showImages.Start(2000);
    }           
}

private async void ImageLoop(object state)
{
    var duration = (int)state;
    while (running)
    {
        var info = await GetImageInfoAsync();
        var filename = await DownloadImageAsync(info.URL);
        using (var img = Image.FromFile(filename))
        using (var sync = new AutoResetEvent(false))
        {
            Invoke(new Action(() => ShowImage(img, info, duration, sync)));                   
            sync.WaitOne();
        }
        File.Delete(filename);
    }
}

private async void ShowImage(Image img, ImageInformation info, int duration, AutoResetEvent sync)
{
    var pb = new PictureBox()
    {
        Dock = DockStyle.Fill,
        SizeMode = PictureBoxSizeMode.Zoom,
        Image = img,
        Cursor = Cursors.Hand
    };
    pb.Click += (s, e) => { Process.Start(info.URL); };
    var form = new Form()
    {
        Size = MaxSize(img.Size),
        StartPosition = FormStartPosition.CenterScreen,
        FormBorderStyle = FormBorderStyle.FixedSingle,
        Text = info.Title,
        ControlBox = false,
        ShowInTaskbar = false
    };
    form.Controls.Add(pb);
    form.Show(this);
    await Task.Delay(duration);
    form.Close();
    sync.Set();
}

private Size MaxSize(Size sz)
{
    var maxscreen = Screen.FromPoint(Location).WorkingArea.Size;
    var scale = Math.Min((float)maxscreen.Width / sz.Width, (float)maxscreen.Height / sz.Height);
    return (scale > 1) ? sz : Size.Round(new SizeF(scale * sz.Width, scale * sz.Height)); // if only scale down
    //return Size.Round(new SizeF(scale * sz.Width, scale * sz.Height)); // if always maximize
}

private async Task<ImageInformation> GetImageInfoAsync()
{
    var json = await client.GetStringAsync("https://meme-api.herokuapp.com/gimme");
    return JsonConvert.DeserializeObject<ImageInformation>(json);           
}

private async Task<string> DownloadImageAsync(string url)
{
    var imageResponse = await client.GetAsync(url);
    var filename = $"N:\\{Path.GetRandomFileName()}{Path.GetExtension(url)}";
    using (var file = File.OpenWrite(filename))
        await imageResponse.Content.CopyToAsync(file);
    return filename;
}

public class ImageInformation
{
    public string URL { get; set; }
    public string PostLink { get; set; }
    public string Title { get; set; }
}
 
Top Bottom