c# - 将图像异步加载到网格中

标签 c# wpf multithreading async-await

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

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

我的编码:

在这里,我正在创建我的 ControlProperties View 模型的一个新实例,并在内部对 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);
        }
    }
}

最佳答案

您不需要将缩略图保存到文件中。使用 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));

关于c# - 将图像异步加载到网格中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44846767/

相关文章:

c# - 如何通过 Json 在 C# 上使用 telegram API?

c# - 位图锁定位和克隆

c - 这个返回值的目的是什么?

java - 为什么我的多线程 Java 程序没有最大化我机器上的所有内核?

c# - 为什么我在浏览器上得到的响应与服务器上得到的响应不同

c# - 字符串插值 C# : Documentation of colon and semicolon functionality

wpf - MVVM Light ViewModelLocator + ResourceDictionaries

c# - 如何根据方法返回启用/禁用 WPF 按钮

c# - 在没有命令的情况下调用 ViewModel 中的方法

c - tcl 多线程就像在 c 中一样,很难用过程执行线程