Condividi tramite


Modifiche al modello di programmazione

Le sezioni seguenti descrivono diversi modi in cui la programmazione con Windows GDI+ è diversa dalla programmazione con Windows Graphics Device Interface (GDI).

Contesti di dispositivo, handle e oggetti grafici

Se sono stati scritti programmi con GDI (l'interfaccia del dispositivo grafico inclusa nelle versioni precedenti di Windows), si ha familiarità con l'idea di un contesto di dispositivo (DC). Un contesto di dispositivo è una struttura usata da Windows per archiviare informazioni sulle funzionalità di un dispositivo di visualizzazione e attributi specifici che specificano la modalità di disegno degli elementi nel dispositivo. Un contesto di dispositivo per una visualizzazione video è associato anche a una particolare finestra sullo schermo. Prima di tutto si ottiene un handle per un contesto di dispositivo (HDC) e quindi si passa tale handle come argomento alle funzioni GDI che effettivamente eseguono il disegno. È anche possibile passare l'handle come argomento alle funzioni GDI che ottengono o impostano gli attributi del contesto di dispositivo.

Quando si usa GDI+, non è necessario preoccuparsi degli handle e dei contesti di dispositivo quando si usa GDI. È sufficiente creare un oggetto Graphics e quindi richiamarne i metodi nello stile familiare orientato agli oggetti, ovvero myGraphicsObject.DrawLine(parameters). L'oggetto Graphics è alla base di GDI+ proprio come il contesto del dispositivo è alla base di GDI. Il contesto di dispositivo e l'oggetto Graphics svolgono ruoli simili, ma esistono alcune differenze fondamentali tra il modello di programmazione basato su handle usato con i contesti di dispositivo (GDI) e il modello orientato agli oggetti usato con oggetti Graphics (GDI+).

L'oggetto Graphics , ad esempio il contesto di dispositivo, è associato a una determinata finestra sullo schermo e contiene attributi (ad esempio, modalità di smoothing e hint per il rendering del testo) che specificano la modalità di disegno degli elementi. Tuttavia, l'oggetto Graphics non è associato a una penna, un pennello, un percorso, un'immagine o un tipo di carattere come contesto di dispositivo. Ad esempio, in GDI, prima di poter usare un contesto di dispositivo per disegnare una linea, è necessario chiamare SelectObject per associare un oggetto penna al contesto di dispositivo. Questa operazione viene definita selezione della penna nel contesto del dispositivo. Tutte le linee disegnate nel contesto del dispositivo useranno tale penna fino a quando non si seleziona una penna diversa. Con GDI+, si passa un oggetto Pen come argomento al metodo DrawLine della classe Graphics . È possibile utilizzare un oggetto Pen diverso in ogni serie di chiamate DrawLine senza dover associare un determinato oggetto Pen a un oggetto Graphics .

Due modi per disegnare una linea

I due esempi seguenti tracciano una linea rossa di larghezza 3 dalla posizione (20, 10) alla posizione (200.100). Il primo esempio chiama GDI e il secondo chiama GDI+ tramite l'interfaccia della classe C++.

Disegno di una linea con GDI

Per disegnare una linea con GDI, sono necessari due oggetti: un contesto di dispositivo e una penna. Per ottenere un handle in un contesto di dispositivo, chiamare BeginPaint e un handle per una penna chiamando CreatePen. Chiamare quindi SelectObject per selezionare la penna nel contesto del dispositivo. Impostare la posizione della penna su (20, 10) chiamando MoveToEx e quindi disegnare una linea da tale posizione della penna su (200, 100) chiamando LineTo. Si noti che MoveToEx e LineTo ricevono entrambi hdc come argomento.

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);

Disegno di una linea con GDI+ e l'interfaccia della classe C++

Per disegnare una linea con GDI+ e l'interfaccia della classe C++, sono necessari un oggetto Graphics e un oggetto Pen . Si noti che non si chiede a Windows di gestire questi oggetti. Si usano invece costruttori per creare un'istanza della classe Graphics (un oggetto Graphics ) e un'istanza della classe Pen (un oggetto Pen ). Il disegno di una linea comporta la chiamata al metodo Graphics::D rawLine della classe Graphics . Il primo parametro del metodo Graphics::D rawLine è un puntatore all'oggetto Pen . Si tratta di uno schema più semplice e flessibile rispetto alla selezione di una penna in un contesto di dispositivo, come illustrato nell'esempio GDI precedente.

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);

Penne, pennelli, percorsi, immagini e tipi di carattere come parametri

Gli esempi precedenti mostrano che gli oggetti Pen possono essere creati e gestiti separatamente dall'oggetto Graphics , che fornisce i metodi di disegno. Gli oggetti Brush, GraphicsPath, Image e Font possono essere creati e gestiti separatamente dall'oggetto Graphics. Molti dei metodi di disegno forniti dalla classe Graphics ricevono un oggetto Brush, GraphicsPath, Image o Font come argomento. Ad esempio, l'indirizzo di un oggetto Brush viene passato come argomento al metodo FillRectangle e l'indirizzo di un oggetto GraphicsPath viene passato come argomento al metodo Graphics::D rawPath . Analogamente, gli indirizzi degli oggetti Image e Font vengono passati ai metodi DrawImage e DrawString . A differenza di GDI, in cui si seleziona un pennello, un percorso, un'immagine o un tipo di carattere nel contesto del dispositivo e quindi si passa un handle al contesto di dispositivo come argomento di una funzione di disegno.

Overload dei metodi

Molti dei metodi GDI+ sono in overload; ovvero, diversi metodi condividono lo stesso nome, ma hanno elenchi di parametri diversi. Ad esempio, il metodo DrawLine della classe Graphics è disponibile nei formati seguenti:

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);

Tutte e quattro le varianti DrawLine precedenti ricevono un puntatore a un oggetto Pen , le coordinate del punto iniziale e le coordinate del punto finale. Le prime due varianti ricevono le coordinate come numeri a virgola mobile e le ultime due varianti ricevono le coordinate come numeri interi. La prima e la terza variante ricevono le coordinate come elenco di quattro numeri separati, mentre la seconda e la quarta variante ricevono le coordinate come coppia di oggetti Point (o PointF).

Nessuna posizione corrente

Si noti che nei metodi DrawLine mostrati in precedenza sia il punto iniziale che il punto finale della riga vengono ricevuti come argomenti. Si tratta di una partenza dallo schema GDI in cui si chiama MoveToEx per impostare la posizione corrente della penna seguita da LineTo per disegnare una linea a partire da (x1, y1) e terminando a (x2, y2). GDI+ nel suo complesso ha abbandonato la nozione di posizione attuale.

Metodi separati per disegno e riempimento

GDI+ è più flessibile di GDI quando si tratta di disegnare i contorni e riempire gli interni di forme come rettangoli. GDI ha una funzione Rectangle che disegna il contorno e riempie l'interno di un rettangolo tutto in un unico passaggio. Il contorno viene disegnato con la penna attualmente selezionata e l'interno viene riempito con il pennello attualmente selezionato.

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+ dispone di metodi separati per disegnare il contorno e riempire l'interno di un rettangolo. Il metodo DrawRectangle della classe Graphics ha l'indirizzo di un oggetto Pen come uno dei relativi parametri e il metodo FillRectangle ha l'indirizzo di un oggetto Brush come uno dei relativi parametri.

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);

Si noti che i metodi FillRectangle e DrawRectangle in GDI+ ricevono argomenti che specificano il bordo sinistro, superiore, larghezza e altezza del rettangolo. Questo è a differenza della funzione GDIRectangle , che accetta argomenti che specificano il bordo sinistro del rettangolo, il bordo destro, l'alto e il basso. Si noti anche che il costruttore per la classe Color in GDI+ ha quattro parametri. Gli ultimi tre parametri sono i soliti valori rosso, verde e blu; il primo parametro è il valore alfa, che specifica la misura in cui il colore disegnato viene mescolato con il colore di sfondo.

Costruzione di aree

GDI offre diverse funzioni per la creazione di aree: CreateRectRgn, CreateEllpticRgn, CreateRoundRectRgn, CreatePolygonRgn e CreatePolyPolygonRgn. È possibile che la classe Region in GDI+ abbia costruttori analoghi che accettano rettangoli, puntini di sospensione, rettangoli arrotondati e poligoni come argomenti, ma questo non è il caso. La classe Region in GDI+ fornisce un costruttore che riceve un riferimento all'oggetto Rect e un altro costruttore che riceve l'indirizzo di un oggetto GraphicsPath . Se si vuole costruire un'area basata su un rettangolo arrotondato o un poligono arrotondato, è possibile creare facilmente un oggetto GraphicsPath (che contiene ad esempio un'ellisse) e quindi passare l'indirizzo dell'oggetto GraphicsPath a un costruttore Region .

GDI+ semplifica la creazione di aree complesse combinando forme e percorsi. La classe Region include metodi Union e Intersect che è possibile usare per aumentare un'area esistente con un percorso o un'altra area. Una caratteristica interessante dello schema GDI+ è che un oggetto GraphicsPath non viene eliminato definitivamente quando viene passato come argomento a un costruttore Region . In GDI è possibile convertire un percorso in un'area con la funzione PathToRegion , ma il percorso viene eliminato definitivamente nel processo. Inoltre, un oggetto GraphicsPath non viene eliminato definitivamente quando il relativo indirizzo viene passato come argomento a un metodo Union o Intersect, quindi è possibile usare un determinato percorso come blocco predefinito per diverse aree separate. come illustrato nell'esempio seguente. Si supponga che onePath sia un puntatore a un oggetto GraphicsPath (semplice o complesso) già inizializzato.

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