Easiest way of saving wpf Image control to a file
Asked Answered
L

2

15

I have a Image control inside my wpf application, which has a large image inside of it, but the control itself is only 60x150, this means it only shows a certain portion of this image. What is the easiest way of saving the visible portion to a file?

Thank you for your help.

[EDIT] I ended up using code found here (which I've not been able to locate before posting here)...

Grid r = new Grid();
        r.Background = new ImageBrush(image2.Source);


    System.Windows.Size sz = new System.Windows.Size(image2.Source.Width, image2.Source.Height);
    r.Measure(sz);
    r.Arrange(new Rect(sz));

    RenderTargetBitmap rtb = new RenderTargetBitmap((int)image2.Source.Width, (int)image2.Source.Height, 96d, 96d, PixelFormats.Default);
    rtb.Render(r);

    BmpBitmapEncoder encoder = new BmpBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(rtb));

    FileStream fs = File.Open(@"C:\lol.png", FileMode.Create);
    encoder.Save(fs);
    fs.Close();
Lymphangial answered 6/5, 2012 at 13:21 Comment(0)
B
20

You could use RenderTargetBitmap class and BitmapEncoder.

Define these methods:

void SaveToBmp(FrameworkElement visual, string fileName)
{
    var encoder = new BmpBitmapEncoder();
    SaveUsingEncoder(visual, fileName, encoder);
}

void SaveToPng(FrameworkElement visual, string fileName)
{
    var encoder = new PngBitmapEncoder();
    SaveUsingEncoder(visual, fileName, encoder);
}

// and so on for other encoders (if you want)


void SaveUsingEncoder(FrameworkElement visual, string fileName, BitmapEncoder encoder)
{
    RenderTargetBitmap bitmap = new RenderTargetBitmap((int)visual.ActualWidth, (int)visual.ActualHeight, 96, 96, PixelFormats.Pbgra32);
    bitmap.Render(visual);
    BitmapFrame frame = BitmapFrame.Create(bitmap);
    encoder.Frames.Add(frame);

    using (var stream = File.Create(fileName))
    {
        encoder.Save(stream);
    }
}

If you have your Image control inside a container like this:

<Grid x:Name="MyGrid">
    <Image Name="MyImage" Stretch="None"></Image>
</Grid>

You just need to do so:

SaveToPng(MyGrid, "image.png");

Otherwise you can simply pass the dimensions you want when you use RenderTargetBitmap:

SaveToPng(MyImage, "image.png");

...

RenderTargetBitmap bitmap = new RenderTargetBitmap(YourWidth, YourHeight, 96, 96, PixelFormats.Pbgra32);
Barquisimeto answered 6/5, 2012 at 14:12 Comment(6)
Thanks for this, it would be fantastic if not for one small issue.. The saved image is pitch black... Any ideas how that happened or how to fix it?Lymphangial
I've done a test project in VB.NET: here it is a sample if it can help; and here the XAML.Barquisimeto
@gilderkite I am assuming by saying "MyImage" you mean the image control within WPF, then yes I have tried thisLymphangial
OK this is not working despite your code not giving errors. I just get a black image as a result. My image is - system.windows.controls.imageLymphangial
@Capt.Morgan, I know this is old but I ran into the same problem and you need to place your rendered control on a parent control with no margins as explained here: weblog.west-wind.com/posts/2007/Sep/10/…Misdoing
Is it possible to save a Control that is created dynamically and is not displayed in the UI? Because when I do that I get a black imageEolithic
L
5

I ran into the same 'black' image issue that other did when using gliderkite's solution. The 'black' image appears to be due to the FrameworkElement's margin causing it to get rendered outside of the captured image. I found the workaround in a comment on Rick Stahl's blog

Specifically, measuring and arranging prior to rendering gives it a chance to adjust itself to the fact there are no margins in the picture. The following is a static class I now re-use for screen captures. This is based of of gliderkite's answer and the information on Rick Stahl's blog.

public static class ScreenCapture
{
    public static void SaveToBmp(FrameworkElement visual, string fileName)
    {
        var encoder = new BmpBitmapEncoder();
        SaveUsingEncoder(visual, fileName, encoder);
    }

    public static void SaveToPng(FrameworkElement visual, string fileName)
    {
        var encoder = new PngBitmapEncoder();
        SaveUsingEncoder(visual, fileName, encoder);
    }

    public static void SaveToJpeg(FrameworkElement visual, string fileName)
    {
        var encoder = new JpegBitmapEncoder();
        SaveUsingEncoder(visual, fileName, encoder);
    }

    private static void SaveUsingEncoder(FrameworkElement visual, string fileName, BitmapEncoder encoder)
    {
        RenderTargetBitmap bitmap = new RenderTargetBitmap((int)visual.ActualWidth, (int)visual.ActualHeight, 96, 96, PixelFormats.Pbgra32);
        Size visualSize = new Size(visual.ActualWidth, visual.ActualHeight);
        visual.Measure(visualSize);
        visual.Arrange(new Rect(visualSize));
        bitmap.Render(visual);
        BitmapFrame frame = BitmapFrame.Create(bitmap);
        encoder.Frames.Add(frame);

        using (var stream = File.Create(fileName))
        {
            encoder.Save(stream);
        }
    }
}
Lawabiding answered 23/11, 2016 at 15:35 Comment(2)
I tried other methods, but my width and height on the rendered image were always too small, meaning I had content cutting off on the bottom and right of the image. With this method, though, I get the exact dimensions that I'm expecting. Dunno why...Psychomancy
The element I wanted to save as an Image was in a ScrollViewer and super large, with the other methods above, the image quality was not near enough and the Textblocks were unreadable. Yours fixed the issue. thanks a lot. (I think it has to do with your way of getting the size of the visual.)Ermine

© 2022 - 2024 — McMap. All rights reserved.