How to Take a Screenshot of a Wpf Control

How to take a screenshot of a WPF control?

A screenshot is a shot of the screen... everything on the screen. What you want is to save an image from a single UIElement and you can do that using the RenderTargetBitmap.Render Method. This method takes a Visual input parameter and luckily, that is one of the base classes for all UIElements. So assuming that you want to save a .png file, you could do this:

RenderTargetBitmap renderTargetBitmap = 
new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(yourMapControl);
PngBitmapEncoder pngImage = new PngBitmapEncoder();
pngImage.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
using (Stream fileStream = File.Create(filePath))
{
pngImage.Save(fileStream);
}

Saving image (screenshot) of control in WPF - MVVM Pattern

Trick with CommandParameter can let you pass some FrameworkElement to your command placed in your ViewModel. My example

xaml (you can refine if you need only your control in CommandParameter)

<Window x:Class="WpfTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfTestApp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
d:DataContext="{d:DesignInstance Type=local:YourViewModel, IsDesignTimeCreatable=True}">
<Grid>
<Button Content="Make screenShot" Command="{Binding MakeScreenShotCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>
</Grid>

ViewModel

public class YourViewModel
{
private ICommand _makeScreenShotCommand;
public ICommand MakeScreenShotCommand => _makeScreenShotCommand ?? (_makeScreenShotCommand = new ActionCommand(TakeScreenShot));

private void TakeScreenShot(object control)
{
FrameworkElement element = control as FrameworkElement;
if (element == null)
throw new Exception("Invalid parameter");

RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap((int)element.ActualWidth, (int)element.ActualHeight, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(element);
PngBitmapEncoder pngImage = new PngBitmapEncoder();
pngImage.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
using (Stream fileStream = File.Create("filePath"))
{
pngImage.Save(fileStream);
}
}
}

WPF: Take a screenshot and save it

In WPF project, you have to manually add reference to System.Drawing.dll library. To do so, Project > Add reference > Assembly tab (Framework) > check the desired library.

Moreover your code needs just a bit of tweaking, but the idea it's correct.

        double screenLeft = SystemParameters.VirtualScreenLeft;
double screenTop = SystemParameters.VirtualScreenTop;
double screenWidth = SystemParameters.VirtualScreenWidth;
double screenHeight = SystemParameters.VirtualScreenHeight;

using (Bitmap bmp = new Bitmap((int)screenWidth,
(int)screenHeight))
{
using (Graphics g = Graphics.FromImage(bmp))
{
String filename = "ScreenCapture-" + DateTime.Now.ToString("ddMMyyyy-hhmmss") + ".png";
Opacity = .0;
g.CopyFromScreen((int)screenLeft, (int)screenTop, 0, 0, bmp.Size);
bmp.Save("C:\\Screenshots\\" + filename);
Opacity = 1;
}

}

C# Take screenshot of WPF application

This is how I have used in my application earlier.

I created class to handle screenshot functionality.

public sealed class snapshotHandler
{
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int m_left;
public int m_top;
public int m_right;
public int m_bottom;
}

[DllImport("user32.dll")]
private static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);

public static void Savesnapshot(IntPtr handle_)
{
RECT windowRect = new RECT();
GetWindowRect(handle_, ref windowRect);

Int32 width = windowRect.m_right - windowRect.m_left;
Int32 height = windowRect.m_bottom - windowRect.m_top;
Point topLeft = new Point(windowRect.m_left, windowRect.m_top);

Bitmap b = new Bitmap(width, height);
Graphics g = Graphics.FromImage(b);
g.CopyFromScreen(topLeft, new Point(0, 0), new Size(width, height));
b.Save(SNAPSHOT_FILENAME, ImageFormat.Jpeg);
}
}

To use above functionality, I am calling SaveSnapshot method.

SnapshotHandler.SaveSnapshot(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle);

Create a screenshot of a WPF control in a background thread

It is possible, the bit you are missing is possibly the measure and arrange of the control:

    public MainWindow()
{
InitializeComponent();

var thread = new Thread(CreateScreenshot);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}

private void CreateScreenshot()
{
Canvas c = new Canvas { Width = 100, Height = 100 };
c.Children.Add(new Rectangle { Height = 100, Width = 100, Fill = new SolidColorBrush(Colors.Red) });

var bitmap = new RenderTargetBitmap((int)c.Width, (int)c.Height, 120, 120, PixelFormats.Default);
c.Measure(new Size((int)c.Width, (int)c.Height));
c.Arrange(new Rect(new Size((int)c.ActualWidth, (int)c.ActualHeight)));
bitmap.Render(c);

var png = new PngBitmapEncoder();
png.Frames.Add(BitmapFrame.Create(bitmap));

using (Stream stm = File.Create("c:\\temp\\test.png"))
{
png.Save(stm);
}

}


Related Topics



Leave a reply



Submit