将WPF视图保存为图像,最好是.png格式。

12

我已经搜索过并理解如何使用BmpBitmapEncoder在WPF中保存图像。我的程序有一个MVVM视图,我想将其保存为图像。是否可以将其设置为BitmapFrame以便我进行编码?如果可以,是否有在线教程?

下面是我要保存的视图。

<Grid>
    <view:OverallView Grid.Row="1"
        Visibility="{Binding IsOverallVisible,Converter={StaticResource B2VConv}}" />
</Grid>

OverallView是一个用户控件。


如果将视图设置为BitmapFrame不可行,那么哪些WPF元素可以被设置为BitmapSource/Frame


2
这篇文章有帮助吗?http://www.grumpydev.com/2009/01/03/taking-wpf-screenshots/ - Matt Hamilton
2个回答

25
你可以将其返回为 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;
}

之后,您可以使用PngBitmapEncoder将其保存为PNG并保存到流中,例如:

public static void SaveAsPng(RenderTargetBitmap src, Stream outputStream)
{
    PngBitmapEncoder encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(src));

    encoder.Save(outputStream);   
}

修复:位图 => 结果


将位图改为结果。除此之外,一切都好。 - Kiang Teng
1
src 参数可以是 BitmapSource,使其更广泛可用。此外,编码器局部变量应该声明为 var,在我看来。 - vidstige
我不知道如何调用这些方法,有人能解释一下吗?谢谢。 - Denis
@Denis,你可以在下面看到我的答案。上面的代码有不必要的逻辑,我已经删除了它。我还将其转换为扩展方法,并提供了示例,以便您了解如何使用它们。 - Mark A. Donohoe

1
赞扬上面的答案,让我找到了解决方案的路径,但实际上这过于复杂化了需求。具体来说,您不需要创建一个 DrawingVisual 传递给 Render 调用。因为视图已经是一个 Visual,而 Render 调用接受 Visual,所以您可以直接传递视图。
下面是一个简化版本,我还将其改成了 FrameworkElement 的扩展方法,这样您就可以轻松地在任何控件中使用它。

注意:虽然 Render 接受 Visual,但您不能扩展 Visual,因为您需要实际的宽度和高度来创建 RenderTargetBitmap,但出于同样的原因,您可能已经在使用 FrameworkElement

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;
}

我有第二个扩展方法,它接受任何BitmapSource(其中包括RenderTargetBitmap),并根据提供的编码器返回字节。
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();
}

下面是如何将它们全部结合使用的方法。这段代码将任何FrameworkElement渲染为PNG:

var renderTargetBitmap = someFrameworkElement.CopyAsBitmap();
var pngData = renderTargetBitmap.Encode(new PngBitmapEncoder());
File.WriteAllBytes(@"C:\Users\SomeUser\Desktop\TestOutput.png", pngData);

更简洁地说...

var pngData = someFrameworkElement.CopyAsBitmap().Encode(new PngBitmapEncoder());
File.WriteAllBytes(@"C:\Users\SomeUser\Desktop\TestOutput.png", pngData);

如果要将其呈现为JPG格式,只需更改编码器,像这样...

var jpegData = someFrameworkElement.CopyAsBitmap().Encode(new JpegBitmapEncoder());
File.WriteAllBytes(@"C:\Users\SomeUser\Desktop\TestOutput.jpg", jpegData);

此外,由于RenderTargetBitmap最终是通过BitmapSource成为ImageSource,因此您也可以直接将其设置为Image控件的Source属性,如下所示...
someImage.Source = someFrameworkElement.CopyAsBitmap();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接