从另一个线程更新图像控件

时间:2016-09-12 11:03:44

标签: c# multithreading user-interface

我想要一个监视ceratain文件夹并显示该文件夹中出现的新图像的应用程序。我成功设法使用FileSystemWatcher类检测新图像,但是我有问题要显示它们。我有以下代码:

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)); }));
        }        
    }

适用于大约10-20个小(~100kB)图像和1-2个大(~4MB)的图像。稍后它会引发一个异常,即图像被另一个进程使用。我还在调试器中观察到,应用程序使用的内存随着每个新图像的增加而急剧增加,就像以前的图像没有被丢弃一样。

创建BitmapImage并在Image控件中显示它的正确方法是什么?

编辑:

我尝试了一个可能重复的建议解决方案,但是这仍然给了我相同的例外,这次甚至没有工作过。修改后的代码下方:

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;
            }
        }

2 个答案:

答案 0 :(得分:0)

我设法找到了一个有效的解决方案:

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;
            }
        }

我的猜测是FileSystemWatcher在检查任何修改时暂时拥有该文件的所有权。等待一段时间后,文件解锁并准备好安全处理。不过,如果有人有更深入的解释或更好的解决方案,我很乐意看到它。

答案 1 :(得分:0)

您可能会遇到此问题,因为您没有处理已创建的位图对象。试试以下解决方案这将在创建新位图时处理您之前的位图。

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; }));
}