0

I want to have an application that monitors a ceratain folder and show new images that appear in that folder. I successfully managed to detect new images using FileSystemWatcher class, however I have a problem to display them. I have a following code:

public partial class MainWindow : Window
    {
        FileSystemWatcher watcher = new FileSystemWatcher();
        public MainWindow()
        {
            InitializeComponent();
            watcher.Path = "C:/Users/maciejt/Pictures";
            watcher.Filter = "*.jpg";
            watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                | NotifyFilters.FileName | NotifyFilters.DirectoryName;
            watcher.Created += new FileSystemEventHandler(OnCreated);
            watcher.EnableRaisingEvents = true;
        }
        private void OnCreated(object source, FileSystemEventArgs e)
        {
            Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);
            image.Dispatcher.Invoke(new Action(() => { image.Source = new BitmapImage(new Uri(e.FullPath)); }));
        }        
    }

It works for some 10-20 creations of small (~100kB) images and 1-2 large (~4MB). Later it throws an exception that the image is used by another process. I also observed in the debugger, that memory used by the application increases drastically with every new image, just like previous images were not disposed.

What would be a correct way to create a BitmapImage and show it in Image control?

EDIT:

I have tried a solution suggested in a possible duplicate, however that still gives me the same exception, not even working once this time. Below the code after modification:

private void OnCreated(object source, FileSystemEventArgs e)
        {
            Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);            
            image.Dispatcher.Invoke(new Action(() => { image.Source = LoadBitmapImage(e.FullPath); }));
        }

public static BitmapImage LoadBitmapImage(string fileName)
        {
            using (var stream = new FileStream(fileName, FileMode.Open))
            {
                var bitmapImage = new BitmapImage();
                bitmapImage.BeginInit();
                bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                bitmapImage.StreamSource = stream;
                bitmapImage.EndInit();
                bitmapImage.Freeze();
                return bitmapImage;
            }
        }
mactro
  • 518
  • 7
  • 13
  • Possible duplicate of [Image file copy, is being used by another process](http://stackoverflow.com/questions/18167280/image-file-copy-is-being-used-by-another-process) – Sinatr Sep 12 '16 at 11:07

2 Answers2

0

I have managed to find a working solution:

private void OnCreated(object source, FileSystemEventArgs e)
    {
        Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);
        image.Dispatcher.Invoke(new Action(() => { image.Source = LoadBitmapImage(e.FullPath); }));

    }

public static BitmapImage LoadBitmapImage(string fileName)
    {
        while (!IsFileReady(fileName))
        {
            Thread.Sleep(100);
        }
        using (var stream = new FileStream(fileName, FileMode.Open))
        {
            var bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.StreamSource = stream;
            bitmapImage.EndInit();
            bitmapImage.Freeze();
            return bitmapImage;
        }
    }
public static bool IsFileReady(String sFilename)
        {
            // If the file can be opened for exclusive access it means that the file
            // is no longer locked by another process.
            try
            {
                using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None))
                {
                    return (inputStream.Length > 0);   
                }
            }
            catch (Exception)
            {
                return false;
            }
        }

My guess is that the FileSystemWatcher takes ownership of the file for a while, when it's checking for any modifications. When I wait for a while, the file is unlocked and ready to process safely. Still, if anyone has more in-depth explanation or a better solution, I will happily see it.

mactro
  • 518
  • 7
  • 13
  • I think this is working for you because while you wait in WHILE loop, garbage collector releases the bitmap objects you created. So you can create new BItmap using same file. – Kamalesh Wankhede Sep 12 '16 at 13:16
0

You are may be facing this issue because you are not disposing the bitmap objects you have created. Try following solution. This will dispose your previous Bitmap when new bitmap is created.

BitmapImage _image;
private void OnCreated(object source, FileSystemEventArgs e)
{
    if (_image != null)
    {
      FreeMemoryAcquiredByImage(_image);
      _image = null;
    }

    _image = new BitmapImage(new Uri(e.FullPath));
    //You may have to freeze _image here.

    image.Dispatcher.Invoke(new Action(() => { image.Source = _image; }));
}
Kamalesh Wankhede
  • 1,475
  • 16
  • 37
  • Does the `Bitmap` class exist in WPF? Can't use it... And the `Image` and `BitmapImage` classes don't have `Dispose()` method. – mactro Sep 12 '16 at 13:34