Search code examples
wpffilesystemwatcher

FileSystemWatcher locks file, how to release it?


I am using a FileSystemWatcher to raise the event when an image file is edited on Paint and update the preview image control using it. But setting the file as a source second time, it throws an error because the file is still in use by another process. So I discovered this is because of FileSystemWatcher.

I have this code:

    private void btnEdit_Click(object sender, RoutedEventArgs e)
    {
        if (!File.Exists(lastImage)) return;
        FileSystemWatcher izleyici = new FileSystemWatcher(System.IO.Path.GetDirectoryName( lastImage),
            System.IO.Path.GetFileName(lastImage));
        izleyici.Changed += izleyici_Changed;
        izleyici.NotifyFilter = NotifyFilters.LastWrite;
        izleyici.EnableRaisingEvents = true;
        ProcessStartInfo info = new ProcessStartInfo();
        info.FileName = lastImage;
        info.Verb = "edit";
        Process.Start(info);
    }

    void izleyici_Changed(object sender, FileSystemEventArgs e)
    {
       //I want to add code here to release the file. Dispose() not worked for me

       setImageSource(lastImage);
    }

    void setImageSource(string file)
    {
        var bitmap = new BitmapImage();

        using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            bitmap.BeginInit();
            bitmap.CacheOption = BitmapCacheOption.OnLoad;
            bitmap.StreamSource = stream;
            bitmap.EndInit();
        }

        ssPreview.Source = bitmap;
    }

In this code I want to release the file before updating the Image control. I tried Dispose but it was not worked. How can I do that?


Solution

  • The file is neither locked by the FileSystemWatcher, nor by MS Paint. What actually happens is that you get an InvalidOperationException, because the FileSystemWatcher's Changed event is not fired in the UI thread, and hence the handler method can't set the Image control's Source property.

    Invoking the image loading code in a Dispatcher action solves the issue:

    void setImageSource(string file)
    {
        Dispatcher.Invoke(new Action(() =>
        {
            using (var stream = new FileStream(
                                    file, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                ssPreview.Source = BitmapFrame.Create(
                    stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
            }
        }));
    }