1

I'm trying to update the GUI, and I have an asynchronous function that uses LoadAsyc(), when I load just one image, it works but when I try to load more than one, the second one doesn't display.

This my code:

public UserFriendlyInterface()
{
    InitializeComponent();

    locationFileH5 = "";
    serverStatus = false;
    ipAddress = getLocalIPAddress();
    port = 5000;
    watcher = new FileSystemWatcher(@"flask_server\cnn\_prepImages_");
    watcher.EnableRaisingEvents = true;
    watcher.Changed += watcher_Changed;
}

private void watcher_Changed(object sender, FileSystemEventArgs e)
{
    updateImages();
}

async Task updateImages()
{
    pictureBoxNormalImg.WaitOnLoad = false;
    pictureBoxNormalImg.LoadAsync(@"flask_server\cnn\_prepImages_\normal.jpg");

    pictureBoxSegmentation.WaitOnLoad = false;
    pictureBoxSegmentation.LoadAsync(@"flask_server\cnn\_prepImages_\segmentation.jpg");
}
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Victor Bryant
  • 11
  • 1
  • 5
  • you will have to await the async call – Shantanu Sep 22 '19 at 05:44
  • Please, can you tell how and where I should put it? – Victor Bryant Sep 22 '19 at 05:52
  • So if you comment out the first load, does the second image work? – TheGeneral Sep 22 '19 at 06:02
  • It's going to work, however, I need to display both. – Victor Bryant Sep 22 '19 at 06:20
  • What are you targetting: Winforms, WPF, ASP..? YOU should __always__ TAG your questions correctly so one can see it on the questions page! – TaW Sep 22 '19 at 07:28
  • Your code works fine here. Most likely your 2nd path is wrong. – TaW Sep 22 '19 at 07:37
  • About the files "normal.jpg" and "segmentation.jpg", how are they changed? Depending on the way you change these files, the [`FileSystemWatcher.Changed`](https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher.changed) event will be raised one or two times for each file. So the `UpdateImages` method could be called 4 times per operation, and some of them could be while one of the files is updated. So there are a lot of possible reasons why this code may behave in unpredictable ways. – Theodor Zoulias Sep 22 '19 at 07:43
  • There is no filter and so no image needs to be changed. It is enough to change, add or delete any file in the folder. – TaW Sep 22 '19 at 07:44
  • The folder that contains those two images, it's being updated by overwriting the images, then I want to delete the previous images & display the new ones, when the directory changes. – Victor Bryant Sep 22 '19 at 13:20
  • @VictorBryant what should happen if only one image in the folder is overwritten, and the other is not? What if both are overwritten, but with a delay of 10 seconds between the first and the second? – Theodor Zoulias Sep 22 '19 at 13:47
  • Good question! I'm sure that all of them are overwritten, but maybe one of them with a delay of microseconds. Then, I could use Task.Delay before updating the GUI. – Victor Bryant Sep 22 '19 at 15:00
  • I hadn't thought of that. – Victor Bryant Sep 22 '19 at 15:01
  • Theodor Zoulias. I just added the Task.Delay() on the first line of the updateImage() function with a delay of 700, and it works. You were right, I had to wait until the whole directory has been updated. – Victor Bryant Sep 22 '19 at 16:04

1 Answers1

0

What you are trying to achieve can be achieved more robustly by querying the Name property of the FileSystemEventArgs object, and updating only the corresponding PictureBox.

private static void Watcher_Changed(object sender, FileSystemEventArgs e)
{
    PictureBox pictureBox;
    switch (e.Name.ToLowerInvariant())
    {
        case "normal.jpg": pictureBox = pictureBoxNormalImg; break;
        case "segmentation.jpg": pictureBox = pictureBoxSegmentation; break;
        default: pictureBox = null; break;
    }
    if (pictureBox != null)
    {
        Image image = null;
        try
        {
            using (var temp = new Bitmap(e.FullPath))
            {
                image = new Bitmap(temp);
            }
        }
        catch { } // Swallow exception
        if (image != null)
        {
            pictureBox.Invoke((MethodInvoker)(delegate ()
            {
                pictureBox.Image = image;
            }));
        }
    }
}

I would avoid the LoadAsync method because it is intended mainly for loading images from the internet, and because I don't totally trust it.


Update: There were two problems with my initial code:
1) Free file locked by new Bitmap(filePath)
2) FileSystemWatcher Changed event is raised twice

The updated code solves these problems (hopefully), but not in the most robust or efficient way possible.


Update: To make the code more efficient, by avoiding the repeated loading of the images caused by multiple firings of the Changed event, you could use the extension method OnChanged found in this answer. It suffices to replace the line below:

watcher.Changed += Watcher_Changed;

...with this one:

watcher.OnChanged(Watcher_Changed, 100);
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
  • I have two pictures boxes on a form, but I can guarantee that both images get to the file almost at the same time, just with a delay of microseconds, that's why I'm using Task.Delay() at the top of the updateImage() function. – Victor Bryant Sep 23 '19 at 19:07
  • I just tested your code and when I try for the second time. It doesn't display the new image, it keeps the first image. – Victor Bryant Sep 23 '19 at 19:09
  • I updated my answer. You should consider adding [logging](https://stackoverflow.com/questions/5057567/how-do-i-do-logging-in-c-sharp-without-using-3rd-party-libraries) to your application, so that you can log the exceptions. It helps with debugging too! – Theodor Zoulias Sep 24 '19 at 07:30