Änderungen am Programmiermodell

In den folgenden Abschnitten werden verschiedene Möglichkeiten beschrieben, wie sich die Programmierung mit Windows GDI+ von der Programmierung mit Windows Graphics Device Interface (GDI) unterscheidet.

Gerätekontexte, Handles und Grafikobjekte

Wenn Sie Programme mit GDI (der Grafikgeräteschnittstelle, die in früheren Versionen von Windows enthalten ist) geschrieben haben, sind Sie mit der Idee eines Gerätekontexts (Device Context, DC) vertraut. Ein Gerätekontext ist eine Struktur, die von Windows verwendet wird, um Informationen über die Funktionen eines bestimmten Anzeigegeräts und Attribute zu speichern, die angeben, wie Elemente auf diesem Gerät gezeichnet werden. Ein Gerätekontext für eine Videoanzeige ist auch einem bestimmten Fenster auf dem Display zugeordnet. Zuerst erhalten Sie ein Handle für einen Gerätekontext (HDC), und dann übergeben Sie dieses Handle als Argument an GDI-Funktionen, die die Zeichnung tatsächlich ausführen. Sie übergeben das Handle auch als Argument an GDI-Funktionen, die die Attribute des Gerätekontexts abrufen oder festlegen.

Wenn Sie GDI+ verwenden, müssen Sie sich nicht so mit Handles und Gerätekontexten beschäftigen wie bei der Verwendung von GDI. Sie erstellen einfach ein Graphics-Objekt und rufen dann dessen Methoden im bekannten objektorientierten Stil auf – myGraphicsObject.DrawLine(parameters). Das Grafikobjekt ist der Kern von GDI+, genau wie der Gerätekontext der Kern von GDI. Der Gerätekontext und das Grafikobjekt spielen ähnliche Rollen, es gibt jedoch einige grundlegende Unterschiede zwischen dem handlebasierten Programmiermodell, das mit Gerätekontexten (GDI) verwendet wird, und dem objektorientierten Modell, das mit Grafikobjekten (GDI+) verwendet wird.

Das Grafikobjekt ist wie der Gerätekontext einem bestimmten Fenster auf dem Bildschirm zugeordnet und enthält Attribute (z. B. Glättungsmodus und Textrenderinghinweise), die angeben, wie Elemente gezeichnet werden sollen. Das Graphics-Objekt ist jedoch nicht an einen Stift, Pinsel, Pfad, Bild oder Schriftart gebunden, da es sich um einen Gerätekontext handelt. Wenn Sie beispielsweise in GDI einen Gerätekontext zum Zeichnen einer Linie verwenden können, müssen Sie SelectObject aufrufen, um dem Gerätekontext ein Stiftobjekt zuzuordnen. Dies wird als Auswählen des Stifts im Gerätekontext bezeichnet. Alle im Gerätekontext gezeichneten Linien verwenden diesen Stift, bis Sie einen anderen Stift auswählen. Mit GDI+ übergeben Sie ein Pen-Objekt als Argument an die DrawLine-Methode der Graphics-Klasse . Sie können in jedem einer Reihe von DrawLine-Aufrufen ein anderes Pen-Objekt verwenden, ohne ein bestimmtes Pen-Objekt einem Graphics-Objekt zuordnen zu müssen.

Zwei Möglichkeiten zum Zeichnen einer Linie

In den folgenden beiden Beispielen wird jeweils eine rote Linie der Breite 3 von Position (20, 10) zur Position (200.100) gezeichnet. Im ersten Beispiel wird GDI aufgerufen, im zweiten wird GDI+ über die C++-Klassenschnittstelle aufgerufen.

Zeichnen einer Linie mit GDI

Zum Zeichnen einer Linie mit GDI benötigen Sie zwei Objekte: einen Gerätekontext und einen Stift. Sie erhalten ein Handle für einen Gerätekontext, indem Sie BeginPaint aufrufen, und ein Handle für einen Stift, indem Sie CreatePen aufrufen. Als Nächstes rufen Sie SelectObject auf, um den Stift im Gerätekontext auszuwählen. Sie legen die Stiftposition auf (20, 10) fest, indem Sie MoveToEx aufrufen und dann eine Linie von dieser Stiftposition auf (200, 100) zeichnen, indem Sie LineTo aufrufen. Beachten Sie, dass MoveToEx und LineTohdc als Argument empfangen.

HDC          hdc;
PAINTSTRUCT  ps;
HPEN         hPen;
HPEN         hPenOld;
hdc = BeginPaint(hWnd, &ps);
   hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
   hPenOld = (HPEN)SelectObject(hdc, hPen);
   MoveToEx(hdc, 20, 10, NULL);
   LineTo(hdc, 200, 100);
   SelectObject(hdc, hPenOld);
   DeleteObject(hPen);
EndPaint(hWnd, &ps);

Zeichnen einer Linie mit GDI+ und der C++-Klassenschnittstelle

Zum Zeichnen einer Linie mit GDI+ und der C++-Klassenschnittstelle benötigen Sie ein Graphics-Objekt und ein Pen-Objekt . Beachten Sie, dass Sie Windows nicht nach Handles für diese Objekte fragen. Stattdessen verwenden Sie Konstruktoren, um eine instance der Graphics-Klasse (ein Graphics-Objekt) und eine instance der Pen-Klasse (ein Pen-Objekt) zu erstellen. Beim Zeichnen einer Linie wird die Graphics::D rawLine-Methode der Graphics-Klasse aufgerufen. Der erste Parameter der Graphics::D rawLine-Methode ist ein Zeiger auf Ihr Pen-Objekt . Dies ist ein einfacheres und flexibleres Schema als das Auswählen eines Stifts in einen Gerätekontext, wie im vorherigen GDI-Beispiel gezeigt.

HDC          hdc;
PAINTSTRUCT  ps;
Pen*         myPen;
Graphics*    myGraphics;
hdc = BeginPaint(hWnd, &ps);
   myPen = new Pen(Color(255, 255, 0, 0), 3);
   myGraphics = new Graphics(hdc);
   myGraphics->DrawLine(myPen, 20, 10, 200, 100);
   delete myGraphics;
   delete myPen;
EndPaint(hWnd, &ps);

Stifte, Pinsel, Pfade, Bilder und Schriftarten als Parameter

Die vorherigen Beispiele zeigen, dass Pen-Objekte getrennt vom Graphics-Objekt erstellt und verwaltet werden können, das die Zeichnungsmethoden bereitstellt. Brush-, GraphicsPath-, Image- und Font-Objekte können auch getrennt vom Graphics-Objekt erstellt und verwaltet werden. Viele der von der Graphics-Klasse bereitgestellten Zeichnungsmethoden erhalten ein Brush-, GraphicsPath-, Image- oder Font-Objekt als Argument. Beispielsweise wird die Adresse eines Brush-Objekts als Argument an die FillRectangle-Methode übergeben, und die Adresse eines GraphicsPath-Objekts wird als Argument an die Graphics::D rawPath-Methode übergeben. Ebenso werden Adressen von Image - und Font-Objekten an die DrawImage - und DrawString-Methoden übergeben. Dies steht im Gegensatz zu GDI, bei dem Sie einen Pinsel, pfad, ein Bild oder eine Schriftart im Gerätekontext auswählen und dann ein Handle als Argument an eine Zeichnungsfunktion an den Gerätekontext übergeben.

Methodenüberladung

Viele der GDI+-Methoden sind überladen; Das heißt, mehrere Methoden haben den gleichen Namen, verfügen aber über unterschiedliche Parameterlisten. Die DrawLine-Methode der Graphics-Klasse hat beispielsweise folgende Formen:

Status DrawLine(IN const Pen* pen,
                IN REAL x1,
                IN REAL y1,
                IN REAL x2,
                IN REAL y2);
Status DrawLine(IN const Pen* pen,
                IN const PointF& pt1,
                IN const PointF& pt2);
Status DrawLine(IN const Pen* pen,
                IN INT x1,
                IN INT y1,
                IN INT x2,
                IN INT y2);
    
Status DrawLine(IN const Pen* pen,
                IN const Point& pt1,
                IN const Point& pt2);

Alle vier oben genannten DrawLine-Variationen erhalten einen Zeiger auf ein Pen-Objekt , die Koordinaten des Startpunkts und die Koordinaten des Endpunkts. Die ersten beiden Variationen erhalten die Koordinaten als Gleitkommazahlen, und die letzten beiden Variationen erhalten die Koordinaten als ganze Zahlen. Die erste und dritte Variante erhalten die Koordinaten als Liste von vier separaten Zahlen, während die zweite und vierte Variation die Koordinaten als Paar von Point-Objekten (oder PointF)-Objekten empfangen.

Keine aktuelle Position mehr

Beachten Sie, dass in den zuvor gezeigten DrawLine-Methoden sowohl der Startpunkt als auch der Endpunkt der Linie als Argumente empfangen werden. Dies ist eine Abkehr vom GDI-Schema, bei dem Sie MoveToEx aufrufen, um die aktuelle Stiftposition festzulegen, gefolgt von LineTo , um eine Linie zu zeichnen, die bei (x1, y1) beginnt und bei (x2, y2) endet. GDI+ als Ganzes hat den Begriff der aktuellen Position aufgegeben.

Separate Methoden für Draw und Fill

GDI+ ist flexibler als GDI, wenn es darum geht, die Konturen zu zeichnen und das Innere von Formen wie Rechtecke zu füllen. GDI verfügt über eine Rectangle-Funktion , die die Kontur zeichnet und das Innere eines Rechtecks in einem Schritt ausfüllt. Die Kontur wird mit dem aktuell ausgewählten Stift gezeichnet, und das Innere wird mit dem aktuell ausgewählten Pinsel gefüllt.

hBrush = CreateHatchBrush(HS_CROSS, RGB(0, 0, 255));
hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
SelectObject(hdc, hBrush);
SelectObject(hdc, hPen);
Rectangle(hdc, 100, 50, 200, 80);

GDI+ verfügt über separate Methoden zum Zeichnen der Kontur und zum Füllen des Inneren eines Rechtecks. Die DrawRectangle-Methode der Graphics-Klasse hat die Adresse eines Pen-Objekts als einen ihrer Parameter, und die FillRectangle-Methode hat die Adresse eines Brush-Objekts als einen ihrer Parameter.

HatchBrush* myHatchBrush = new HatchBrush(
   HatchStyleCross,
   Color(255, 0, 255, 0),
   Color(255, 0, 0, 255));
Pen* myPen = new Pen(Color(255, 255, 0, 0), 3);
myGraphics.FillRectangle(myHatchBrush, 100, 50, 100, 30);
myGraphics.DrawRectangle(myPen, 100, 50, 100, 30);

Beachten Sie, dass die FillRectangle - und DrawRectangle-Methoden in GDI+ Argumente empfangen, die den linken Rand, die obere, breite und höhe des Rechtecks angeben. Dies steht im Gegensatz zur GDIRectangle-Funktion , die Argumente akzeptiert, die den linken Rand, den rechten Rand, den oberen und unteren Rand des Rechtecks angeben. Beachten Sie auch, dass der Konstruktor für die Color-Klasse in GDI+ über vier Parameter verfügt. Die letzten drei Parameter sind die üblichen roten, grünen und blauen Werte; Der erste Parameter ist der Alphawert, der den Umfang angibt, in dem die gezeichnete Farbe mit der Hintergrundfarbe gemischt wird.

Erstellen von Regionen

GDI bietet mehrere Funktionen zum Erstellen von Regionen: CreateRectRgn, CreateEllpticRgn, CreateRoundRectRgn, CreatePolygonRgn und CreatePolyPolygonRgn. Sie können erwarten, dass die Region-Klasse in GDI+ über analoge Konstruktoren verfügt, die Rechtecke, Ellipsen, abgerundete Rechtecke und Polygone als Argumente verwenden, aber dies ist nicht der Fall. Die Region-Klasse in GDI+ stellt einen Konstruktor bereit, der einen Rect-Objektverweis und einen anderen Konstruktor empfängt, der die Adresse eines GraphicsPath-Objektsempfängt. Wenn Sie einen Bereich basierend auf einer Ellipse, einem abgerundeten Rechteck oder einem Polygon erstellen möchten, können Sie dies ganz einfach tun, indem Sie ein GraphicsPath-Objekt erstellen (das z. B. eine Ellipse enthält) und dann die Adresse dieses GraphicsPath-Objekts an einen Region-Konstruktor übergeben.

GDI+ erleichtert das Bilden komplexer Bereiche durch die Kombination von Formen und Pfaden. Die Region-Klasse verfügt über Union - und Intersect-Methoden , mit denen Sie eine vorhandene Region um einen Pfad oder eine andere Region erweitern können. Ein gutes Feature des GDI+-Schemas ist, dass ein GraphicsPath-Objekt nicht zerstört wird, wenn es als Argument an einen Region-Konstruktor übergeben wird. In GDI können Sie einen Pfad mit der PathToRegion-Funktion in eine Region konvertieren, aber der Pfad wird im Prozess zerstört. Außerdem wird ein GraphicsPath-Objekt nicht zerstört, wenn seine Adresse als Argument an eine Union- oder Intersect-Methode übergeben wird, sodass Sie einen angegebenen Pfad als Baustein für mehrere separate Regionen verwenden können. Dies wird im folgenden Beispiel gezeigt. Angenommen , onePath ist ein Zeiger auf ein GraphicsPath-Objekt (einfach oder komplex), das bereits initialisiert wurde.

Region  region1(rect1);
Region  region2(rect2);
region1.Union(onePath);
region2.Intersect(onePath);