Lubie WPF’a.
Można o nim powiedzieć, że jest niedorobiony, wolny a technologia ta już nie będzie dalej rozwijana przez MS. Można też zachwycać się nad tym jak bardzo wiele jest klas w WPF i jak wiele jest ich jeszcze nieodkrytych kąsków.

Ostatnio natrafiłem na jeden z nich o którym chciałbym dziś napisać: *Bitmap(De/En)coder. Są to klasy, które umożliwiają pracę z różnymi formatami graficznymi np. PngBitmap(De/En)coder, BmpBitmap(De/En)coder, JpegBitnap(De/En)coder, TiffBitmap(De/En)coder.
Kiedyś, we wpisie 70-502 (WPF) – Image Metadata pisałem już o decoderach. Dziś trochę o encoderach.


Generalnie, jak można się domyśleć to co można odczytać za pomocą klas De-, przy pomocy klas En- można zapisać, tak więc w skrócie opisują za pomocą tych klas, można w WPFie stworzyć odpowiednie obrazki. Tyle? Gdyby to było wszystko pewnie bym o tym nie pisał posta (chociaż, kto wie – dawno już nic nie napisałem ;)) – posiadają one jednak ciekawą opcję. Potrafią, w bardzo prosty sposób zapisać w sobie stan kontrolki czyli zrobić zrzut ekranu naszej aplikacji. Sweet :]. Jak możemy ich użyć? Zobaczmy poniżej. Aby móc zapisać cokolwiek w tym obiekcie musimy dodać to do kolekcji Frames. Niestety przyjmuje ona obiekty typu BitmapFrame. Ona natomiast w statycznym konstruktorze bierze BitmapSource. Jak więc za tem możemy za ich pomocą zapisać do pliku nasze okno? Z pomocą przychodzi jeszcze jedna sweet-klasa a mianowicie RenderTargetBitmap.
A poskładane całe do kupy?

Code Snippet
  1. private void SaveScreenshot(System.Windows.Media.Imaging.BitmapEncoder encoder, string name)
  2. {
  3.     RenderTargetBitmap targetBitmap = new RenderTargetBitmap((int) Width, (int) Height, 96, 96,
  4.                                                              PixelFormats.Default);
  5.     targetBitmap.Render(this);
  6.     BitmapFrame frame = BitmapFrame.Create(targetBitmap);            
  7.     encoder.Frames.Add(frame);
  8.     using (FileStream stream = File.Open(name, FileMode.Create))
  9.     {
  10.         encoder.Save(stream);
  11.     }
  12. }

Tworzymy sobie odpowiednich rozmiarów RenderTargetBitmap a następnie renderujemy do niego zawartość naszej formy. Następnie wrzucamy to do frame’a i zapisujemy do pliku. Proste. Tak skonstruowaną metodę możemy sobie użyć z dowolnym decoderam i zapisywać do jakiego formatu nam się podoba.

Code Snippet
  1. private void SavePng(object sender, RoutedEventArgs e)
  2. {
  3.     PngBitmapEncoder encoder = new PngBitmapEncoder();
  4.     SaveScreenshot(encoder, “main.png”);
  5. }
  6.  
  7. private void SaveBmp(object sender, RoutedEventArgs e)
  8. {
  9.     BmpBitmapEncoder encoder = new BmpBitmapEncoder();
  10.     SaveScreenshot(encoder, “main.bmp”);
  11. }
  12.  
  13. private void SaveTiff(object sender, RoutedEventArgs e)
  14. {
  15.     TiffBitmapEncoder encoder = new TiffBitmapEncoder();
  16.     SaveScreenshot(encoder, “main.tiff”);
  17. }
  18.  
  19. private void SaveJpg(object sender, RoutedEventArgs e)
  20. {
  21.     JpegBitmapEncoder encoder = new JpegBitmapEncoder();
  22.     SaveScreenshot(encoder, “main.jpg”);
  23. }

Gotowe. PS. Jak wykonacie poniższy kod to zobaczycie dziwną ramkę po prawej i u dołu. Powodem tego jest fakt, że podstawiamy wielkość całego okna wraz z obramowaniem a renderowana jest tylko zawartość konta. Przez to wymiary faktycznego obrazka są większe niż to co na nim rysujemy.
Miłego renderowania.