Самый простой способ сохранить wpf Image control в файл

У меня есть элемент управления Image внутри моего приложения wpf, в котором есть большое изображение внутри него, но сам элемент управления равен только 60x150, это означает, что он отображает только определенную часть этого изображения. Каков самый простой способ сохранить видимую часть файла?

Благодарим вас за помощь.

[EDIT] Я закончил с использованием кода, найденного здесь (который я не смог найти перед публикацией здесь)...

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();

Ответ 1

Вы можете использовать класс RenderTargetBitmap и BitmapEncoder.

Определите эти методы:

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

Если у вас есть Image элемент управления внутри контейнера:

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

Вам просто нужно сделать это:

SaveToPng(MyGrid, "image.png");

В противном случае вы можете просто передать нужные размеры, когда используете RenderTargetBitmap:

SaveToPng(MyImage, "image.png");

...

RenderTargetBitmap bitmap = new RenderTargetBitmap(YourWidth, YourHeight, 96, 96, PixelFormats.Pbgra32);

Ответ 2

Я столкнулся с тем же вопросом "черного" изображения, что другие делали при использовании решения gliderkite. "Черное" изображение, похоже, связано с краем FrameworkElement, заставляя его визуализироваться вне захваченного изображения. Я нашел обходное решение в комментарии в блоге Рика Шталя

В частности, измерение и компоновка до рендеринга дает возможность настроить себя на то, что на картинке нет полей. Ниже приведен статический класс, который я теперь повторно использую для захвата экрана. Это основано на gliderkite ответа и информации о блоге Рика Шталя.

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