Vorheriges Kapitel:  Grafik   Nächstes Kapitel: Serialisierung       Inhaltsverzeichnis Inhaltsverzeichnis       Die Downloadseite Download       Kontakt Kontakt

 

Drucken und Seitenansicht

 

OnPrepareDC(...)
OnBeginPrinting(...)
OnPreparePrinting(...)
OnPrint(...)
OnEndPrinting(...)
Das Beispielprogramm

 

 

Das Drucken von Dokumenten gehört in der Windows-Programmierung zu den schwierigen Aufgaben. Dank MFC ist diese Aufgabe jetzt leichter zu bewältigen. Wenn Sie in Windows etwas ausdrucken möchten, brauchen Sie sich nicht um den Drucker und Druckertreiber zu kümmern, Windows übernimmt diese Aufgabe für Sie. Wir haben uns bereits im Kapitel Grafik mit Begriffen wie Abbildungsmodi, Gerätekontext usw. beschäftigt. Wenn Sie die Absicht haben, mit Hilfe von MFC ein Dokument auszudrucken, erledigt das Anwendungsgerüst eine Reihe von Aufgaben für Sie. Die Seitenansicht ist eine Leistung von MFC und gehört nicht direkt zu Windows. Um eine Seitenansicht zu programmieren, bedarf es erheblicher Programieraufwand. MFC liefert Ihnen die Seitenansicht Gratis. Sie brauchen keine Zeile Quellcode dafür zu schreiben. Aus diesem Grund gehen wir nicht weiter auf die Seitenansicht ein. Damit Sie auch tatsächlich auf Papier das vorfinden, was Sie beabsichtigt hatten, müssen Sie eine Reihe von Vorkehrungen treffen. Sie müssen auf jeden Fall den richtigen Abbildungsmodus, Stift, Farbe, Seitenzahl und Seitenwechsel definieren. Wenn Sie im Schritt vier des AppWizards die Option Drucken und Seitenansicht gewählt haben stehen Ihnen folgende Funktionen für die Bewältigung der Druckaufgabe zur Verfügung:

 

 

Dies ist die erste Funktion, die vom Anwendungsgerüst(Framework) aufgerufen wird, um den Druckvorgang vorzubereiten. In dieser Funktion sollten Sie am besten den Abbildungsmodus setzen. Der Abbildungsmodus setzt fest, wie groß etwa eine Einheit auf dem Papier sein sollte. Die obige Funktion hat zwei Parameter, die Sie kennen sollten. CDC ist der Gerätekontext für den Druckvorgang. CPrintInof ist ein Strukt, das die nötigen Informationen über den Druckauftrag speichert. Ein Beispiel für die Funktion OnPrepareDC(.....) könnte wie folgt aussehen:

pDC->SetMapMode(MM_LOMETRIC);

Mit dieser Anweisung legen Sie fest, daß jede Einheit 0,1mm betragen sollte.

 

 

Diese Funktion wird vor einem Druckvorgang aber nach OnPrepareDC(.....) aufgerufen. Hier können Sie Ihre GDI-Objekte wie etwa Stifte oder Bürsten initialisieren. Sie können aber die GDI-Objekte direkt in OnPrint(...) initialisieren. Falls Sie die GDI-Objekte hier initialisieren, müssen Sie in der eigentlichen Druck-Funktion OnPrint(.....) das GDI-Objekt mit dem Gerätekontext verbinden.

 

 

Diese Funktion wird unmittelbar vor dem Drucken einer Seite vom Anwendungsgerüst aufgerufen. Falls die Länge des Dokuments Ihnen bekannt sein sollte, dann können Sie z.B. die Maximale Seitenzahl hier schreiben. Somit legen Sie auch fest, nach wie vielen Zeilen ein Seitenwechsel stattfinden sollte. Folgendes ist ein Beispiel dafür:

pInfo->SetMaxPage((Gesamtzahl der Zeilen)/(Zeilen pro Seite)+1);

 

 

Diese Funktion wird vom Anwendungsgerüst aufgerufen, um eine Seite eines Dokumentes auszudrucken. Für jede Seite, die gedruckt werden sollte, ruft das Anwendungsgerüst diese Funktion unmittelbar nach OnPrepareDC(.....) auf. Die Information über die aktuelle Seite erhält OnPrint(.....) vom m_nCurPage, das ein Mitglied von CPrintInfo ist. Für eine einfache Anwendung können Sie eine Grafik, die Sie in OnDraw(.....) gezeichnet haben, sehr einfach ausdrucken, in dem Sie die Funktion OnDraw(.....) in OnPrint einfach aufrufen. z.B.

OnDraw(pDC);

 

 

Falls Sie die GDI-Objekte in OnBeginPrinting(...) initialisiert haben, müssen Sie diese hier wieder löschen.

 

 

 

Das Beispielprogramm zu dem Thema ist jedoch einigermaßen kompliziert. Es berücksichtigt viele Probleme, denen Sie beim Ausdruck eines mehrseitigen Dokumentes begegnen würden. Die Anwendung Drucker druckt eine Rechnung aus.


Abbildung
Sie haben die Möglichkeit, Artikel, Artikelnummer und Preis in eine Listcontrol einzugeben. Bildschirm und die bedruckte Seite sehen unterschiedlich aus. Sie können auf jeder Seite 14 Artikel ausdrucken. Auf jeder bedruckten Seite stehen außerdem Gesamtpreis, Datum, Seitenzahl und Uhrzeit.

Abbildung
Legen Sie ein neues Projekt namens Drucker an. Wählen Sie im Schritt eins des AppWizards die Option Einzelnes Dokument und leiten Sie im Schritt sechs die Ansicht von CFormView ab. Plazieren Sie auf die Dialogseite drei Editboxen für die Eingabe von Artikel, Artikelnr und Preis. Bauen Sie außerdem eine Listcontrol , worin Sie Ihre Eingaben der Reihe nach eintragen. Die Anzahl der Einträge ist nicht beschränkt, was bedeutet, daß Sie mehre Seiten drucken können. Der Gesamtpreis muß auch noch in einer weiteren Editbox gezeigt werden. Schließlich brauchen Sie noch zwei Schaltflächen, eine für den Eintrag jeder Zeile in die Listcontrol und eine weitere zum Löschen der Einträge für den Beginn einer neuen Rechnung. Die Nachrichten-Funktion zum Eintrag in die Listcontrol heißt OnDone() und zum Löschen der Einträge und Beginn einer neuen Rechnung OnNeu(). Beide Nachrichten-Funktionen sind selbstverständlich mit ClassWizard erzeugt worden. Wie schon bekannt, müssen Sie zuerst die Listcontrol in OnInitialUpdate(.....) initialisieren. Der Quellcode sieht wie folgt aus:

void CDruckerView::OnInitialUpdate() 
{
CFormView::OnInitialUpdate();	
MessageBox("Hier geht's hauptsächlich um die Drucktechnik. Sie werden "
"eine einfache Quittung ausfüllen und sie dann ausdrucken. "
"Außerdem können Sie den Einsatz von einer List Control beobachten. ");
	
		
m_list.InsertColumn(0,"Artikel-Beschreibung",LVCFMT_LEFT,270,0);
m_list.InsertColumn(1,"Artikelnummer",LVCFMT_RIGHT,110,1);
m_list.InsertColumn(3,"Preis",LVCFMT_RIGHT,110,3);
}

m_list ist die Member-Variable vom Type CListCtrl und ist mit der Listcontrol verbunden. Die Funktion für den Eintrag in die Listcontrol, nämlich OnDone sieht wie folgt aus:

void CDruckerView::OnDone() 
{
char beschreib[128], artnr[20],preis[20];
LV_ITEM item;
UpdateData(TRUE);
strncpy(beschreib,m_beschreibung,127);
strncpy(artnr,m_art_nummer,19);
CString str;
str.Format("%0.2f",m_preis);
strncpy(preis,str,19);
	
item.mask=LVIF_TEXT;
item.iItem=0;
item.pszText=beschreib;
item.iSubItem=0;
int itemNr=m_list.InsertItem(&item);
item.iItem= itemNr;
item.iSubItem=1;
item.pszText=artnr;
m_list.SetItem(&item);
item.iItem=itemNr;
item.iSubItem=2;
item.pszText=preis;
m_list.SetItem(&item);
m_gesamt +=m_preis;
m_beschreibung="";
m_art_nummer="";
m_preis=0;
UpdateData(FALSE);
((CEdit*)(GetDlgItem(IDC_BESCHREIBUNG)))->SetFocus();
 
}		

Wenn Sie sich die ersten Zeilen des Quellcodes in der obigen Funktion ansehen, fällt auf, daß die Eingaben aus den Editboxen nochmals in den Variablen beschreib, artnr und preis kopiert wurden. Das war so nötig, denn die Listcontrol akzeptiert nur Null terminierte(/0) Zeichenketten und CString ist bekanntlich keine derartige Zeichenkette. Jetzt ist es an der Zeit, uns der eigentlichen Aufgabe des Druckens zu wenden.

Setzen Sie zuerst einmal den Abbildungsmodi in der Funktion OnPrepareDC(...):

void CDruckerView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) 
{
if(pDC->IsPrinting()){
pDC->SetMapMode(MM_LOMETRIC);
}
CFormView::OnPrepareDC(pDC, pInfo);
}

MM_LOMETRIC bedeutet, daß jede Einheit auf dem Papier 0,1mm betragen soll. Achten Sie darauf, daß die y-Komponente in diesem Abbildungsmodus negatives Vorzeichen hat. In der Einleitung zu dem Beispielprogramm wurde bereits erwähnt, daß nach 14 Einträgen bzw. Zeilen ein Seitenwechsel stattfinden sollte. Diesen Sachverhalt klären wir in die Funktion OnPreparePrinting(...) ab:

BOOL CDruckerView::OnPreparePrinting(CPrintInfo* pInfo)
{
pInfo->SetMaxPage(m_list.GetItemCount()/14 +1); 
return DoPreparePrinting(pInfo);
}

Die Gesamtzahl der Einträge erhalten wir durch die MFC Funktion GetItemCount(), die der Klasse CListCtrl angehört... Somit ruft das Anwendungsgerüst nach dem Druck von 14 Einträgen die Funktion OnPrint(...) neu auf, um eine neue Seite auszudrucken. Damit ist es aber nicht gesagt, daß tatsächlich auch neue Zeilen ausgedruckt werden.

Schauen wir uns nun die Funktion OnPrint(...) an:

void CDruckerView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
CString beschreib, artnr, preis, str;

//Teil 1: Gestaltung der Standardseite ohne Einträge:
pDC->SetMapMode(MM_LOMETRIC);//Maße in 0,1mm
int laenge=pDC->GetDeviceCaps(VERTSIZE);//Ausgabe in 0,1mm
int breite=pDC->GetDeviceCaps(HORZSIZE); //Maße des druckbaren Bereiches des Papiers
int rechts=breite*10-50;
int unten=-laenge*10+50;// Koordinatenursprung 
// ist oben links. Wert von y ist nach unten minus.

pDC->Rectangle(50,-80,rechts,unten+300);
CFont font, *pOldfont;

font.CreateFont(-100,0,0,0,700,FALSE,FALSE,0,ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,
DEFAULT_PITCH|FF_SWISS, "Times New Roman");

pOldfont= pDC->SelectObject(&font);
 
pDC->TextOut(600,-100," Firma Visual Power");
pDC->TextOut(800,-250,"Rechnung");
pDC->SelectObject(pOldfont);
pDC->MoveTo(50,-500);
pDC->LineTo(rechts,-500);
pDC->MoveTo(50,-700);
pDC->LineTo(rechts,-700);
pDC->TextOut(200,-600,"Artikel-Beschreibung");
pDC->TextOut(1100,-600,"Artikel-Nr.");
pDC->TextOut(1650,-600,"Preis");
pDC->MoveTo(50,unten+450);
pDC->LineTo(rechts,unten+450);
pDC->MoveTo(1500,-500);
pDC->LineTo(1500,unten+450);
pDC->MoveTo(950,-500);
pDC->LineTo(950,unten+450);
pDC->TextOut(200,unten+250,"Bitte beachten Sie unsere Allgemeinen Geschäftsbedingungen.");
pDC->TextOut(400,unten+200," Rücknahme nach dem Verkauf nicht möglich!");

//Teil 2:
str.Format("Gesamtpreis/EURO: %0.2f",m_gesamt);
pDC->SetTextAlign(TA_LEFT);
pDC->TextOut(80,unten+400,str);
pDC->TextOut(1100,unten+400,"Erhalten von:");

CTime t=CTime::GetCurrentTime(); //Zeit und Datum abfragen
CString dat=t.Format("Datum: %d,%m, %Y "); //Datum
pDC->TextOut(80,-400,dat);
CString zt=t.Format(" Uhrzeit: %H:%M"); //Uhrzeit
pDC->TextOut(1500,-400,zt);

int npage=pInfo->m_nCurPage; //Aktuelle Seite
str.Format("Seite %d ",npage);
pDC->TextOut(900,-400,str);

//Teil 3:
//Die Übernahme von Artikeln aus dem Dialog wird vorbereitet:
int nstart=(npage-1)*14 ;//Startwert für Seite 1=0
int nend=nstart+14; // Anzahl der druckbaren Artikel in einer Seite
int anzahl=m_list.GetItemCount();
int lauf=0;
       for(int i=nstart;i<nend;i++)
                 {
         lauf=lauf++;
     if(i>anzahl){break;}

         beschreib =m_list.GetItemText(i,0);
         artnr = m_list.GetItemText(i,1);
         preis = m_list.GetItemText(i,2);
         pDC->SetTextAlign(TA_LEFT);
         pDC->TextOut(80,-650-lauf*100,beschreib);	
         pDC->SetTextAlign(TA_RIGHT);
         pDC->TextOut(1450,-650-lauf*100,artnr);
         pDC->TextOut(rechts-80,-650-lauf*100,preis);
     if(lauf==14){

lauf=0; //damit in die neue Seite von oben an geschrieben wird.}

                             }
}

Der obige Quellcode besteht aus drei Teilen. Im Teil eins wird eine Standardseite als Rechnung entworfen, im Teil zwei bekommt jede Seite Uhrzeit, Seitenzahl, Datum und Gesamtpreis und im Teil 3 werden die Einträge aus der Listcontrol in die Rechnung eingetragen. Schauen wir uns mal die Zeilen genauer an:

Mit GetDeviceCaps(...) ermitteln Sie die Maße des druckbaren Bereiches auf dem Papier. Der Koordinatenursprung liegt zwar in die obere linke Ecke, die Werte von y haben nach unten negatives Vorzeichen. Die Überschrift der Rechnung schreiben Sie mit großer Schrift, deshalb müssen wir zuerst mit CreateFont(...) einen Stift dafür erzeugen. Nachdem Sie die Überschrift geschrieben haben, löschen Sie den großen Stift und wählen Sie den normalen Stift. Nun werden der Rahmen und die Spalten für die Rechnung gezeichnet. Im Teil zwei schreiben Sie den Quellcode für den Gesamtpreis, Uhrzeit, Seitenzahl und Datum auf jede Seite. Teil 3 hat eine weitere wichtige Aufgabe: Sie wissen bereits, daß das Anwendungsgerüst nach 14 Zeilen OnPrint(...) neu aufrufen und somit eine neue Seite drucken wird. OnPrint(...) würde ansonsten auf jede neue Seite die gleichen alten Zeilen ausgeben. Sie müssen nun dafür sorgen, daß nicht immer die gleiche Seite gedruckt wird. Wir müssen dafür eine Schleife schreiben. Die lokale Variable nstart bildet den Anfangswert der Schleife und nend den Endwert. Mit der Variable lauf innerhalb der Schleife, sorgen wir dafür, daß jedes Mal in einer neuen Seite am Anfang der Seite geschrieben wird. Vielleicht hat Sie Teil 3 ein wenig verwirrt, geben Sie jedoch nicht auf und gehen Sie den Quellcode am besten nochmals durch.

 

 

      Vorheriges Kapitel:  Grafik   Nächstes Kapitel: Serialisierung       Inhaltsverzeichnis Inhaltsverzeichnis       Die Downloadseite Download       Kontakt Kontakt