It is a bit late but maybe someone can use my solution.
My Screenshot class is based on the following solutions:
I use tuples to to return multiple parameters C# 7.0 Tuples.
So here is my class:
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
public class Screenshot
{
//UIElement to create screenshot
private UIElement _element;
//Bounds for the screenshot
private Rect _screenshotBounds;
//Path for Screenshot
private string _path;
private const int DPI = 384;
private const double BASEDPI = 96;
private const double DPISCALE = DPI / BASISDPI;
public Screenshot(UIElement element, string path)
{
this._element = element;
this._screenshotBounds = this.createBounds(this._element);
this._path = path;
}
//public interface to create the screenshot
public void createScreenshot()
{
if (this._element == null)
{
return;
}
//Create a list of tuples with the elements to render in the screenshot
List<(UIElement, Rect, Point)> listElements = new List<(UIElement, Rect, Point)>
{
//Fist element in the list should be the actual UIElement
this.createElementBoundPosition(this._element);
};
RenderTargetBitmap renderBitMap = this.createBitMap(this._screenshotBounds);
//Get the opened Popups, create a list of tuples for the Popups and add them to the list of elements to render
listElements.AddRange(this.createListPopUpBoundsPosition( this.getOpenPopups()));
DrawingVisual drawingVisual = this.createDrawingVisual(listElements);
renderBitMap.Render(drawingVisual);
this.saveRTBAsPNG(renderBitMap);
}
//Create DrawingVisual based on List of Tuples
private DrawingVisual createDrawingVisual(List<(UIElement, Rect, Point)> listElements)
{
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext context = drawingVisual.RenderOpen())
{
foreach((UIElement element, Rect bounds, Point position) in listElements)
{
VisualBrush visualBrush = new VisualBrush(element);
context.DrawRectangle(visualBrush, null, new Rect(position, bounds.Size));
}
}
return drawingVisual;
}
//Save RenderTargetBitmap to file
private void saveRTBAsPNG(RenderTargetBitmap bitmap)
{
PngBitmapEncoder pngBitmapEncoder = new PngBitmapEncoder()
{
Interlace = PngInterlaceOption.On
}
pngBitmapEncoder.Frames.Add(BitmapFrame.Create(bitmap));
using (FileStream fileStream = File.Create(this._path))
{
pngBitmapEncoder.Save(fileStream);
}
}
//Create Bounds for Element
private Rect createBounds(UIElement element)
{
new Rect(new Size((int)element.RenderSize.Width, (int)element.RenderSize.Height));
}
//Create a Tuple with the Element, its bounds and its position
private (UIElement element, Rect bounds, Point position) createElementBoundPosition(UIElement element)
{
return (element, this.createBounds(element), element.PointToScreen(new Point(0,0)));
}
//create the RenderTargetBitmap
private RenderTargetBitmap createBitMap(Rect bounds)
{
(int width, int height) calculatedBounds = this.calculateBounds(bounds);
return new RenderTargetBitmap(calculatedBounds.width, calculatedBounds.height, DPI, DPI, PixelFormats.Pbgra32);
}
//recalculate bounds according to the scale
private (int width, int heigth) calculateBounds(Rect bounds)
{
int width = (int)(bounds.Width * DPISCALE);
int height = (int)(bounds.Height * DPISCALE);
return (width, height);
}
//Convert the list of Popups into a List of Tuples
private List<(UIElement element, Rect bounds, Point position)> createListPopUpBoundsPosition(List<Popup> listPopup)
{
List<(UIElement, Rect, Point)> list = new List<(UIElement, Rect, Point)>();
foreach (Popup p in listPopup)
{
//The Child-Element contains the UIElement to render
UIElement uiElement = p.Child;
list.Add(this.createElementBoundPosition(uiElement));
}
return list;
}
//get the open Popups
private List<Popup> getOpenPopups()
{
return PresentationSource.CurrentSources.OfType<HwndSource>()
.Select(h => h.RootVisual)
.OfType<FrameworkElement>()
.Select(f => f.Parent)
.OfType<Popup>()
.Where(p => p.IsOpen).ToList();
}
}