在新的sprint中发现了个问题,我们需要实现一个文件缩略图的浏览器,显示添加的图片文件的缩略图,并且显示到应用程序中,显示缩略图很简单,不过本着精益求精(我自己都觉得自己是在装b)的原则,需要完成下列需求:
(1) 图片加载完毕之后不能够再使用图片文件;
(2) 图片加载完毕之后不能够占用大量内存;
(3) 图片需要异步加载,在图片过多时尽可能快的让用户能够看到部分图片的缩略图
这三点功能对于我这种初学者来说有些困难,在网上差了很多老外的文章之后,发现了解决方案,具体思路:
(1) 在加载完毕图片之后需要将对原有图片的引用与实例进行Dispose,这样能够解决需求中的(1)于(2);
(2) 自己集成一个Image,这样能够自定义一个依赖项属性,实现对ViewModel中的数据的绑定,来实现对图片缩略图的异步加载
加载图片之后将图片的引用Dispose:
/// <summary> /// Set Image.Source. /// </summary> /// <param name="image"></param> /// <param name="fileName"></param> private static void SetImageSource(System.Windows.Controls.Image image, string fileName) { if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName)) { System.Drawing.Image sourceImage = System.Drawing.Image.FromFile(fileName); int imageWidth = 0, imageHeight = 0; InitializeImageSize(sourceImage, image, out imageWidth, out imageHeight); // From: http://stackoverflow.com/questions/3923697/is-it-possible-to-modify-a-wpf-bitmapsource-in-memory-unsafely-from-another-thr Bitmap sourceBmp = new Bitmap(sourceImage, imageWidth, imageHeight); IntPtr hBitmap = sourceBmp.GetHbitmap(); BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); bitmapSource.Freeze(); WriteableBitmap writeableBmp = new WriteableBitmap(bitmapSource); sourceImage.Dispose(); sourceBmp.Dispose(); image.Source = writeableBmp; } } /// <summary> /// Initialize ImageSize. /// </summary> /// <param name="sourceImage"></param> /// <param name="image"></param> /// <param name="imageWidth"></param> /// <param name="imageHeight"></param> private static void InitializeImageSize(System.Drawing.Image sourceImage, System.Windows.Controls.Image image, out int imageWidth, out int imageHeight) { int width = sourceImage.Width; int height = sourceImage.Height; float aspect = (float)width / (float)height; if (image.Height != double.NaN) { imageHeight = Convert.ToInt32(image.Height); imageWidth = Convert.ToInt32(aspect * imageHeight); } else if (image.Width != double.NaN) { imageWidth = Convert.ToInt32(image.Width); imageHeight = Convert.ToInt32(image.Width / aspect); } else { imageHeight = 100; imageWidth = Convert.ToInt32(aspect * imageHeight); } }
自定义Image用于异步加载:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Controls; using System.Windows; using System.IO; using System.Drawing; using System.Windows.Interop; using System.Windows.Media.Imaging; using System.Windows.Threading; namespace ThrumbsViewer { /// <summary> /// Thrumb Image. /// </summary> public class ThrumbImage : System.Windows.Controls.Image { #region ... Variables ... /// <summary> /// Set ImageSource Handler. /// </summary> private static SetImageSourceHandler setImageSourceHandler; #endregion ... Variables ... #region ... Properties ... /// <summary> /// Gets or sets ThrumbImageSource. /// </summary> public string ThrumbImageSource { get; set; } /// <summary> /// ThrumbImageSourceProperty. /// </summary> public static DependencyProperty ThrumbImageSourceProperty = DependencyProperty.Register("ThrumbImageSource", typeof(string), typeof(System.Windows.Controls.Image), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnThrumbImageSourcePropertyChanged))); #endregion ... Properties ... #region ... Methods ... /// <summary> /// OnThrumbImageSourcePropertyChanged. /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private static void OnThrumbImageSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) { if (sender is System.Windows.Controls.Image && args.NewValue is string) { System.Windows.Controls.Image image = sender as System.Windows.Controls.Image; string fileName = args.NewValue as string; setImageSourceHandler = new SetImageSourceHandler(SetImageSource); image.Dispatcher.BeginInvoke(setImageSourceHandler, DispatcherPriority.ApplicationIdle, new object[] { image, fileName }); } } /// <summary> /// Set Image.Source. /// </summary> /// <param name="image"></param> /// <param name="fileName"></param> private static void SetImageSource(System.Windows.Controls.Image image, string fileName) { if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName)) { System.Drawing.Image sourceImage = System.Drawing.Image.FromFile(fileName); int imageWidth = 0, imageHeight = 0; InitializeImageSize(sourceImage, image, out imageWidth, out imageHeight); // From: http://stackoverflow.com/questions/3923697/is-it-possible-to-modify-a-wpf-bitmapsource-in-memory-unsafely-from-another-thr Bitmap sourceBmp = new Bitmap(sourceImage, imageWidth, imageHeight); IntPtr hBitmap = sourceBmp.GetHbitmap(); BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); bitmapSource.Freeze(); WriteableBitmap writeableBmp = new WriteableBitmap(bitmapSource); sourceImage.Dispose(); sourceBmp.Dispose(); image.Source = writeableBmp; } } /// <summary> /// Initialize ImageSize. /// </summary> /// <param name="sourceImage"></param> /// <param name="image"></param> /// <param name="imageWidth"></param> /// <param name="imageHeight"></param> private static void InitializeImageSize(System.Drawing.Image sourceImage, System.Windows.Controls.Image image, out int imageWidth, out int imageHeight) { int width = sourceImage.Width; int height = sourceImage.Height; float aspect = (float)width / (float)height; if (image.Height != double.NaN) { imageHeight = Convert.ToInt32(image.Height); imageWidth = Convert.ToInt32(aspect * imageHeight); } else if (image.Width != double.NaN) { imageWidth = Convert.ToInt32(image.Width); imageHeight = Convert.ToInt32(image.Width / aspect); } else { imageHeight = 100; imageWidth = Convert.ToInt32(aspect * imageHeight); } } #endregion ... Methods ... } /// <summary> /// Set ImageSource Handler. /// </summary> /// <param name="image"></param> /// <param name="fileName"></param> public delegate void SetImageSourceHandler(System.Windows.Controls.Image image, string fileName); }
经过测试,能够将内存降低为资源文件的大小的1/10。不过只是测试代码,希望您能够对大家有所帮助,呵呵。
demo: http://download.csdn.net/source/3029745
