www.rolandk.de
- Aktuelle Themen zu .Net -
Achtung: Hier handelt es sich um meine alte Seite.
Die aktuelle ist unter folgendem Link erreichbar: www.rolandk.de/wp/
Home Artikel Grafikprogrammierung PictureBox mithilfe von Direct2D




















































PictureBox mithilfe von Direct2D
Samstag, den 26. Februar 2011 um 18:03 Uhr

 

 

Allgemeines

PictureBox ist wahrscheinlich den meisten .Net Entwicklern ein Begriff. Es handelt sich dabei um ein Steuerelement von Windows.Forms, mit dessen Hilfe einfach Bilder bzw. Bitmaps gezeichnet werden können. Im Hintergrund werden die Funktionen des Namensraums System.Drawing verwendet. In diesem Artikel gehe ich der Frage nach, wie ein solches Control auf Basis von Direct2D ausschauen könnte. Ziel dieses Artikels ist es also, die wichtigsten Funktionen des PictureBox-Steuerelements mithilfe von Direct2D nachzubauen und in Form der Klasse Direct2DPictureBox zu kapseln.

 

Wichtigste Funktion von PictureBox

Oben habe ich geschrieben: "Die wichtigsten Funktionen des PictureBox-Steuerelements..". Welche sind das eigentlich? Ich beziehe mich auf die Eigenschaften Image und SizeMode der PictureBox. Über Image kann dem Steuerelement ein Bild übergeben werden, welches dargestellt werden solll Mit der SizeMode Eigenschaft kann der Entwickler schließlich noch festlegen, wie es dargestellt werden soll. Gestreckt, zentriert oder doch in Originalgröße. In der Direct2DPictureBox könnten diese beiden Eigenschaften dann in etwa so nachgebaut werden:

  1. /// <summary>
  2. /// Gets or sets the currently displayed bitmap.
  3. /// </summary>
  4. [Category("Rendering")]
  5. public GDI.Bitmap Image
  6. {
  7. get { return m_gdiBitmap; }
  8. set
  9. {
  10. if (m_gdiBitmap != value)
  11. {
  12. m_gdiBitmap = value;
  13. m_direct2DBitmapInvalid = true;
  14. this.Invalidate();
  15. }
  16. }
  17. }
  18.  
  19. /// <summary>
  20. /// Gets or sets the size mode.
  21. /// </summary>
  22. [Category("Rendering")]
  23. public PictureBoxSizeMode SizeMode
  24. {
  25. get { return m_sizeMode; }
  26. set
  27. {
  28. if (m_sizeMode != value)
  29. {
  30. m_sizeMode = value;
  31. this.Invalidate();
  32. }
  33. }
  34. }

Der Entwickler kann dieser Klasse also ein mittels System.Drawing erstellten Bitmaps übergeben und über Direct2D zeichnen lassen. Die Eigenschaft SizeMode sieht nach außen genauso aus, wie bei der Standard PictureBox.

 

Direct2D Initialisieren

Damit Direct2D verwendet werden kann, müssen verschiedene Kriterien erfüllt sein. Zum einen benötigt man die Bibliothek SlimDX, die .Net Sprachen alle Funktion von Direct2D zur Verfügung stellt. Zum anderen muss in Direct2D ein separates RenderTarget-Objekt initialisiert werden. Dazu gibt es mehr Infos im Direct2D Tutorial auf dieser Seite. Nachfolgend das Coding, welches für eine Direct2DPictureBox nötig wäre.

  1. /// <summary>
  2. /// Called when the control's handle is created.
  3. /// </summary>
  4. protected override void OnHandleCreated(EventArgs e)
  5. {
  6. try
  7. {
  8. //Create the render target
  9. m_factory = new D2D.Factory(
  10. D2D.FactoryType.SingleThreaded,
  11. D2D.DebugLevel.Information);
  12. m_renderTarget = new D2D.WindowRenderTarget(
  13. m_factory,
  14. new D2D.WindowRenderTargetProperties()
  15. {
  16. Handle = this.Handle,
  17. PixelSize = this.Size,
  18. PresentOptions = D2D.PresentOptions.Immediately
  19. });
  20. }
  21. catch (Exception)
  22. {
  23. if (m_renderTarget != null) { m_renderTarget.Dispose(); }
  24. if (m_factory != null) { m_factory.Dispose(); }
  25. m_renderTarget = null;
  26. m_factory = null;
  27. }
  28. }
  29.  
  30. /// <summary>
  31. /// Called when the control's size has changed.
  32. /// </summary>
  33. protected override void OnSizeChanged(EventArgs e)
  34. {
  35. base.OnSizeChanged(e);
  36.  
  37. if (m_factory != null)
  38. {
  39. m_renderTarget.Resize(new Size(this.Width, this.Height));
  40. }
  41. }
  42.  
  43. /// <summary>
  44. /// Called when the control's handle is destroyed.
  45. /// </summary>
  46. protected override void OnHandleDestroyed(EventArgs e)
  47. {
  48. base.OnHandleDestroyed(e);
  49.  
  50. m_renderTarget = DisposeObject(m_renderTarget);
  51. m_factory = DisposeObject(m_factory);
  52. }

 

Zeichnen des Bildes

Der Direct2DPictureBox wird ein System.Drawing.Bitmap Objekt übergeben, dass nicht direkt mithilfe von Direct2D gerendert werden kann. Das Bitmap muss also vorher in ein Direct2D-Bitmap Objekt konvertiert werden. Eine entsprechende Funktion habe ich bereits im Direct2D Tutorial auf dieser Seite vorgestellt. Als nächstes wird noch eine Funktion benötigt, die aus der SizeMode Eigenschaft heraus die Größe und die Position des Bildes berechnet. Nachfolgendes Coding übernimmt genau diese Aufgabe:

  1. /// <summary>
  2. /// Gets an image rectangle based on the given size mode.
  3. /// </summary>
  4. private Rectangle GetDrawingRectangle(PictureBoxSizeMode sizeMode, Size imageSize)
  5. {
  6. Rectangle result = Rectangle.Empty;
  7.  
  8. switch (sizeMode)
  9. {
  10. case PictureBoxSizeMode.StretchImage:
  11. case PictureBoxSizeMode.AutoSize:
  12. result = new Rectangle(0, 0, this.Width, this.Height);
  13. break;
  14.  
  15. case PictureBoxSizeMode.CenterImage:
  16. result = new Rectangle(
  17. (int)(this.Width / 2f - imageSize.Width / 2f),
  18. (int)(this.Height / 2f - imageSize.Height / 2f),
  19. imageSize.Width, imageSize.Height);
  20. break;
  21.  
  22. case PictureBoxSizeMode.Normal:
  23. result = new Rectangle(0, 0, imageSize.Width, imageSize.Height);
  24. break;
  25.  
  26. case PictureBoxSizeMode.Zoom:
  27. double scaleFactorX = (double)imageSize.Width / (double)this.Width;
  28. double scaleFactorY = (double)imageSize.Height / (double)this.Height;
  29. double factorToUse = scaleFactorX > scaleFactorY ? scaleFactorX : scaleFactorY;
  30. Size newSize = new Size(
  31. (int)(imageSize.Width / factorToUse),
  32. (int)(imageSize.Height / factorToUse));
  33. result = new Rectangle(
  34. (int)(this.Width / 2f - newSize.Width / 2f),
  35. (int)(this.Height / 2f - newSize.Height / 2f),
  36. newSize.Width, newSize.Height);
  37. break;
  38. }
  39.  
  40. return result;
  41. }

Ich denke, mit dieser Funktion kann das Verhalten der originalen PictureBox recht gut imitiert werden. Was noch fehlt, ist das Zeichnen selbst. Gezeichnet wird grundsätzlich innerhalb des Paint Ereignisses des Direct2DPictureBox Controls. Nachfolgendes Coding zeigt das Bild auf den Bildschirm.

  1. //Draw using Direct2D
  2. m_renderTarget.BeginDraw();
  3. try
  4. {
  5. m_renderTarget.Clear(new Color4(this.BackColor));
  6.  
  7. if (m_direct2DBitmap != null)
  8. {
  9. m_renderTarget.DrawBitmap(
  10. m_direct2DBitmap,
  11. GetDrawingRectangle(m_sizeMode, m_direct2DBitmap.PixelSize));
  12. }
  13. }
  14. finally
  15. {
  16. m_renderTarget.EndDraw();
  17. }

Wie von Direct2D gewohnt ist dieser Schritt nichts weltbewegendes. Wichtig ist, dass immer BeginDraw und EndDraw aufgerufen wird, was ich persönlich gerne mit einem try-finally Block absichere.

 

Zusammenfassung

Dieser Artikel hat die wichtigsten Schritte gezeigt, eine Direct2DPictureBox Klasse zu entwickeln. Konkrete Vorteile gegenüber der originalen PictureBox gibt es zwar mehr in Spezialfällen, wie gesteigerte Performance bei häufigen Bildwechseln o. Ä., man kann diese Klasse aber gut als Vorlage für komplexere Aufgaben verwenden. Das diesem Artikel beigefügte Beispiel enthält eine komplett ausprogrammierte Direct2DPictureBox Klasse.

 

Quellen

  • Msdn
    Direct2D-Hilfe auf Msdn.
  • SlimDX
    Offizielle Seite des SlimDX-Projekts.

 

Siehe auch...

 

Kommentar hinzufügen

Ihr Name:
Kommentar: