WPF中将控件保存成图片,主要利用了RenderTargetBitmap方法。但是,通过测试发现这种方法有个问题,即图片是从目标UI上一级的左上角作为起点计算长宽的,也就是说,如果目标UI居中显示,那么极有可能导出的图片是不完整的,居左置顶则能导出完整图片。
为了解决这个问题,我们可以先将包含目标UI在内的部分保存成一张大图,然后从大图中提取目标UI部分,主要利用CroppedBitmap函数。
以下两个函数的即实现了这个功能:第一个函数是将ImageSource转换成Bitmap,第二个函数是将指定的UI保存成图片。
// ImageSource --> Bitmap
public static Bitmap ImageSourceToBitmap(ImageSource imageSource)
{
BitmapSource bitmapSource = (BitmapSource)imageSource;
Bitmap bmp = new Bitmap(bitmapSource.PixelWidth, bitmapSource.PixelHeight, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
BitmapData data = bmp.LockBits(
new Rectangle(System.Drawing.Point.Empty, bmp.Size), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
bitmapSource.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride);
bmp.UnlockBits(data);
return bmp;
}
// UI --> Image
public static void UISaveToImage(FrameworkElement ui, string fileName, int bigX, int BigY, int width, int height)
{
RenderTargetBitmap bmp = new RenderTargetBitmap(bigX, BigY, 96d, 96d, PixelFormats.Pbgra32);
bmp.Render(ui);
int offsetX = Math.Max((int)(0.5 * (bigX - width)), 0);
int offsetY = Math.Max((int)(0.5 * (BigY - height)), 0);
BitmapSource bmpSource = new CroppedBitmap(BitmapFrame.Create(bmp), new Int32Rect(offsetX, offsetY, width, height));
ImageSourceToBitmap(bmpSource).Save(fileName);
}
补充说明:bigX和bigY为目标UI、目标UI上一级中较大宽和较大高;
比如以下代码:
<ScrollViewer Grid.Column="2" Name="svProcess" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" BorderBrush="BlueViolet" BorderThickness="1">
<Image Name="imgProcess" Stretch="Uniform" RenderOptions.BitmapScalingMode="HighQuality" IsHitTestVisible="False"/>
</ScrollViewer>
调用方式:
int offsetX = Math.Max((int)(0.5 * (svProcess.ActualWidth - imgProcess.ActualWidth)), 0);
int offsetY = Math.Max((int)(0.5 * (svProcess.ActualHeight - imgProcess.ActualHeight)), 0);
int bigX = Math.Max((int)svProcess.ActualWidth, (int)imgProcess.ActualWidth);
int bigY = Math.Max((int)svProcess.ActualHeight, (int)imgProcess.ActualHeight);
CImageProcess.UISaveToImage(imgProcess, filename, bigX, bigY, (int)imgProcess.ActualWidth, (int)imgProcess.ActualHeight);