【问题标题】:Loading images into a Grid asynchronously将图像异步加载到网格中
【发布时间】:2017-12-04 10:22:00
【问题描述】:

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

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

我的编码:

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

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

我的问题:

由于图像需要一段时间才能加载,因此我需要在将它们转换并添加到我的网格中时完全控制我的 UI,但我一点也不正确。有人可以帮我提供一些建议或编码 sn-ps 以帮助我前进吗?

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

【问题讨论】:

  • 图像“正在转换”的具体情况如何? “convert”方法是如何定义的?
  • @mm8 我已经更新了我的问题。请看下文。
  • 你没有任何意义。如果您需要任何帮助,请准确显示您如何调用 ConvertHighQualityRAWImage 方法。并且请去掉任何不相关的代码 sn-ps。
  • 很公平,为此我们使用DataTemplates 等,但这不是重点。我们希望看到的是如何获取图片,如何将它们分配给 UI 元素。现在请单线程。
  • @XAMlMAX 我更新了我的代码 :) 我将在我回家后将我的代码发布到我将图像分配给我的 UI 元素的位置。

标签: c# wpf multithreading async-await


【解决方案1】:

最终解决方案:

我只想将此评论添加到 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;
}

【讨论】:

    【解决方案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));
    

    【讨论】:

    • 这工作得更快。非常感谢克莱门斯!
    猜你喜欢
    • 2012-04-04
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多