前言:整理下wpf image控件显示图片的方式,分为本地图片和内存图片(来自于网络交互中的图片)
一、Image
命名空间:System.Windows.Controls
程序集:PresentationFramework.dll
表示用于显示图像的控件,这个图像一般就是指我们本地文件存储的照片或来自于网络请求中的照片资源。
[System.Windows.Localizability(System.Windows.LocalizationCategory.None, Readability=System.Windows.Readability.Unreadable)]public class Image : System.Windows.FrameworkElement, System.Windows.Markup.IUriContext
继承Object→DispatcherObject→DependencyObject→Visual→UIElement→
FrameworkElement→Image
Image类可用于加载以下图像类型: .bmp、.gif、.ico、.jpg、.png、wdp 和 tiff。
二、 Image赋值-本地图像资源
Image赋值方式有多种,这里分为本地图像资源赋值和内存(网络请求图像资源)
查看Image的属性可以看到,有一个Source属性,其类型为ImageSource类型
//// 摘要:// 获取或设置图像的 System.Windows.Media.ImageSource。//// 返回结果:// 所绘制图像的源。 默认值为 null。public ImageSource Source { get; set; }
导航到ImageSource类,可以看到他在Media名下,不是在Drawing名下,说明是一种多媒体文件格式。因为是一个抽象类,我们不能直接使用他,需要使用他的实现类。同时他有个ToString方法,所以在XAML中经常为Image的Source属性附上一个string(本地图片地址)就可以。
2.1 XAML引用资源
<Grid><Grid.RowDefinitions><RowDefinition Height="*"/><RowDefinition Height="*"/> </Grid.RowDefinitions> <Image Source="{Binding ImgPath}"/><Image Grid.Row="1" Source="pack://application:,,,/Resource/ButtonClick_16x.png"/></Grid>//踩坑:Source这里使用的相对位置,但是如果不加"pack://application:,,,"就不能显示成功,具体的参考这篇博客:/g120/p/4688101.html
//code-behindpublic string ImgPath { get; set; }public MainWindowViewModel(){ImgPath = "pack://application:,,,/Resource/Brush_16x.png";}
2.2 ImageSource方式--用代码引用资源
前面说了,ImageSource是一个抽象类,不能直接使用他,而是使用他的继承类来代替,查阅MSDN如下:
BitmapSource(也是个抽象类)派生自ImageSource,就用它的子类BitmapImage来实现了
//code-behindimage.Source = new BitmapImage(new Uri("imgPath", UriKind.Absolute));
三、Image赋值-内存(网络请求资源)
当图像来自于摄像头或者屏幕截图或者网页图片时,就需要在内存中进行操作。WPF的Image控件的Source属性的类型为ImageSource,只要是继承自ImageSource的对象都可以赋值给Source属性,前面讲过BitmapImage继承自BitmapSource,而BitmapSource又继承自ImageSource,因此为了能够将内存中的Bitmap位图显示在Image控件中,需要将Bitmap转换为ImageSource类型。
转换方式有多种,笔者这里将查询到的三种方式做下介绍。
3.1 Bitmap类定义
命名空间:System.Drawing
程序集:System.Drawing.dll
封装 GDI+ 位图,此位图由图形图像及其属性的像素数据组成。Bitmap是用于处理由像素数据定义的图像的对象。
[System.Visible(true)][System.Serializable]public sealed class Bitmap : System.Drawing.Image
继承 Object→MarshalByRefObject→Image→Bitmap
属性ComVisibleAttributeSerializableAttribute
3.2WriteableBitmap
这种方式不安全,有可能会把内存搞炸,慎用。个人理解是将bitmap中数据导入到WriteableBitmap中,然后再赋值给Image使用。
因为是从bitmap导数据到WriteableBitmap中,因此需要创建一个与Bitmap大小相同,像素格式兼容的WriteableBitmap。
WriteableBitmap wb = new WriteableBitmap(bitmap.Width, bitmap.Height, 0, 0, System.Windows.Media.PixelFormats.Bgra32, null);
然后调用Bitmap的LockBits获取其内部的图像数据,通过WritePixels的方式写入WriteableBitmap,这样即完成了转换。
var data = bitmap.LockBits(new System.Drawing.Rectangle(new System.Drawing.Point(0, 0), bitmap.Size), System.Drawing.Imaging.ImageLockMode.ReadOnly, src.PixelFormat);wb.WritePixels(new Int32Rect(srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height), data.Scan0, data.Height * data.Stride, data.Stride, destinationX, destinationY);bitmap.UnlockBits(data);
//将Bitmap 转换成WriteableBitmap public static WriteableBitmap BitmapToWriteableBitmap(System.Drawing.Bitmap src){var wb = CreateCompatibleWriteableBitmap(src);System.Drawing.Imaging.PixelFormat format = src.PixelFormat;if (wb == null){wb = new WriteableBitmap(src.Width, src.Height, 0, 0, System.Windows.Media.PixelFormats.Bgra32, null);format = System.Drawing.Imaging.PixelFormat.Format32bppArgb;}BitmapCopyToWriteableBitmap(src, wb, new System.Drawing.Rectangle(0, 0, src.Width, src.Height), 0, 0, format);return wb;}//创建尺寸和格式与Bitmap兼容的WriteableBitmappublic static WriteableBitmap CreateCompatibleWriteableBitmap(System.Drawing.Bitmap src){System.Windows.Media.PixelFormat format; switch (src.PixelFormat){case System.Drawing.Imaging.PixelFormat.Format16bppRgb555:format = System.Windows.Media.PixelFormats.Bgr555;break;case System.Drawing.Imaging.PixelFormat.Format16bppRgb565:format = System.Windows.Media.PixelFormats.Bgr565;break;case System.Drawing.Imaging.PixelFormat.Format24bppRgb:format = System.Windows.Media.PixelFormats.Bgr24;break;case System.Drawing.Imaging.PixelFormat.Format32bppRgb:format = System.Windows.Media.PixelFormats.Bgr32;break; case System.Drawing.Imaging.PixelFormat.Format32bppPArgb:format = System.Windows.Media.PixelFormats.Pbgra32;break; case System.Drawing.Imaging.PixelFormat.Format32bppArgb:format = System.Windows.Media.PixelFormats.Bgra32;break;default:return null;}return new WriteableBitmap(src.Width, src.Height, 0, 0, format, null);}//将Bitmap数据写入WriteableBitmap中public static void BitmapCopyToWriteableBitmap(System.Drawing.Bitmap src, WriteableBitmap dst, System.Drawing.Rectangle srcRect, int destinationX, int destinationY, System.Drawing.Imaging.PixelFormat srcPixelFormat){var data = src.LockBits(new System.Drawing.Rectangle(new System.Drawing.Point(0, 0), src.Size), System.Drawing.Imaging.ImageLockMode.ReadOnly, srcPixelFormat);dst.WritePixels(new Int32Rect(srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height), data.Scan0, data.Height * data.Stride, data.Stride, destinationX, destinationY);src.UnlockBits(data);}
3.3Imaging.CreateBitmapSourceFromHBitmap
[DllImport("gdi32.dll", SetLastError = true)]private static extern bool DeleteObject(IntPtr hObject);/// <summary>/// 从bitmap转换成ImageSource/// </summary>/// <param name="icon"></param>/// <returns></returns>public static ImageSource ChangeBitmapToImageSource(Bitmap bitmap){//Bitmap bitmap = icon.ToBitmap();IntPtr hBitmap = bitmap.GetHbitmap();ImageSource wpfBitmap = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap,IntPtr.Zero,Int32Rect.Empty,BitmapSizeOptions.FromEmptyOptions());if (!DeleteObject(hBitmap)){throw new ponentModel.Win32Exception();}//一定要卸载IntPtr,否则内存泄漏很快就没有内存了。网上有人说这个方法有时不能卸载掉,我短时间测试是能够及时卸载掉内存的。需要说明的是:DeleteObject()方法不会影响imgSource和bmp中的数据,也就是DeleteObject()执行过后,imgSource和bmp中的图像数据依然完整地存在。为了能使用DeleteObject方法,需要声明来自于gdi32的外部函数 [DllImport("gdi32")]static extern int DeleteObject(IntPtr o),参考MSDN说明/zh-tw/library/1dz311e4(v=vs.80).aspx?cs-save-lang=1&cs-return wpfBitmap;}
3.4MemoryStream
先将Bitmap数据Save到Memory Stream中,然后再用MemoryStream将其转换为字节数组,再利用MemoryStream来赋值给BitmapImage的StreamSource属性,从而间接将Bitmap对象转换为BitmapImage对象
private BitmapImage BitmapToBitmapImage(System.Drawing.Bitmap bitmap){Bitmap b = new Bitmap(bCode);MemoryStream ms = new MemoryStream();b.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);byte[] bytes = ms.GetBuffer(); //byte[] bytes= ms.ToArray(); 这两句都可以ms.Close();//Convert it to BitmapImageBitmapImage image = new BitmapImage();image.BeginInit();image.StreamSource = new MemoryStream(bytes);image.EndInit();return image ;}
引用文章:C# wpf Bitmap转换成WriteableBitmap(BitmapSource)的方法_Alfred-N的博客-CSDN博客
使用不安全代码将 Bitmap 位图转为 WPF 的 ImageSource 以获得高性能和持续小的内存占用 - walterlvWPF 使用不安全代码快速从数组转 WriteableBitmap