RenderTargetBitmap removes downgrades a RichtextBox\'s TextRenderingMode to GreyScale. So a captured PNG looks poor quality and doesnt match the WPF control
If I use
You can use the same technique to render your window into the bitmap as windows using when taking a screen shot with Alt + Print Screen. The idea is to get a window handle and then render it to a bitmap using BitBlt system call.
Below is an example (you would need to reference the System.Drawing.dll assembly in your project to make it work):
public static void SaveToBitmapNative(Window window, FrameworkElement element, string fileName)
{
WindowInteropHelper helper = new WindowInteropHelper(window);
// detect the window client area position if rendering a child element
double incX = 0, incY = 0;
if (window != element)
{
System.Drawing.Point pos = new System.Drawing.Point(0, 0);
ClientToScreen(helper.Handle, ref pos);
incX = pos.X - (int)window.Left;
incY = pos.Y - (int)window.Top;
}
// transform child position to window coordinates
GeneralTransform transform = element.TransformToVisual(window);
Point point = transform.Transform(new Point(0, 0));
Rect rect = new Rect(point.X + incX, point.Y + incY, element.ActualWidth, element.ActualHeight);
// render window into bitmap
using (System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(
(int)rect.Width, (int)rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (System.Drawing.Graphics memoryGraphics = System.Drawing.Graphics.FromImage(bitmap))
{
IntPtr dc = memoryGraphics.GetHdc();
IntPtr windowDC = GetWindowDC(helper.Handle);
BitBlt(dc, 0, 0, (int)rect.Width, (int)rect.Height,
windowDC, (int)rect.Left, (int)rect.Top, TernaryRasterOperations.SRCCOPY);
memoryGraphics.ReleaseHdc(dc);
ReleaseDC(helper.Handle, windowDC);
}
// save bitmap to file
bitmap.Save(fileName);
}
}
[DllImport("gdi32.dll")]
static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, TernaryRasterOperations rop);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr ptr);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDc);
[DllImport("user32.dll")]
static extern bool ClientToScreen(IntPtr hWnd, ref System.Drawing.Point lpPoint);
public enum TernaryRasterOperations : uint
{
SRCCOPY = 0x00CC0020,
SRCPAINT = 0x00EE0086,
SRCAND = 0x008800C6,
SRCINVERT = 0x00660046,
SRCERASE = 0x00440328,
NOTSRCCOPY = 0x00330008,
NOTSRCERASE = 0x001100A6,
MERGECOPY = 0x00C000CA,
MERGEPAINT = 0x00BB0226,
PATCOPY = 0x00F00021,
PATPAINT = 0x00FB0A09,
PATINVERT = 0x005A0049,
DSTINVERT = 0x00550009,
BLACKNESS = 0x00000042,
WHITENESS = 0x00FF0062
}
here's how you can call this
private void saveButton_Click(object sender, RoutedEventArgs e)
{
// saves the entire window into the bitmap
SaveToBitmapNative(this, this, "c:\\test0.png");
// saves a child control (RichTextBox) into the bitmap
SaveToBitmapNative(this, richTextBox, "c:\\test1.png");
}
Note: this would not work for layered windows updated via UpdateLayeredWindow function.
hope this helps, regards