Screenshot in WPF (Screen Capture Tool)

Ich hab mal wieder ein kleines Tutorial geschrieben. Diesmal geht es darum, einen Screenshot des Bildschirms in WPF anzuzeigen und diesen anschließend zu speichern.
In Windows Forms hatte man damit kein Problem. Die Klassen lieferten alles was man dafür brauchte. In WPF sieht das anders aus. Um in WPF einen Screenshot zu erstellen muss man auf die dll's des Betriebssystems zurückgreifen, aber dazu später mehr. Natürlich könnte man auch einfach in seiner WPF-Anwendung auf die Forms-Klassen zurückgreifen. Das ganze würde allerdings das Programm unnötig aufblähen und ist zudem unschön, da es unter Umständen zu Namenskonflikten führen kann.

Fangen wir also mit einem Leeren WPF-Projekt an. Dem Projekt muss die Bibliothek System.Drawing hinzugefügt werden. Das ist auch schon alles. Wir können also gleich mit der Oberfläche beginnen. Die XAML-Datei sieht folgendermaßen aus:
<Window x:Class="Screenshot.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="476" Width="614">
    <Grid>
        <TextBox Height="23" HorizontalAlignment="Left" Margin="312,402,0,0" Name="txt_dir" VerticalAlignment="Top" Width="160" />
        <Button Content="Go" Height="23" HorizontalAlignment="Left" Margin="11,401,0,0" Name="btn_go" VerticalAlignment="Top" Width="75" Click="btn_go_Click" />
        <Button Content="Save" Height="23" HorizontalAlignment="Left" Margin="505,402,0,0" Name="btn_save" VerticalAlignment="Top" Width="75" Click="btn_save_Click" />
        <ScrollViewer Height="395" HorizontalAlignment="Left" Name="scrollViewer1" VerticalAlignment="Top" Width="592" HorizontalScrollBarVisibility="Visible">
            <Canvas Margin="0,0,0,0" Name="cnvs" VerticalAlignment="Top">
                <Image Name="img_1" Stretch="Fill" Margin="0,0,0,0" />
            </Canvas>
        </ScrollViewer>
        <Label Content=".jpg" Height="28" HorizontalAlignment="Left" Margin="469,399,0,0" Name="label1" VerticalAlignment="Top" />
    </Grid>
</Window>
Insgesamt ist es nicht viel: Der Bereich um den Screenshot anzuzeigen, ein Button um den Screenshot zu erstellen, ein Textfeld für den Speicherort und ein weiteren Button um den Screenshot zu speichern.
Das hier ist der Programmcode:
// 2010 by beBug
// bebugsblog.blogspot.com

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.IO;

namespace Screenshot
{
 /// 
 /// Interaction logic for MainWindow.xaml
 /// 
 public partial class MainWindow : Window
 {
  #region DllImports
  [DllImport("user32.dll")]
  public static extern IntPtr GetDC(IntPtr ptr);
  [DllImport("user32.dll")]
  public static extern IntPtr GetDesktopWindow();
  [DllImport("user32.dll")]
  public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);
  [DllImport("user32.dll")]
  public static extern int GetSystemMetrics(int abc);
  [DllImport("gdi32")]
  public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
  [DllImport("gdi32")]
  public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth, int nHeight);
  [DllImport("gdi32")]
  public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
  [DllImport("gdi32")]
  public static extern bool BitBlt(IntPtr hDestDC, int X, int Y, int nWidth, int nHeight, IntPtr hSrcDC, int SrcX, int SrcY, int Rop);
  [DllImport("gdi32")]
  public static extern bool DeleteObject(IntPtr handle);
  [DllImport("gdi32")]
  public static extern IntPtr DeleteDC(IntPtr hDC);
  #endregion

  #region Const
  const int SRCCOPY = 0xCC0020;
  const int SCREEN_X = 0;
  const int SCREEN_Y = 1;
  #endregion

  BitmapSource tmp;

  public MainWindow()
  {
   InitializeComponent();
  }

  private void btn_go_Click(object sender, RoutedEventArgs e)
  {
   int height;
   int width;
   IntPtr device;
   IntPtr memory;
   IntPtr Bitmap;

   // Handle auf Device Context und Speicher
   device = GetDC(GetDesktopWindow());
   memory = CreateCompatibleDC(device);

   // Breite und Höhe auslesen
   width = GetSystemMetrics(SCREEN_X);
   height = GetSystemMetrics(SCREEN_Y);

   // Erstelle ein Bild mit den Größen des Desktops
   Bitmap = CreateCompatibleBitmap(device, width, height);

   if (Bitmap != IntPtr.Zero)
   {
    // Das Bild auf den Speicherbereich zeigen lassen
    SelectObject(memory, Bitmap); 
// Screen aus dem Device in den Speicher schreiben
    BitBlt(memory, 0, 0, width, height, device, 0, 0, SRCCOPY);
                
    // Speicherobjekt löschen
    DeleteDC(memory);
    // Device freigeben
    ReleaseDC(GetDesktopWindow(), device);

    // Bildquelle erstellen
    tmp = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(Bitmap,
                                            IntPtr.Zero,
                                            Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());

    // Bildquelle an Bild übergeben
    this.img_1.Source = tmp;

    // Bildgröße Anpassen
    this.img_1.Height = this.img_1.Source.Height;
    this.img_1.Width = this.img_1.Source.Width;
    this.cnvs.Height = this.img_1.Source.Height;
    this.cnvs.Width = this.img_1.Source.Width;

    // Bild löschen und Speicher freigeben
    DeleteObject(Bitmap);
   }
  }

  private void btn_save_Click(object sender, RoutedEventArgs e)
  {
   // Renderbitmap mit den Bildgrößen erstellen
   RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
                                                  (int)img_1.Width,
                                                  (int)img_1.Height,
                                                  96d,
                                                  96d,
                                                  PixelFormats.Pbgra32);
   // Bild rendern
   renderBitmap.Render(img_1);

   //Als jpg speichern
   using (FileStream fileStream = new FileStream(this.txt_dir.Text.ToString() + ".jpg", FileMode.Create))
   {
    JpegBitmapEncoder encoder = new JpegBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
    encoder.QualityLevel = 100;
    encoder.Save(fileStream);
   }
  }
 }
}
Das wars auch schon. Unser Screen-Capture-Programm ist fertig. Zugegeben, der Code sieht etwas aufwendig aus, nur um einen Screenshot zu speichern, aber letztendlich ist er nicht wirklich kompliziert.

Download VS2010
Bookmark and Share

3 Kommentare:

Anonym hat gesagt…

Hallo,

das Screenshot Beispiel ist wirklich gut.
Wie heißt denn das Theme das du da für VS benutzt hast?!

Danke,
Andreas

Flo hat gesagt…

Hallo Andreas,
dankeschön. Das Design hab ich leider nicht mehr auf meinem Rechner. Kann ich dir nicht sagen.

mfg Florian

Abid Mohammed hat gesagt…

Hallo, This article was really helpful. Could you say me where is the rendered bitmap, i mean "filestream" getting saved? which location?

Kommentar veröffentlichen