开发者

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 a Visual, you can't extend Visual since you need the actual width and height to create the RenderTargetBitmap, but chances are for that same reason you're probably already using a FrameworkElement 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();
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜