Создание изображений из элементов управления WPF

Существует достаточно распространенная задача, когда окно приложения или его часть необходимо сохранить как изображение. В WPF есть класс, который очень упрощает ее решение – RenderTargetBitmap. Он позволяет получить изображение любого элемента управления WPF (включая его дочерние элементы) в растровом формате. Рассмотрим пример использования данного класса и некоторые особенности.

Сразу перейдем к коду примера. После я дам краткое его описание и расскажу про пару особенностей.

/// <summary>Renders the Visual object and store it to file.</summary>
/// <param name="baseElement">The Visual object to be used as a bitmap. </param>
/// <param name="imageWidth">The height of the bitmap.</param>
/// <param name="imageHeight">The width of the bitmap.</param>
/// <param name="pathToOutputFile">Full path to the output file.</param>
private void SaveControlImage(Visual baseElement, 
        int imageWidth, int imageHeight, string pathToOutputFile)
{
    // 1) get current dpi
    PresentationSource pSource = PresentationSource.FromVisual(Application.Current.MainWindow);
    Matrix m = pSource.CompositionTarget.TransformToDevice;
    double dpiX = m.M11 * 96;
    double dpiY = m.M22 * 96;

    // 2) create RenderTargetBitmap
    RenderTargetBitmap elementBitmap = 
                new RenderTargetBitmap(imageWidth, imageHeight, dpiX, dpiY, PixelFormats.Default);

    // 3) undo element transformation
    DrawingVisual drawingVisual = new DrawingVisual();
    using (DrawingContext drawingContext = drawingVisual.RenderOpen()) {
        VisualBrush visualBrush = new VisualBrush(baseElement);
        drawingContext.DrawRectangle(visualBrush, null,
            new Rect(new Point(0, 0), new Size(imageWidth, imageHeight)));
    }

    // 4) draw element
    elementBitmap.Render(drawingVisual);

    // 5) create PNG image
    BitmapEncoder encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(elementBitmap));

    // 6) save image to file
    using (FileStream imageFile = 
                new FileStream(pathToOutputFile, FileMode.Create, FileAccess.Write)) {
        encoder.Save(imageFile);
        imageFile.Flush();
        imageFile.Close();
    }
}

Код достаточно простой, но все же дам краткие комментарии:

  • Шаг 1 - Получаем DPI для монитора.
  • Шаг 2 – Создаем экземпляр класса RenderTargetBitmap
  • Шаг 3 – Сдвигаем элемент к левому верхнему углу изображения (т.к. для него скорее всего задано смещение).
  • Шаг 4 – Переводим элемент в растровое изображение. Тут есть интересный момент с фоном элемента, который мы рассмотрим далее.
  • Шаг 5 – Переводим растровое изображение в PNG формат. В WPF доступны следующие кодировщики: BmpBitmapEncoder, GifBitmapEncoder, JpegBitmapEncoder, PngBitmapEncoder, TiffBitmapEncoder, WmpBitmapEncoder.
  • Шаг 6 – Создаем поток в памяти и записываем туда данные для дальнейшего PNG файла.

Пример сохраненного изображенияРезультатом данного кода будет сохраненный на диске файл с изображением в PNG формате. Так же класс RenderTargetBitmap позволяет получить массив пикселей изображения для дальнейшей обработки. Все просто, но есть два интересных момента:

  • Смещенное изображение элемента WPFПервый заключается в шаге 3. Если не убрать сдвиги элемента, то сохраняемые элементы будут так же смещены и на изображении. Например, как на рисунке справа (я специально добавил серый фон, чтобы было лучше заметно).
  • Второй момент в прозрачности фона многих элементов управления. Сохраненное изображение WPF GridГенерируемое изображение будет содержать пиксели в формате с альфа-каналом (например PixelFormat.Pbgra32). При сохранении в формат, без поддержки прозрачности необходимо будет задать фон. В приложенном примере я несколько схитрил и задал белый фон для Grid. Поэтому его копии можно сохранять в любом другом формате (можете попробовать, заменив PngBitmapEncoder на любой другой из отмеченных выше).

Ну дополнительно - пример кода печати элемента WPF. Это очень просто:

PrintDialog dlgPrint = new PrintDialog();
// Print
if (dlgPrint.ShowDialog() == true) {
    dlgPrint.PrintVisual(element, "The picture is drawn dynamically");
}

Осталось дать ссылку на демонстрационный проект для Visual Studio 2010:
RenderTargetBitmapDemo.zip (15.90 kb).

Комментарии (6) -

Спасибо вам огромное. Я очень долго мучилась, пытаясь найти способ распечатать содержимое моих юзерконтролов, пока не нашла наконец вашу статью.

@ Юлия: Пожалуйста.

Максим 23.09.2013 13:23:10

Пример использует монитора устройства, а у многих он переопределен в настройках(например у меня) и скрин снимается неправильно.

@ Максим: А что понимается под "переопределен в настройках"?

Максим 24.09.2013 22:45:01

Sorry, это касалось вот этого кода:http://habrahabr.ru/post/94292/ Просто я был очень запутанный.

Спасибо! Заработало. Два дня бодалась с сохранением Chart из  WPFtoolkit в изображение

Добавить комментарий