Vorheriges Kapitel:  Menüs  Nächstes Kapitel: Drucken       Inhaltsverzeichnis Inhaltsverzeichnis       Die Downloadseite Download       Kontakt Kontakt

 

Grafik

 

Grafik im Ansichtsfenster
Grafik in einer Dialogseite
Grafik in beliebigen Funktionen
Bitmaps
Schriften

 

 

 

Falls Sie unter MS DOS programmiert haben, wissen Sie daß viele Programme die Daten direkt in den Grafikspeicher oder an den Druckeranschluß ausgegeben haben. Damit dies auch der Druckvorgang funktionieren konnte, mußte für jede Grafikkarte und Drucker eine eigene Treibersoftware entwickelt werden. Windows hat diese Technik überholt. Dank der Windows GDI( Graphic Device Interface) auf Deutsch, Grafikgeräteschnittstelle, braucht man nicht mehr, sich um die Hardware zu kümmern. Diese Aufgabe übernimmt Windows selbst. Sie müssen in Ihren Anwendungen nur die GDI Funktionen aufrufen, um Grafiken zu programmieren. Ihr Programm gibt die Daten jedoch nicht direkt auf den Bildschirm aus. GDI legt ein Abbild der Grafik in einer Datenstruktur namens Gerätekontext(Device Context) im Speicher ab. Gerätekotexte sind in Klassen realisiert worden, die über zahlreiche Member-Funktionen verfügen, mit deren Hilfe Sie die Grafiken gestalten können. Sie können mit Hilfe eines Zeigers auf den Gerätekontext zugreifen und die Daten auf den Bildschirm oder Drucker ausgeben. Also wann immer Sie Grafiken programmieren, brauchen Sie einen Grätekontext dafür. Im allgemeinen, werden Sie in der Grafikprogrammierung drei unterschiedliche Gerätekontexte brauchen. Diese heißen CDC, CPaintDC und CClientDC. Wenn Sie vorhaben, die Grafik im Ansichtfenster bzw. Hauptrahmenfenster der Anwendung auszugeben, sind Sie auf CDC angewiesen. CDC befindet sich in der Nachrichten-Funktion OnDraw(). Falls Sie in einem Bereich Ihrer Dialogseite zeichnen möchten, müssen Sie CPaintDC benutzen. CPaintDC wird in der Nachrichten-Funktion OnPaint() aufgerufen. Machmal müssen Sie auch die Grafikfunktionen in anderen Funktionen aufrufen, wofür Sie dann den Gerätekontext CClientDC brauchen. Um mit der Grafik umgehen zu können, muß man sich leider ein wenig theoretisch anstrengen. Je nach Anforderung einer Anwendung, gibt es eine Reihe von Vorkehrungen, die Sie vorher treffen müssen, damit Sie auch den erwünschten Resultat auf dem Bildschirm erhalten. Sie müssen auf jeden Fall über den Client-Bereich, logische Koordinaten, Gerätekoordinaten, Bildschirmkoordinaten und Abbildungsmodie Bescheid wissen.

Der Client-Bereich Ihrer Anwendung ist der Bereich ohne Menüleiste, Rahmen und Statusleiste. Es ist der Bereich, wo Sie direkt zeichnen können. Sie können viele Funktionen nur innerhalb des Client-Bereiches anwenden. Die logischen Koordinaten beziehen sich auf den Client-Bereich. Die Werte bei den logischen Koordinaten sind so zu sagen absolut, d.h. falls Sie eine lange Zeichnung haben und Sie das Fenster scrollen müssen, nehmen die Koordinatenwerte auch entsprechend zu. Der Koordinatenursprung bei den logischen Koordinaten befindet sich an der oberen linken Ecke des Clientbereiches. Die Werte der Koordinaten nehmen nach rechts und nach unten zu. Die Werte der Gerätekoordinaten(auch Windowskoordinaten genannt) sind relativ, d.h. wenn Sie bei einer langen Grafik das Ansichtfenster scrollen, nehmen die Koordinatenwerte nicht entsprechend zu, da sich die Koordinaten immer auf das jeweils sichtbare Fenster beziehen. Bei den Bildschirmkoordinaten(Screen) liegt der Koordinatenursprung in der oberen linken Ecke des Bildschirmes. Die Wahl der Koordinaten ist nicht immer beliebig. Es gibt eine Reihe von Funktionen, die jeweils nur mit logischen oder Gerätekoordinaten arbeiten. Dank MFC stehen Ihnen Funktionen zur Umwandlung von Koordinaten zur Verfügung. Neben den Gerätekontextklassen, hat die MFC Klasse CWnd nützliche und wichtige Funktionen, die Sie sich anschauen sollten. Merken Sie sich an, daß die CDC Member-Funktionen logische Koordinaten und die CWnd Member-Funktionen Gerätekoordinaten benutzen. Die Koordinatenumwandlung spielt diesbezüglich eine wichtige Rolle.

Mit den Abbildungsmodi legen die Skalierung für die Größe der Grafik fest. Der voreingestellte Wert für die Koordinaten ist MM_TEXT. Hier entspricht eine Einheit des Koordinatensystems einer Pixelgröße. Je nach Auflösung des Bildschirms und der Grafikkarte, kann sich die Größe eines Pixels ändern. Sie können jedoch z.B. mit dem Abbildungsmodus MM_ LOMETRIC die Größe einer Einheit auf 0,1 mm festlegen. Der Abbildungsmodus ist vor allem zum Drucken wichtig.

Im folgenden wird versucht, anhand von drei Beispielen die Sache etwas zu vereinfachen. Der Umgang mit GDI-Objekten unterliegt einer festen Muster, die man sich leicht anmerken kann. Sie müssen immer zuerst ein GDI-Objekt erzeugen, das Objekt mit dem Gerätekontext verbinden und nach Beendigung der Zeichenoperation den Kontext wieder löschen. Die wichtigsten GDI-Objekte sind CPen, CBrush , CFont und CBitmap. Wie der Name schon sagt, ist CPen ein Stift, CBrush eine Bürste, mit der man geschlossene Bereiche färbt, CFont ist eine Schrift und CBitmap ein Bild.

 

 

Das Beispielprogramm zu diesem Thema heißt graf1.

Wann immer Sie im Ansichtfenster bzw. Hauptrahmenfenster Ihrer Anwendung zeichnen möchten, sollten Sie dies in der Funktion OnDraw(.....) tun. Die Funktion OnDraw(CDC* pDC) hat einen Zeiger auf den Gerätekontext CDC als Parameter. Sie können auf die Member-Funktionen von CDC über den Zeiger pDC zugreifen. Legen Sie mit AppWizard ein neues Projekt Namens graf1 an. Wählen Sie im Schritt eins des AppWizards die Option Einzelnes Dokument und akzeptieren Sie alle anderen Standardeinstellungen und erzeugen Sie Ihre Anwendung.


Abbildung
In der Funktion OnDraw(.....) Ihrer Ansicht können Sie folgenden Quellcode schreiben:

void CGraf1View::OnDraw(CDC* pDC)
{
CGraf1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// Ab hier:
CPen Pen, peni;
CPen* ptrPen;
CBrush Brush, *ptrBrush;
Pen.CreatePen(PS_SOLID,4,RGB(255,0,0));
ptrPen=pDC->SelectObject(&Pen);
pDC->Rectangle(10,10,180,180);
pDC->SetTextColor(RGB(140,70,0));
pDC->TextOut(220,100," Das ist Grafik !!!");
Brush.CreateSolidBrush(RGB(0,0,255));
ptrBrush=pDC-> SelectObject(&Brush);
pDC->Ellipse(400,50,570,170);
pDC->SelectObject(ptrBrush);
pDC->SelectObject(ptrPen);
peni.CreatePen(PS_SOLID,8,RGB(150,0,150));
pDC->SelectObject(&peni);
pDC->MoveTo(10,200);
pDC->LineTo(550,200);
pDC->LineTo(270,280);
pDC->LineTo(10,200);
pDC->SelectObject(ptrPen);
pDC->Rectangle(30,30,160,160);
pDC->Ellipse(40,40,150,150);
}

Es ist nicht unbedingt nötig, jede Zeile des Quellcodes zu erklären. Ich gehe nur auf die wesentlichen Teile des Codes ein:

Mit MFC Funktion CreatePen(.....) erzeugen Sie zuerst einen neuen Stift. Mit der Funktion SelectObject(.....) verbinden Sie den Stift mit dem Gerätekontext. Zusätzlich merkt sich SelectObject() den alten Stift, den wir in ptrPen speichern. Nachdem wir den Rechteck und die Ellipse gezeichnet haben, müssen wir den neuen Stift wieder löschen, indem wir den alten Stift wieder herstellen. Die Daten über den alten Stift befinden sich in Ptrpen, also wir benutzen wieder die Funktion SelectObject, aber diesmal mit ptrPen als Parameter. Mit der Funktion RGB(.....) bestimmen Sie die Farbe. Wenn Sie fürs Zeichen verschiedene Stifte brauchen, müssen Sie für jeden Stift auch ein neue Variable deklarieren. Im Beispielprogramm haben wir zwei Stifte erzeugt, mit den Variablen Pen und peni.

 

obenunten 

Das Beispielprogramm zu diesem Thema heißt graf2.

Wenn Sie auf einen bestimmten Bereich einer Dialogseite zeichnen möchten, dann sind Sie auf die Gerätekontextklasse CPaintDC angewiesen. Damit Sie überhaupt auf die Dialogseite zeichnen können, müssen Sie einen Bereich auf Ihrer Dialogseite als ungültig deklarieren. Die Funktion Invalidate(.....), die einen Bereich auf der Dialogseite für ungültig erklärt, löst gleichzeitig die Nachricht WM_PAINT aus, die den Zeichenvorgang einleitet. Nun zurück zu unserem Beispiel. Mit der Anwendung graf2 können Sie Farben mischen. Sie werden drei Schieberegler(Sliders) vorfinden, mit deren Hilfe Sie die Werte der Farbkomponenten rot, grün, blau(RGB) kombinieren. Das Ergebnis sehen Sie in einem Rechteck neben den Schiebereglern. Außerdem gibt die Anwendung die RGB Werte hexadezimal aus.


Abbildung
Legen Sie mit AppWizard ein Projekt namens graf2 an. Wählen Sie im Schritt eins des AppWizards die Option einzelnes Dokument und im Schritt sechs die MFC Klasse CFormView als Basisklasse für die Ansicht. Positionieren Sie die drei Schieberegler für die Farbkomponenten, ein Gruppenfeld als Rechteck für Anzeige der Farbe und drei Editboxen für die hexadezimalen RGB Werte auf die Dialogseite IDD_GRAF2_FORM. Deklarieren Sie in graf2view.h drei int variablen m_rot, m_gruen, m_blau und eine CRect-Variable m_farbe. Initialisieren Sie die Schieberegler in OnInitialUpdate(). Der Quellcode sieht wie folgt aus:

void CGraf2View::OnInitialUpdate() 
{
CFormView::OnInitialUpdate();
//Ab hier:
CSliderCtrl* pSlideRot=
(CSliderCtrl*)GetDlgItem(IDC_ROT);
pSlideRot->SetRange(0,255);
pSlideRot->SetTicFreq(15);
SetDlgItemText(IDC_STATIC_ROT,"0");

CSliderCtrl* pSlideGruen=
(CSliderCtrl*)GetDlgItem(IDC_GRUEN);
pSlideGruen->SetRange(0,255);
pSlideGruen->SetTicFreq(15);
SetDlgItemText(IDC_STATIC_GRUEN,"0");

CSliderCtrl* pSlideBlau=
(CSliderCtrl*)GetDlgItem(IDC_BLAU);
pSlideBlau->SetRange(0,255);
pSlideBlau->SetTicFreq(15);
SetDlgItemText(IDC_STATIC_BLAU,"0");

SetDlgItemText(IDC_HEX_ROT,"00");
SetDlgItemText(IDC_HEX_GRUEN,"00");
SetDlgItemText(IDC_HEX_BLAU,"00");

GetDlgItem (IDC_FARBE)->GetWindowRect (&m_farbe); 
ScreenToClient(&m_farbe);
int Border = (m_farbe.right - m_farbe.left) / 8;
m_farbe.InflateRect (-Border, -Border);
}

Der Quellcode für die Initialisierung der Schieberegler und Editboxen ist schon bekannt. Mit GetDlgItem(IDC_FARBE)->GetWindowRect(&m_farbe) wird die Position und Größe des Rechtecks IDC_FARBE in m_farbe gespeichert. Da GetWindowRect(.....) die Bildschirmkoordinaten liefert, müssen diese in Clientkoordinaten umgewandelt werden. ScreenToClient(.....) übernimmt diese Aufgabe. InflateRect(.....) baut einen Rand im Rechteck des Gruppenfeldes. Die Grafik sieht damit optisch besser aus.

Nun brauchen wir noch die Funktion OnPaint() für die Nachricht WM_PAINT zum Zeichnen, und die Funktion OnHScroll() für die Nachricht WM_HSCROLL zur Arbeit mit den Schiebereglern. Erzeugen Sie mit Hilfe von ClassWizard diese Funktionen. Der Quellcode für die Funktion OnPaint() sieht wie folgt aus:

 

void CGraf2View::OnPaint() 
{
CPaintDC dc(this); // Gerätekontext fürs Zeichnen
// Ab hier:
COLORREF Color = RGB(m_rot, m_gruen, m_blau);
dc.FillSolidRect (&m_farbe, Color);
}

Wir sehen, daß sich in OnPaint() nicht so furchtbar viel Quellcode befindet. Die erste Zeile des Codes, nämlich die Erzeugung des Gerätekontextes hat bereits ClassWizard für uns übernommen. Die Lokale Variable color vom Typ COLORREF speichert die RGB Werte . FillSolidRect(.....) malt das Rechteck des Gruppenfeldes mit der Farbe von color. Nun fällt auf, daß die RGB Werte irgendwo herkommen müssen. Außerdem muß OnPaint() von einer anderen Funktion aus aufgerufen werden, wenn die Farben ständig aktualisiert werden müssen. Die Antwort darauf gibt uns die Funktion OnHScroll():

 

void CGraf2View::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
//Ab hier:
CSliderCtrl* pSlide =(CSliderCtrl*) pScrollBar;
CString strText;

      switch(pScrollBar->GetDlgCtrlID())
                             {
           case IDC_ROT:
      strText.Format("%d",pSlide->GetPos());
      SetDlgItemText(IDC_STATIC_ROT,strText);
      m_rot=pSlide->GetPos();
      strText.Format("%.2X",pSlide->GetPos());
      SetDlgItemText(IDC_HEX_ROT,strText);
      break;
          case IDC_GRUEN:
      strText.Format("%d",pSlide->GetPos());
      SetDlgItemText(IDC_STATIC_GRUEN,strText);
      m_gruen=pSlide->GetPos();
      strText.Format("%.2X",pSlide->GetPos());
      SetDlgItemText(IDC_HEX_GRUEN,strText);
      break;
         case IDC_BLAU:
      strText.Format("%d",pSlide->GetPos());
      SetDlgItemText(IDC_STATIC_BLAU,strText);
      m_blau=pSlide->GetPos();
      strText.Format("%.2X",pSlide->GetPos());
      SetDlgItemText(IDC_HEX_BLAU,strText);
      break;
                                     }
InvalidateRect (&m_farbe);
UpdateWindow ();
CFormView::OnHScroll(nSBCode, nPos, pScrollBar);
}

Der Quellcode ist bis auf die letzten Zeilen nicht neu. Jedes Mal wenn Sie den Wert eines der Schieberegler ändern, merkt dies die Funktion OnHScroll() und aktualisiert die Werte m_rot, m_grün und m_blau. Anschließend wird die Funktion InvalidateRect(.....) aufgerufen, die einen Rechteck zum Update-Bereich der Dialogseite erklärt. Der Update-Bereich ist die Stelle, die neu gezeichnet werden muß. Diese Funktion löst außerdem die Nachricht WM_PAINT, die die Funktion OnPaint() aufruft.

 

 

 

Das Beispielprogramm zu diesem Thema heißt graf3.

Es kommt öfters vor, daß Sie Grafikfunktionen außerhalb von OnDraw() oder OnPaint() schreiben müssen. In einem solchen Fall müssen Sie den Gerätekontext CClientDC benutzen. Die Benutzung der GDI-Objekte unterliegt auch hier den gleichen Regeln wie in OnDraw(). In unserem Beispielprogramm werden wir lernen, wie man mit der Maus zeichnet und wie man die Windows Farbpalette benutzt. Außerdem ist diese Anwendung dialogbasierend. Dialogbasierend bedeutet, daß das Programm weder eine Ansicht, noch ein Dokument hat. Legen Sie ein Projekt namens graf3 an. Wählen Sie im Schritt eins des AppWizards die Option dialogbasierend und akzeptieren Sie alle anderen Schritte. Löschen Sie auf der Dialogseite IDD_GRAF3_DIALOG die Tasten OK und Cancel. Positionieren Sie zwei neue Schaltflächen, eine zum Aufruf der Windows Farbpalette und die andere als Exit Taste. Wenn Sie im fertigen Programm auf die linke Maustaste drücken, und die Maus dann bewegen, können Sie auf die Dialogseite Zeichnen.


Abbildung
Deklarieren Sie zwei int Variablen m_VorX, m_VorY und eine weitere Varible farbe vom Typ COLORREF in graf3Dlg.h. Wie Sie aus dieser Beschreibung entnehmen können, brauchen Sie eine Nachrichten-Funktion OnLButtonDown() für die Nachricht WM_LBUTTONDOWN und eine weitere Nachrichten-Funktion OnMouseMove für die Nachricht WM_MOUSEMOVE. Erzeugen Sie die besagten Funktionen mit Hilfe von ClassWizard. Der Quellcode in den beiden Funktionen sieht wie folgt aus.

void CGraf3Dlg::OnLButtonDown(UINT nFlags, CPoint point) 
{
m_VorX=point.x;
m_VorY=point.y;
CDialog::OnLButtonDown(nFlags, point);
}

Die obige Funktion liefert die Position des Mauscursers, wenn Sie zum ersten Mal auf die Maustaste drücken. Beachten Sie, daß point ein Parameter der obigen Funktion ist.

 

void CGraf3Dlg::OnMouseMove(UINT nFlags, CPoint point) 
{
    if((nFlags& MK_LBUTTON)== MK_LBUTTON)
    {
    CPen *oldpen;
    CClientDC dc(this);
    CPen NeuPen(PS_SOLID,3,farbe);
    oldpen= dc.SelectObject(&NeuPen);
    dc.MoveTo(m_VorX,m_VorY);
    dc.LineTo(point.x,point.y);
    m_VorX=point.x;
    m_VorY=point.y;
    dc.SelectObject(oldpen); 
   //probieren Sie die Anwendung
   //mal auch ohne die obige Zeile
    }
CDialog::OnMouseMove(nFlags, point);
}

Der Parameter nFlags der obigen Funktion stellt fest, welche Maustaste benutzt worden ist, falls die linke Maustaste(MK_LBUTTON) gedrückt worden war, sollte der Zeichenvorgang beginnen. Hier sehen Sie, daß der Gerätekontext CClientDC benutzt worden ist. Hier müssen Sie auch wie bei den anderen Gerätekontexten zuerst einen Stift erzeugen, den Stift mit dem Kontext verbinden und gleichzeitig den alten Stift in oldpen speichern. Hier ist es sehr wichtig den Stift am Ende der Funktion mit SelectObject(.....) wieder zu löschen. Andernfalls wird die Anwendung den Arbeitsspeicher sehr schnell komplett belegen, und Sie müssen dann die Anwendung beenden. Probieren Sie am besten die Anwendung einmal auch ohne den Stift zu löschen.

Sie werden in diesem Programm auch lernen, wie Sie die Windows Farbpalette aufrufen können. Verbinden Sie die Schaltfläche zum Aufruf der Farbpalette mit der Nachricht BN_CLICKED:

void CGraf3Dlg::OnPalette() 
{
CColorDialog dlgColor;
int iret=dlgColor.DoModal();
// Windows Farbpalette wird geladen
    if(iret !=IDCANCEL)
    {
   farbe=dlgColor.GetColor(); 
    }
   // gewählte Farbe wird abgefragt.
}

Es ist erstaunlich! Nur mit den beiden ersten Zeilen laden Sie bereits die Windows-Farbpalette. Mit GetColor speichern Sie die Farbe, die Sie aus der Farbpalette gewählt haben, in die Variable farbe. Die Variable farbe haben Sie schon in graf3Dlg.h deklariert. Jetzt müssen Sie noch die Exit Schaltfläche programmieren, und damit ist die Anwendung schon fertig.

void CGraf3Dlg::OnExit() 
{
OnOK(); 
}

 

 

 

Das Beispielprogramm zu diesem Thema heißt bitmap.

Ein Bitmap ist ein Datenfeld, das Bildschirmpixeln zugeordnet ist. Es gibt zwei Arten von Bitmaps: GDI-Bitmaps und DIBs. GDI-Bitmaps, auf die wir hier eingehen, sind geräteabhängig. DIBs sind im Gegenteil geräteunabhängig.

Da ein Bitmap ein GDI Objekt ist, kann man davon ausgehen, daß die bisher bekannte Prozedur zum Umgang mit den GDI-Objekten wieder verwendet wird. Es ist richtig. Es kommt aber noch einiges dazu. Sie müssen für Bitmaps einen Speicher-Gerätekontext erzeugen. Ein Speicher-Gerätekontext ist ein Block vom Speicher, der eine Bildschirmoberfläche repräsentiert. Die MFC Funktion CreateCompatibleDC(.....) übernimmt diese Aufgabe für Sie. Unser Beispielprogramm bitmaps lädt und löscht ein Bitmap vom Menü aus.


Abbildung
Legen Sie ein neues Projekt namens bitmaps und wählen Sie im Schritt eins des AppWizards die Option einzelnes Dokument. Starten Sie den Resource-Editor und aktivieren Sie den Befehl Einfügen/Resource. Wählen Sie dann den Resource-Typ Bitmap und aktivieren Sie die Schaltfläche Import.

Abbildung
Importieren Sie aus dem Verzeichnis Windows das Bitmap Wald.bmp(forest.bmp) und bestätigen Sie die Aktion. Nun ist das Bitmap Wald.bmp ein Bestandteil Ihrer Anwendung. Dieses Bitmap erscheint im Resource-Editor als IDB_BITMAP1. Benennen Sie es in IDB_WALD um, indem Sie IDB_BITMAP1 markieren und mit der rechten Maustaste die Eigenschaften des Bitmaps aufrufen. Entwerfen Sie mit dem Menü-Editor im Rsource-Editor ein neues Menü namens Bitmap. Die Unterpunkte des Menü Bitmap heißen Menü laden und Menü löschen. Der Befehl Menü laden wird nur dann aktiviert, wenn sich auf dem Bildschirm kein Bild befindet. Der Befehl Menü löschen ist dementsprechend nur dann aktiv, wenn das Bitmap bereits vorher geladen ist. Damit dies auch so funktioniert, müssen Sie mit Hilfe von ClassWizard für jeden dieser Befehle jeweils eine zusätzliche Nachrichten-Funktion zwecks Befehlsaktualisierung schreiben. Wenn Sie im ClassWizard den ID des Befehls Bitmap löschen markieren, stehen Ihnen zwei Nachrichten zur Verfügung, nämlich COMMAND und UPDATE_COMMAND_UI. In der zugehörigen Funktion zu COMMAND schreiben Sie den Code für den Befehl und in der zugehörigen Nachrichten-Funktion zu UPDATE_COMMAND_UI schreiben Sie die Bedingung für die Ausführung des Befehls. In diesem Zusammenhang wird hier mit einem Boolean Wert hantiert. Deklarieren Sie in bitmapsView.h eine Variable bitmap des Typs BOOL und setzen Sie ihren Wert im Konstruktor der Ansicht auf FALSE. Wenn Sie sich den Quellcode der vier Nachrichten-Funktionen für die zwei Befehle ansehen, ist das Konzept leicht verständlich:

 

void CBitmapsView::OnBmpLoad() 
{
bitmap=TRUE;
Invalidate(); 
}

 

void CBitmapsView::OnBmtClear() 
{
bitmap=FALSE;
Invalidate(); 
}

 

void CBitmapsView::OnUpdateBmtClear(CCmdUI* pCmdUI) 
{
pCmdUI->Enable(bitmap); 
}

 

void CBitmapsView::OnUpdateBmpLoad(CCmdUI* pCmdUI) 
{
pCmdUI->Enable(!bitmap); 
} 

Wenn Sie den Bitmap laden, wird der Wert von bitmap auf TRUE gesetzt und somit auch der Befehl Bitmap löschen aktiviert und umgekehrt. Die Funktion Invalidate() löst die Nachricht WM_PAINT aus, die wiederum die Funktion OnDraw() in der Ansicht aufruft. Schauen wir uns nun den Quellcode in der Funktion OnDraw() an:

void CBitmapsView::OnDraw(CDC* pDC)
{
CBitmapsDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//Ab hier:
CDC memo;
RECT rect;
BITMAP BP;
CBitmap m_bitmap;
GetClientRect(&rect);
    if(bitmap)
             {
    m_bitmap.LoadBitmap(IDB_WALD);
    m_bitmap.GetObject(sizeof(BP),&BP);
    int orgweite=BP.bmWidth;
    int orghoehe= BP.bmHeight;
    memo.CreateCompatibleDC(NULL);
    memo.SelectObject(&m_bitmap);
    pDC->StretchBlt( 0,
                     0,
                     rect.right,
                     rect.bottom,
                     &memo,
                     0,
                     0,
                     orgweite,
                     orghoehe,
                     SRCCOPY );
    DeleteObject(&BP);
          }
}

Die Funktion GetClientRect(.....) speichert die Größe des Clientbereiches in die Variable rect. Mit LoadBitmap() laden Sie die Bitmap IDB_WALD . Mit GetObject() ermitteln Sie die Höhe, Weite und Farbe des Bitmaps. Mit CreateCompatibleDC() erzeugen Sie einen Speicher-Gerätekontext. Und mit SelectObject() verbinden Sie den Speicher-Gerätekontext mit der Bitmap. Mit der Funktion StretchBlt(.....) rufen die Bitmap auf den Bildschirm auf. Wenn Sie die Größe des darstellenden Fensters ändern, paßt sich das Bitmap automatisch der Größe des neuen Fensters an. Mit DeleteObjekt() geben Sie den Speicher wieder frei. Nun werden Sie vielleicht den Quellcode zum Löschen der Bitmap suchen. Sie werden jedoch nicht sehr viel vorfinden. Der Quellcode in OnDraw() wird nur dann ausgeführt, wenn die Boolean-Variable bitmap den Wert TRUE hat. In der Nachrichten-Funktion zum Löschen der Bitmap hat die boolean Variale bitmap den Wert FALSE. Durch die Funktion Invalidate() wird OnDraw() ausgeführt. Die Funktion Invalidate() löscht den Inhalt des Bildschirms, bevor etwas Neues gezeichnet werden kann . Da die Boolean-Variable einen Zugriff auf den Quellcode diesmal verhindert, bleibt der Inhalt des Bildschirms leer.

 

 

 

Das Beispielprogramm zu diesem Thema heißt Letters.

Eine Schrift ist auch ein GDI-Objekt. Aus diesem Grunde muß man mit ihr wie andere GDI-Objekte umgehen. Man muß sie zuerst erzeugen, einen Kontext definieren und die Schrift dann mit dem Kontext verbinden. Das Beispielprogramm Letters kann die Schriftgröße, Schriftart, Schriftfarbe und Schriftauszeichnung festlegen. Die Dialogseite der Anwendung hat eine Editbox und einen rechteckigen Bereich, wo Sie das Ergebnis der Änderungen in der gewählten Schrift sehen können.


Abbildung
Legen Sie ein neues Projekt Namens Letters an. Wählen Sie im Schritt eins des AppWizards die Option einzelnes Dokument, und leiten Sie im Schritt sechs die Ansicht von CFormView ab. Bis auf die Funktion für die Benutzung der Schrift ist eigentlich nicht sehr viel neues dabei, falls Sie sich die vorherigen Lektionen angeschaut haben. Die Eigenschaften der Schrift bilden in der Anwendung Letters eine Property Sheet. Jede Seite der PropertySheet ist wiederum einem entsprechenden Formatbefehl zugeordnet. Das Menü Format hat fünf Unterpunkte, nämlich Schriftgröße, Schriftart, Schriftfarbe, Schriftauszeichnung und Eigenschaften. In der Property Sheet Eigenschaften haben Sie die Möglichkeit, die ersten vier Aktionen auszuführen. Um die Effektivität zu erhöhen, sind die gleichen Seiten der Property Sheet auch für Menübefehle benutzt worden. Sie müssen zwar für jede Seite der Property Sheet eine eigene Klasse erzeugen, diese Klassen können jedoch auch in einer einzigen Datei Platz finden. Diese Klassen befinden sich in unserem Beispielprogramm in property.cpp bzw. property.h. Im folgenden sehen Sie den Quellcode für den Befehl Schriftfarbe, die entsprechende Property Page aufruft:

void CLettersView::OnFarbe() 
{
CPropertySheet sheetfarbe;
sheetfarbe.AddPage(&pagefarbe);
sheetfarbe.DoModal();
m_StrText=pagefarbe.m_Text_Farbe;
InvalidateRect(&m_Flaeche);
UpdateWindow();
}

Die Variable pagefarbe ist ein Objekt der Klasse CPageFarbe, m_Text_Farbe ist die Laufvariable für die Radio Buttons der Farben, und m_StrText ist eine int Variable. Die Deklarationen dieser Variablen befinden sich in der Headerdatei. Wie Sie auch hier sehen, spielt die Funktion Invalidate(...) eine wichtige Rolle, da sie den Zeichenvorgang auslöst. In der gleichen Art funktionieren auch die anderen Formatbefehle. Um den Quellcode nachvollziehen zu können, schauen Sie sich die Headerdatei der Ansicht und die Member-Variablen der Steuerelemente, die ClassWizard erzeugt hat an. Nun werfen wir einen Blick auf die OnPaint() Funktion , wo sich die Funktionen zur Gestaltung von Schriften befinden:

void CLettersView::OnPaint() 
{
CPaintDC dc(this); // device context for painting
int IstFett=400;
int x,y;
BYTE IstKursiv=FALSE;
BYTE IstUnderline=FALSE;
CLettersDoc* pDoc=GetDocument();

// Teil 1
UpdateData(TRUE);
   switch(m_StrDicke)
        {
         case 0:
     IstFett=400;
     break;
         case 1:{
     IstFett=400;
     IstKursiv=TRUE;}
     break;
         case 2:{
     IstFett=700;
     IstKursiv=FALSE;}
     break;
        }
   switch(m_StrLine){
         case FALSE:
     IstUnderline=FALSE;
     break;
         case TRUE:
     IstUnderline=TRUE;
     break;}
//Teil2:

CFont m_font;
m_font.CreateFont(m_StrGroesse,
                             0,
                             0,
                             0,
                             IstFett,
                             IstKursiv,
                             IstUnderline,
                             0,
                             ANSI_CHARSET,
                             OUT_DEFAULT_PRECIS,
                             CLIP_DEFAULT_PRECIS,
                             DEFAULT_QUALITY,
                             DEFAULT_PITCH |FF_SWISS,
                             m_StrArt);

CFont* pOldFont=dc.SelectObject(&m_font);
   switch(m_StrText)
             {
         case 0:
     dc.SetTextColor(RGB(0,0,0));
     break;
         case 1:
     dc.SetTextColor(RGB(255,0,0));
     break;
         case 2:
     dc.SetTextColor(RGB(0,255,0));
     break;
         case 3:
     dc.SetTextColor(RGB(160,70,0));
     break;
         case 4:
     dc.SetTextColor(RGB(0,0,255));
     break;
                   }
dc.SetBkMode(TRANSPARENT); 

//Teil 3
x=m_Flaeche.left+8;
y=(m_Flaeche.bottom+m_Flaeche.top)/2;
dc.TextOut(x,y,m_Text);
}

Den Quellcode in der obigen Funktion kann man in drei Teilen aufteilen. Die Funktion für die Gestaltung der Schrift CreateFont(.....) , die sich in Teil zwei befindet, benötigt sehr viele Parameter. Teil eins sorgt dafür, daß diese Parameter ihre Informationen durch die Menübefehle oder Symbolschaltflächen bekommen. Sie müssen nicht jeden einzelnen Parameter in der Funktion CreateFont(.....) ändern, die wichtigsten Parameter sind der erste und der letzte Parameter. Mit dem ersten Parameter bestimmen Sie Schriftgröße und mit dem letzten die Schriftart. Im Teil drei wird die frisch erzeugte Schrift auf ein Viereck ausgegeben. Wenn Sie das Programm laufen lassen, wird Ihnen auffallen, daß der Inhalt der Editbox gleichzeitig auf dem Viereck erscheint. Für dies ist die Nachricht EN_CHANGE zuständig. Wann immer Sie etwas in eine Editbox eingeben, wird die Nachricht EN_CHANGE ausgelöst. Schreiben Sie Mit Hilfe des ClassWizards eine Nachrichten-Funktion, die diese Nachricht abfängt. Der Quellcode sieht wie folgt aus:

void CLettersView::OnChangeEditZeichen() 
{
InvalidateRect(&m_Flaeche);
UpdateWindow();
}

Wenn Sie etwas in die Editbox eingeben, löst Invalidate den Zeichenvorgang aus. Es ist noch zu erwähnen, daß die Member- Variable m_Text mit der Editbox verbunden ist.

 

 

      Vorheriges Kapitel:  Menü  Nächstes Kapitel: Drucken       Inhaltsverzeichnis Inhaltsverzeichnis       Die Downloadseite Download       Kontakt Kontakt