Save wpf view as image, preferably .png
I have searched and understand how to save an image in WPF by using BmpBitmapEncoder
. My program has a MVVM view that I want to save as an image. Is it possible to set it as BitmapFrame
so I can encode it? If so, is there an online tutorial?
Listed below 开发者_如何学Cis the view I want to save.
<Grid>
<view:OverallView Grid.Row="1"
Visibility="{Binding IsOverallVisible,Converter={StaticResource B2VConv}}" />
</Grid>
OverallView
is a user control.
If setting a view as a BitmapFrame
is not possible, what wpf elements can be set as a BitmapSource/Frame
?
You can return it as RenderTargetBitmap:
public static RenderTargetBitmap GetImage(OverallView view)
{
Size size = new Size(view.ActualWidth, view.ActualHeight);
if (size.IsEmpty)
return null;
RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
DrawingVisual drawingvisual = new DrawingVisual();
using (DrawingContext context = drawingvisual.RenderOpen())
{
context.DrawRectangle(new VisualBrush(view), null, new Rect(new Point(), size));
context.Close();
}
result.Render(drawingvisual);
return result;
}
After that you can use the PngBitmapEncoder to save it as PNG and save it to stream, e.g.:
public static void SaveAsPng(RenderTargetBitmap src, Stream outputStream)
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(src));
encoder.Save(outputStream);
}
FIX: bitmap => result
Kudos to the accepted answer above for leading me down the path to a solution, but that's actually over-complicating the requirements. Specifically, you don't need to create a DrawingVisual
to pass to the Render
call. You can pass the view directly since it's already a Visual
, which the Render
call accepts.
Here's a simplified version that I've also changed into an extension method on FrameworkElement
so you can use it easily with any control.
Note: Although
Render
accepts aVisual
, you can't extendVisual
since you need the actual width and height to create theRenderTargetBitmap
, but chances are for that same reason you're probably already using aFrameworkElement
anyway.
public static RenderTargetBitmap? CopyAsBitmap(this FrameworkElement frameworkElement) {
var targetWidth = (int)frameworkElement.ActualWidth;
var targetHeight = (int)frameworkElement.ActualHeight;
// Exit if there's no 'area' to render
if (targetWidth == 0 || targetHeight == 0)
return null;
// Prepare the rendering target
var result = new RenderTargetBitmap(targetWidth, targetHeight, 96, 96, PixelFormats.Pbgra32);
// Render the framework element into the target
result.Render(frameworkElement);
return result;
}
I then have a second extension method that takes any BitmapSource
(which RenderTargetBitmap
is a subclass of) and returns the bytes based on the provided encoder.
public static byte[] Encode(this BitmapSource bitmapSource, BitmapEncoder bitmapEncoder){
// Create a 'frame' for the BitmapSource, then add it to the encoder
var bitmapFrame = BitmapFrame.Create(bitmapSource);
bitmapEncoder.Frames.Add(bitmapFrame);
// Prepare a memory stream to receive the encoded data, then 'save' into it
var memoryStream = new MemoryStream();
bitmapEncoder.Save(memoryStream);
// Return the results of the stream as a byte array
return memoryStream.ToArray();
}
And here's how you use it all together. This code renders any FrameworkElement
as a PNG:
var renderTargetBitmap = someFrameworkElement.CopyAsBitmap();
var pngData = renderTargetBitmap.Encode(new PngBitmapEncoder());
File.WriteAllBytes(@"C:\Users\SomeUser\Desktop\TestOutput.png", pngData);
Or more succinctly...
var pngData = someFrameworkElement.CopyAsBitmap().Encode(new PngBitmapEncoder());
File.WriteAllBytes(@"C:\Users\SomeUser\Desktop\TestOutput.png", pngData);
To instead render it as a JPG, just change the encoder, like so...
var jpegData = someFrameworkElement.CopyAsBitmap().Encode(new JpegBitmapEncoder());
File.WriteAllBytes(@"C:\Users\SomeUser\Desktop\TestOutput.jpg", jpegData);
Additionally, since RenderTargetBitmap
is ultimately an ImageSource
(via BitmapSource
), you can also set it as the Source
property of an Image
control directly, like so...
someImage.Source = someFrameworkElement.CopyAsBitmap();
精彩评论