Wpf图像显示
今天聊一聊wpf使用原生图像控件显示图像的那些事,作者也是找了很多资料,踩了很多坑,总结了以下几种方式。
前端样式
1. 图像背景小方格
图像背景方格是使用DrawingBrush
来实现的,这样界面放大和缩小,方格块的大小不会变化,如果不想使用图像背景方格,用纯色甚至不设置背景都是可以的。
<ResourceDictionary>
<!--图像背景的小格子-->
<DrawingBrush x:Key="ImageBackgroundIcon" Viewport="0,0,25,25" ViewportUnits="Absolute" TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#D8D8D8">
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="0,0,100,100"/>
<RectangleGeometry Rect="100,100,100,100" />
</GeometryGroup>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</ResourceDictionary>
补充:DrawingBrush
可以在控件的背景图上画线、画几何图形等,甚至Button
的背景图片也可以使用DrawingBrush
来实现
2.布局
<Grid Background="#B0B0B0">
<Border BorderBrush="{StaticResource ImageBackgroundIcon}" BorderThickness="3000"></Border>
<Image Margin="10" Source="{Binding ImageProperty}" Stretch="Uniform" ></Image>
</Grid>
3.效果
后端代码
采用MVVM的框架,Image
的Source
绑定到ViewModel
里的WriteableBitmap
类型
然后将本地图像根据路径转为WriteableBitmap
类型,并通知界面显示。下面介绍如何将本地图像转为WriteableBitmap
类型,主要介绍三种方法
方式一:
//方式一:
private WriteableBitmap GenImage1(string imagePath)
{
//此方式对小图比较友好,大图加载比较慢
Bitmap bitmap = new Bitmap(imagePath);
WriteableBitmap wb = new WriteableBitmap(bitmap.Width, bitmap.Height, 96, 96, PixelFormats.Bgr24, null);
wb.Lock();
int width = wb.PixelWidth;
int height = wb.PixelHeight;
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
System.Drawing.Color pixel = bitmap.GetPixel(x, y);
byte[] colorData = { pixel.B, pixel.G, pixel.R}; // B G R
Int32Rect rect = new Int32Rect(x, y, 1, 1);
int stride = wb.BackBufferStride;//单行像素数据中的字节数
wb.WritePixels(rect, colorData, stride, 0);
}
}
wb.Unlock();
return wb;
}
方式二:
//方式二:
private WriteableBitmap GenImage2(string imagePath)
{
//
Bitmap bitmap = new Bitmap(imagePath);
MemoryStream stream = new MemoryStream();
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
byte[] bytes = stream.GetBuffer(); //byte[] bytes= ms.ToArray();
stream.Close();
//
WriteableBitmap wb = new WriteableBitmap(bitmap.Width, bitmap.Height, 96, 96, PixelFormats.Bgr24, null);
unsafe
{
wb.Lock();
Marshal.Copy(bytes, 0, wb.BackBuffer, bitmap.Width * bitmap.Height * 3);
wb.AddDirtyRect(new Int32Rect(0, 0, bitmap.Width, bitmap.Height));
wb.Unlock();
}
return wb;
}
说明:
stream.GetBuffer
这种方式获得的图像数据上下颠倒的,显示的时候也是上下颠倒的,但是不影响使用,上下颠倒原因如下:
- BMP图像文件中,图像数据是从最下面一行从左到右存放,然后是倒数第二行,一直到最上面一行。
- 《Windows 图形编程》这本书里有解释原因,当时的设计者倾向于笛卡尔坐标系作为图像坐标系。
也可以使用自己的图像数据结构,这种方式显示的图像不会上下颠倒,我将图像的数据结构也放在下面,但是根据图像路径生成MyImage
的方法要自己定义
//方式二:
private WriteableBitmap GenImage2(string imagePath)
{
//
MyImage img = new MyImage(imagePath);
WriteableBitmap wb = new WriteableBitmap(img.Width, img.Height, 96, 96, PixelFormats.Bgr24, null);
unsafe
{
fixed (byte* pdata = img.Datas)
{
wb.Lock();
Marshal.Copy(img.Datas, 0, wb.BackBuffer, img.Width * img.Height * 3);
wb.AddDirtyRect(new Int32Rect(0, 0, img.Width, img.Height));
wb.Unlock();
}
}
return wb;
}
//图像数据结构
public class MyImage
{
public int Width;
public int Height;
public byte[] Datas;
}
方式三:
//方式三:
private WriteableBitmap GenImage3(string imagePath)
{
Bitmap bitmap= new Bitmap(imagePath);
// 将Bitmap转换为MemoryStream
System.IO.MemoryStream stream = new System.IO.MemoryStream();
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
stream.Position = 0;
// 创建WriteableBitmap
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = stream;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
// 将BitmapImage转换为WriteableBitmap
WriteableBitmap writeableBitmap = new WriteableBitmap(bitmapImage);
// 返回WriteableBitmap
return writeableBitmap;
}