异步将图像加载到网格中

时间:2017-06-30 12:42:25

标签: c# wpf multithreading async-await

我正在开发一个小应用程序,它将获取RAW图像文件,将它们转换为低质量JPEG,然后将这些JPEG作为缩略图加载到网格中。

我的问题:我在转换图片时遇到UI被阻止的问题。我正在动态添加控件,以便在每个图像进行转换后在网格中托管这些图像。此外,我将这些图片绑定到我的Image控件的Source,并在代码隐藏中使用ControlProperties ViewModel。

我的编码:

在这里,我正在创建ControlProperties视图模型的新实例,我正在ImageSource内进行图像转换。

cp = new ControlProperties()
{
    ImageId = controlCount += 1, ImageSource = ThumbnailCreator.CreateThumbnail(imagePath)
};

我的问题:

看到图像需要一段时间才能加载,我需要完全控制我的UI,同时将它们转换并添加到我的网格中,但我根本没有把它弄好。有人可以帮我提一些建议或编码片段让我一起去吗?

我的ThumbnailCreator班级

using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;

namespace SomeProjName
{
    public class ThumbnailCreator
    {
        private static string imageLocation;
        private static int currentImage;
        public static BitmapImage CreateThumbnail(string oldImagePath)
        {
            ConvertHighQualityRAWImage(oldImagePath);

            if (imageLocation != string.Empty && imageLocation != null)
                return OpenImage(imageLocation);
            else return null;
        }

        //Creates low quality JPG image from RAW image
        private static void ConvertHighQualityRAWImage(string oldImagePath)
        {

            BitmapImage image = new BitmapImage(new Uri(oldImagePath));
            var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
            encoder.Frames.Add(BitmapFrame.Create(image));

            using (var filestream = new FileStream(GetImageLocation(), FileMode.Create))
                encoder.Save(filestream);

            image.UriSource = null;
            image.StreamSource = null;
            image = null;

            GC.WaitForPendingFinalizers();
            GC.Collect();
        }

        //Returns low quality JPG thumbnail to calling method
        private static BitmapImage OpenImage(string imagePath)
        {
            BitmapImage image = new BitmapImage();
            image.BeginInit();
            image.DecodePixelWidth = 283;
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.UriSource = new Uri(imagePath, UriKind.Relative);
            image.EndInit();

            DeleteImage();
            return image;
        }

        private static string GetImageLocation()
        { 
            imageLocation = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "thumbnails")).FullName + GetCurrentImage();
            return imageLocation;
        }

        private static string GetCurrentImage()
        {
            return "\\" + (currentImage += 1).ToString() + ".jpg";
        }

        private static void DeleteImage()
        {
            if (File.Exists(imageLocation))
                File.Delete(imageLocation);
        }
    }
}

2 个答案:

答案 0 :(得分:2)

您不需要将缩略图保存到文件中。改为使用MemoryStream

public class ThumbnailCreator
{
    public static BitmapImage CreateThumbnail(string imagePath)
    {
        BitmapFrame source;

        using (var stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read))
        {
            source = BitmapFrame.Create(
                stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
        }

        var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
        encoder.Frames.Add(BitmapFrame.Create(source));

        var bitmap = new BitmapImage();

        using (var stream = new MemoryStream())
        {
            encoder.Save(stream);
            stream.Position = 0;

            bitmap.BeginInit();
            bitmap.DecodePixelWidth = 283;
            bitmap.CacheOption = BitmapCacheOption.OnLoad;
            bitmap.StreamSource = stream;
            bitmap.EndInit();
        }

        bitmap.Freeze();
        return bitmap;
    }

中间编码和解码通道似乎没有必要,所以你可以简单地写一下:

public class ThumbnailCreator
{
    public static BitmapImage CreateThumbnail(string imagePath)
    {
        var bitmap = new BitmapImage();

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

        bitmap.Freeze();
        return bitmap;
    }
}

如果要异步调用CreateThumbnail方法,请使用Task.Run()

cp.ImageSource = await Task.Run(() => ThumbnailCreator.CreateThumbnail(fileName));

答案 1 :(得分:0)

最终解决方案:

我只想将此评论添加到Clemens解决方案中。我还使用垃圾收集器在加载大量图像时停止大量内存使用累积。这是我用来创建缩略图的最终方法。

public static BitmapImage CreateThumbnail(string imagePath)
{
    var bitmap = new BitmapImage();

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

    bitmap.Freeze();

    GC.WaitForPendingFinalizers();
    GC.Collect();

    return bitmap;
}