Druckvorschau so möglich?



  • Hi

    Ich habe einen Link gefunden (http://www.se.fh-heilbronn.de/usefulstuff/VCPLUS6/kap_c.htm) Auf dieser Seite wird gezeigt, wie man eine Druckvorschau realisieren kann....
    Nun aber meine Frage:
    Ich benutze nur ein Dialog (habe bis jetzt immer gerade gedruckt, d.h alle Text und Linien direckt zum Drucker geleitet...) Ist es möglich mit diesem Code aus dem Link eine Vorschau zu erstellen??? Wenn ja wie?
    Kann ich auch einfach wie bisher

    dcPrint.MoveTo(xalt,yalt); //Start
    dcPrint.LineTo(abstandx,y); //Ende
    

    benützen um um eine Linie zu zeichnen? Mein Gedanke ist, dass ich eine Funktion habe, und dieser Funktion nur noch das Ausgabegerät (Drucker oder Vorschaufenster) übergeben muss...
    Ist dies so möglich?

    Vielen Dank für die Antworten!



  • Servus,

    durchaus ist das möglich. Du könntest eine SDI- oder MDI-Anwendung mit dem Anwendungsassistenten erstellen. In diesen wird nämlich das Drucken standardmäßig unterstützt. In deiner Ansichtsklasse fügst Du die virtuelle Funktion OnPrint(...) mit dem Klassenassistenten hinzu. In dieser Funktion rufst Du die gleiche Funktion auf, die auch das Zeichnen auf dem Fenster übernimmt. Also die Funktion, die auch in OnDraw(...) der View-Klasse aufgerufen wird, kann z.B. DrawContent(CDC* pDC, ...) heißen. In ihr steht dann dein Code:

    CYourView::DrawContent(CDC* pDC)
    {
       ...
    
       pDC.MoveTo(xalt,yalt); //Nicht mehr dcPrint.MoveTo(xalt,yalt); 
       pDC.LineTo(abstandx,y); //dcPrint.LineTo(abstandx,y);
    
       ...
    }
    

    Man kann Seitenansicht oder Drucken aus dem Datei-Menü wählen. Für beide Fälle wird deine Funktion DrawContent(CDC* pDC) aufgerufen. Der dabei übergebene Gerätekontext (pDC) bezieht sich dabei immer auf auf das jewilige Ausgabegerät. Beim Drucken auf den Druckertreiber, bei der Seitenansicht auf das entsprechende Fenster (Bildschirm, Graphikkartentreiber), beim Zeichnen in das Fenster der Anwendung eben auf dieses Fenster, usw.

    Vielleicht geht das auch mit einer Dialogbasierten Anwendung. Hab ich noch nicht probiert.

    Grüsse

    WOODZ



  • Vielen Dank!
    Ich habe eben eine Dialogbasierende Anwendung!
    Wie würde ich denn nun auf den Dialog anstatt auf den Drucker zeichnen?
    Einfach den Dialognamen.moveTo etc.?

    Ein Weiteres Problem habe ich, wenn ich eine Funktion machen will inder der Drucker auch gebraucht wird.
    Ich habe z.B ein Diagramm, welches über mehrere Seiten geht nun möchte ich die Fusszeile in eine separate Funktion auslagern... Wie geht dies? Sprich, wie kann ich ganze Objekte einer Funktion übergeben/einbinden?



  • Servus,

    ich hab mich ein bischen schlauer gemacht. Bei eienr Dialoganwendung kannst Du z.B. die Windows Print-Architektur verwenden. Das was ich dir gestern mit OnPrint(...) erzählt habe gilt nur für Projekte, in denen Klassen, die von CView abgeleitet sind vorkommen. Also wie z.B. eine MDI-Anwendung.

    Um auf den Dialog zu zeichnen mußt Du den Messagehandler für die WM_PAINT Nachricht mit dem Klassenassistenten in die von CDialog abgeleitete Klasse einfügen. Den Gerätekontext für den Bildschirm holst Du dir mit

    CPaintDC dc(this);
    

    Den Gerätekontext für den Drucker holst Du Dir so:
    Wenn Du den Druckernamen weißt

    CDC dc;
    dc.CreateDC(NULL, _T ("DeinDruckerName"), NULL, NULL);
    

    Wenn Du den unter Windows definierten Standarddrucker verwenden willst

    CDC dc;
    CPrintDialog dlg(FALSE);
    dlg.GetDefaults();
    dc.Attach(dlg.GetPrinterDC());
    

    Wenn Du den Benutzer einen Drucker auswählen lassen willst

    CDC dc;
    CPrintDialog dlg(FALSE);
    if(dlg.DoModal() ==  IDOK)
       dc.Attach(dlg.GetPrinterDC());
    

    Mit den 3 oben genannten Möglichkeiten holst Du dir also erst eilmal den Devicekontext für einen Drucker.
    Jetzt muß der Druckauftrag irgendwie gestartet werden. Das kann man z.B. so machen:

    DOCINFO di;
    ::ZeroMemory (&di,sizeof (DOCINFO));
    di.cdSize = sizeof (DOCINFO);
    di.lpszDocName = _T("Name des Druckauftrages, wie er im Spooler erscheint");
    di.StartDoc (&di);
    

    Jetzt folgen Grafikfunktionen zum Zeichnen auf den Gerätekontext, wie man es vom Zeichnen auf den Bildschirm her kennt, mit dem CDC-Objekt. Also z.B.

    dc.Ellipse(0,0,100,100);
    

    zeichnet einen Kreis mit einem Durchmesser von 100 logischen Einheiten.
    Logische Einheiten sind abhängig vom gewählten MapMode. Siehe unbedingt MSDN!

    Wenn Du mehrere Seiten Drucken möchtest bzw. den Start einer neuen Seite triggern möchtest (also auch beim Drucken von nur einer Seite), kann man die Zeichenroutine in eine Schleife packen, anchließend ist der Druckauftrag zu beenden:

    //hier muß natürlich all das, was ich oben prozessiert habe stehen, also Device-Context für Drucker holen, Druckauftrag starten, etc.
    for(int i=1; i<nPageCount; ++i)
    {
      dc.StartPage(); //Start der Seite
      dc.SetMapMode(/*der gewünschte Mode, z.B.*/MM_LOMETRIC);
      //MapMode muß für jede Seite neu gesetzt werden!
    
      //GDI-Objekte, Stift, Brush, etc. erstellen
      //GDI-Objekte in den Devicekontext selektieren mit dc.SelectObject(...)
      // hier stehen dann Deine Zeichenfunktionen, Ellipse, Linie Punkte, etc...
      //GDI-Objekte wieder freigeben
    
      dc.EndPage(); //Ende der Seite  
    }
    
    dc.EndDoc(); //Ende des Druckauftrages
    

    Objekte werden idR. eimal für den Gebrauch instanziirst. Für die Übergabe als Parameter an eine Funktion wird dann idR. der Pointer oder eine Referenz auf dieses Objekt übergeben.

    Beispiel:

    Du hast eine Klasse namens Diagam. In ihr befinden sich verschiedene Memberfunktionen, wie z.B. Initialize(...), ReadData(...), SaveData(...), DrawDataToScreen(...), DrawDataToPrinter(...), DrawHeader(...),
    DrawFooter(CString& strText), etc.

    Dann hast Du noch Datenkomponenten, wie z.B. CObList m_DataList, CString m_strHeadLine, CString m_strFootLine, int m_nDataCount, etc.

    Du instanziirst ein Objekt der Klasse Diagram mit

    Diagram myDiagram;
    

    Du bekommst irgendwo den Text für die Fußzeile her. Im einfachsten Fall gibst Du ihn statisch an mit

    CString strFooter("Das ist die Fußzeile für mein Diagramm");
    

    Du zeichnest den Text mit

    myDiagram.DrawFooter(strFooter);
    

    unter das Diagramm. Der Memberfunktion wird also ein CString-Objekt als Referenz übergeben. Das kann man natürlich genauso mit selbstdefinierten Objekten machen.

    Mehr weiß ich leider nicht. Vielleicht hilft dir das ein wenig. Wenn Du vielleicht beim nächsten Mal etwas Code mit postest, kann man konkret auf dein Problem eingehen.

    Grüsse

    WOODZ



  • Hi

    Drucken war bis jetzt kein Problem! Aber Danke trotzdem für die super Beschreibung!

    Ich handhabe das ganze bis jetzt so:

    if (dlgPrint.DoModal()==IDOK) 
        { 
            CDC dcPrint; 
            dcPrint.Attach(dlgPrint.GetPrinterDC()); 
            DOCINFO myPrintJob; 
            myPrintJob.cbSize = sizeof(myPrintJob); 
            myPrintJob.lpszDocName = "PrintingTest"; 
            myPrintJob.lpszOutput = NULL; 
            myPrintJob.lpszDatatype = NULL; 
            myPrintJob.fwType = NULL; 
            if (dcPrint.StartDoc(&myPrintJob)>=0) 
            { 
                // Als Hilfe wie man bestimmte Parameter abfragt 
                int te = dcPrint.GetDeviceCaps(ASPECTY); 
                int test = dcPrint.GetDeviceCaps(ASPECTX); 
                int m_iHorizont = dcPrint.GetDeviceCaps(HORZRES); 
                int m_iVerdikal = dcPrint.GetDeviceCaps(VERTRES);
    			int spalte2;
    			int zeilenabstand; 
    
                dcPrint.StartPage(); 
                RECT rctA; 
                rctA.left = (m_iHorizont/210) * m_iLeft;            // 210 Breite DIN A4   
                rctA.top = (m_iVerdikal/297) * m_iTop;              // 297 Höhe DIN A4   
                rctA.right  = m_iHorizont - ((m_iHorizont/210) * m_iRight);   
                rctA.bottom = m_iVerdikal - ((m_iVerdikal/297) * m_iButton);
    
    			zeilenabstand = (300/dcPrint.GetDeviceCaps(VERTRES)*20);
    //Verschiedene zeichnungen
    }
    

    Nun kommt eine Neue Seite und dazu möchte ich diese Funktion aufrufen (mit dem Ausgabegerät)

    void CADS2Dlg::fusszeile()
    {
    
        CString Textzeile;
        CString Dateiname="sahfjlasfhl.dat";
        CString Erstellt= "12.12.2004";
        CString datum="123456";
    
        Textzeile="Dateiname: %i | Erstellt am: %i | Ausgedruckt am: %i",Dateiname,Erstellt,datum;
    
        dcPrint.DrawText(Textzeile, &rctA, DT_WORDBREAK);
    
    }
    

    Was muss ich nun in die Zeile: void CADS2Dlg::fusszeile() noch eingeben und was muss ich bei Funktionsaufruf übergeben?

    Wäre es nun auch möglich, dass ich hier

    CDC dcPrint; 
            dcPrint.Attach(dlgPrint.GetPrinterDC());
    

    (in der ersten Zeilen) das Ausgabe gerät aus einer Variablen lese? (Damit ich nur eine Fukntion für Vorschau und Druck brauche) Ein anderes Problem ist, dass ich das ganze in der Druckvorschau Zoomen und Scrollen kann... Ist es nicht möglich wenn ich eine Dialogbasierende Anwendung habe, das ganze in ein fertiges Fenster zu "drucken" im Prinzip brauche ich so eine Vorschau wie in Word etc...

    Ist dies sehr aufwendig? Wie packe ich das ganze am Besten an?
    p.S weiss jemand gerade, wie ich das aktuelle Datum (dd.mm.yyyy) auslesen kann?
    (Diese Zeile stimmt irgendwie nicht so ganz! Wie kann ich Veriablen mit statischem Text zusammenführen?

    Textzeile="Dateiname: %i | Erstellt am: %i | Ausgedruckt am: %i",Dateiname,Erstellt,datum;
    

    Vielen Dank für die Antworten!!!!!



  • Hallo addict,

    hab ich wohl falsch verstanden, das mit dem Drucken.

    Zitat:
    Was muss ich nun in die Zeile: void CADS2Dlg::fusszeile() noch eingeben und was muss ich bei Funktionsaufruf übergeben?

    Ganz einfach:

    void CADS2Dlg::fusszeile(CDC* pDC) //Patrameter: pointer auf CDC
    

    und der Funktionsaufruf:

    fusszeile(&dcPrint) //Paranmeter: Adresse des (außerhalb) kreierten Gerätekontextes
    

    Zitat:
    Wäre es nun auch möglich, dass ich hier

    CDC dcPrint; 
            dcPrint.Attach(dlgPrint.GetPrinterDC());
    

    (in der ersten Zeilen) das Ausgabe gerät aus einer Variablen lese? ? (Damit ich nur eine Fukntion für Vorschau und Druck brauche)

    Antwort:
    Damit erstellst Du einen neuen Drucker-Devicekontext, der nichts mit dem vor Aufruf von CADS2Dlg::fusszeile(...) zu tun hat. Du müßtest einen neuen Druckauftrag anstossen. Machs so, wie ich Dir oben gezeigt habe.
    Für die Druckvorschau rufst Du einfach CADS2Dlg::fusszeile(...) mit dem Gerätekontext für den Bildschirm auf, also

    CPaintDC dc(this);
    fusszeile(&dc);
    

    wie ich es Dir bei der letzten Antwort auch schon versucht habe zu erklären.

    Zitat:
    Ein anderes Problem ist, dass ich das ganze in der Druckvorschau Zoomen und Scrollen kann... Ist es nicht möglich wenn ich eine Dialogbasierende Anwendung habe, das ganze in ein fertiges Fenster zu "drucken" im Prinzip brauche ich so eine Vorschau wie in Word etc...

    Antwort:
    Zoomen und scrollen wird von der Klasse CScrollView hervorragend unterstützt. Dazu würde sich allerdings eine SDI- oder MDI-Anwendung extrem gut anbieten. Dort ist vorallem die Seitenvorschau und die Druckerei so gut wie vorgefertigt. Man kann aber bestimmt auch einen neuen Dialog erstellen, indem man dann programmatisch ein CScrollView-Objekt einbettet. Etwas ähnliches hab ich auch in meinem Projekt gemacht. Dazu schaust Du am besten unter diesem Link:

    http://www.c-plusplus.net/forum/viewtopic.php?t=78787

    Bezieht sich aber auf eine MDI-Anwendung. Das prinzip ist ein Frame-Window zu erstellen und darin ein selbstdefiniertes CScrollview-Objekt einzubetten.
    Man kann auch die Messagehandler für die Nachrichten WM_VSCROLL und WM_HSCROLL implementieren, wenn man zu der Ansicht noch Scrolleisten hinzufügt. Das hab ich aber noch nie gemacht. Hab da also keine Ahnung.

    Zitat:
    p.S weiss jemand gerade, wie ich das aktuelle Datum (dd.mm.yyyy) auslesen kann?

    Antwort:
    Schau dir mal in der MSDN die CDateTimeCtrl-Klasse genauer an!

    gutes Gelingen

    WOODZ



  • Hi

    Sorry aber ich bin wohl etwas schwe von Begriff... 🙄

    Die Beispiele von CodeGuru sind irgendwie nicht so ganz das was ich suche...
    Ich möchte genau diese Vorschau (mit diesen Funktionen)
    http://www.codeguru.com/Cpp/W-D/doc_view/printing/article.php/c3263/
    Ich habe aber eine Komplett Dialogbasierende Anwendung! Wie kann ich das denn so umsetzen? (Am liebsten immernoch, dass ich nur eine Funktion brauche.)
    Ich kann doch den Drucker zuerst zuweisen und erst danach, die Fukntio aufrufen, dann funktioniert dies doch oder nicht?

    Sehe ich es richtig wenn die Fukntion so definiert ist

    void CADS2Dlg::fusszeile(CDC* pDC)
    

    ich dann mit pDC.move.... z.B zeichnen kann?

    Vielen Dank für die Geduld und die super Antworten!



  • Hi,

    Sorry aber ich bin wohl etwas schwe von Begriff...

    Das kann ich nicht beurteilen. Was Du aber auf jeden Fall verstehen mußt, wenn Du dich in der C/C++ Landschaft aufhältst sind Pointer und Referenzen. Dazu solltest Du dir ein Grundlagenbuch beschaffen und kräftig üben.

    Aussagen, wie

    Sehe ich es richtig wenn die Fukntion so definiert ist

    void CADS2Dlg::fusszeile(CDC* pDC)
    

    ich dann mit pDC.move.... z.B zeichnen kann?

    dürften dann nicht mehr vorkommen. Zumal im CDC-Objekt gar keine move(...)-Funktion existiert, nur eine MoveTo(...)-Funktion.
    pDC.move kann nicht funktionieren, weil pDC ein Pointer ist. Du mußt CDC::MoveTo(...) so

    pDC->MoveTo(...);
    

    aufrufen.


Anmelden zum Antworten