Technology Experience

Contenuti gestiti da Igor Damiani
posts - 949, comments - 2741, trackbacks - 15120

My Links

News

  • Questo blog si propone di raccogliere riflessioni, teoriche e pratiche, su tutto quello che riguarda il world-computing che mi sta attorno: programmazione in .NET, software attuale e futuro, notizie provenienti dal web, tecnologia in generale, open-source.

    L'idea è quella di lasciare una sorta di patrimonio personale, una raccolta di idee che un giorno potrebbe farmi sorridere, al pensiero di dov'ero e cosa stavo facendo.

    10/05/2005,
    Milano

Archives

Post Categories

Generale

[MCAD.14] Completare la stampa con una bella anteprima

Ok, dopo qualche giorno di pausa, riprendiamo un po' con la nostra guida ad MCAD. Nel mio ultimo post di venerdì scorso, abbiamo visto come utilizzare la classe PrintDocument per stampare quello che vogliamo. Il succo sta nel creare un oggetto PrintDocument e gestire l'evento PrintPage della classe con un handler adatto, come abbiamo fatto qui:

prnDoc.PrintPage += new PrintPageEventHandler(this.printBirthday);

Quindi, abbiamo creato una function printBirthday(); e usando GDI+ abbiamo scritto tutto quello che vogliamo sul PrintDocument. Questo metodo, sebbene funzioni alla perfezione, ha un grosso difetto: non produce un'anteprima di stampa, ma indirizza l'output direttamente alla stampante predefinita del sistema. Questo può anche andar bene nel caso di applicazioni server, o applicazioni schedulate in notturna, o comunque in quei casi dove è necessario appunto generare una stampa senza intervento da parte dell'utente. D'altro canto, però, un'applicazione moderna che si rispetti deve avere la possibilità di visualizzare una bella anteprima di stampa, cosicchè l'utente possa avere un'idea di quello che sta ottenendo su carta. Questo è l'obiettivo di oggi.

Come avevamo accennato la volta precedente, abbiamo bisogno di una classe in più, la PrintPreviewDialog, che non sostituisce la PrintDocument, ma la complementa e la integra. Come funziona? Cominciamo col dire che la logica e la struttura che abbiamo già visto non cambiano: dobbiamo creare un oggetto PrintDocument, dobbiamo gestire il PrintPage e usare GDI+ per disegnare e scrivere il contenuto del documento. Vediamo un attimo come ho modificato il codice:

private void btnPrint_Click(object sender, System.EventArgs e)
{
    
this.PageNumer = 0;
    PrintPreviewDialog prnPreview = 
new PrintPreviewDialog();
    prnPreview.UseAntiAlias = 
true;

    PrintDocument prnDoc = 
new PrintDocument();
    prnDoc.PrintPage += 
new PrintPageEventHandler(this.printBirthday);
    prnDoc.DocumentName = resMan.GetString("TITLE_DOCUMENT");
    
this.TotalPage = this.lstResults.Items.Count / this.LinePerPage;
    
if (this.TotalPage == 0) this.TotalPage = 1;
    
//prnDoc.Print();
    
prnPreview.Document = prnDoc;
    prnPreview.ShowDialog();
}

Innanzitutto ho commentato la riga prnDoc.Print(): se la lasciassi, manderei in stampa il documento, esattamente come succedeva prima. Ho istanziato un oggetto prnPreview, ho associato la property Document all'oggetto PrintDocument che voglio mandare in stampa (prnDoc) e poi ho chiamato il metodo ShowDialog() per mostrare l'anteprima. La chiamata a ShowDialog() scatena l'evento PrintPage del PrintDocument, che viene gestito esattamente nello stesso modo e con lo stesso codice del mio post precedente. Possiamo dire quindi che il PrintPreviewDialog funge da container del PrintDocument: l'output viene reindirizzato su questo oggetto invece che sulla stampante.

Per fare dei test e delle prove interessanti, ho usato delle variabili PageNumber e TotalPage: contengono rispettivamente il numero della pagina corrente e il numero totale delle pagine che devono essere stampate. In questo modo, nella function printBirthday ho potuto scrivere quanto segue:

if (this.PageNumer == this.TotalPage)
    ev.HasMorePages = 
false;
else
    
ev.HasMorePages = true;

Oppure, aprofittando delle forme contratte di .NET che mi piacciono veramente tanto:

ev.HasMorePages = !(this.PageNumer == this.TotalPage);

Se proviamo a compilare il codice (che pubblicherò presto), vediamo come il click sul Button btnPrint mostra una finestra con la nostra anteprima di stampa che abbiamo appena realizzato. Fantastico!!! Forse l'unica cosa che mi fa storcere il naso è, per una volta, il troppo controllo che abbiamo in questo contesto: abituato come sono da anni a lavorare con Crystal Reports o altri engine di stampa, qui può essere davvero troppo lungo e macchinoso produrre report di una certa qualità. Vero è però che mai come adesso posso avere il pieno controllo di quello che succede sul report, potendo fino all'ultimo lavorare con GDI+ per posizionare bitmap, scritte orizzontali, verticali, ruotate (fatelo un po' con CR e impazzirete)...non solo, ma il tutto controllato da codice come si vuole, veramente cose dell'altro mondo!!! Consiglio inoltre una lettura al mio post di sabato Ereditare da PrintDocument e decorare la nuova classe con un custom Attribute perchè secondo me è un buon suggerimento su come velocizzare la creazione di report con gli Attribute di .NET.

Ultima chicca (opzionale): a qualcuno potrebbe non piacere l'aspetto della finestra di anteprima generata dalla classe PrintPreviewDialog. Non so se ci avete fatto caso, ma questa classe eredita da System.Windows.Forms, per cui, ciliegina sulla torta, abbiamo a disposizione la collection Controls, per cui possiamo aggiungere controlli a piacimento sulla WF dell'anteprima. In realtà non è così semplice: io sono riuscito ad aggiungere un Button btnClose, definito via codice, alla ToolBar, inserendo questo codice prima della chiamata a ShowDialog() che abbiamo visto prima. Vediamo un po':

foreach (object ctl in prnPreview.Controls)
{
    
if (ctl is ToolBar)
    {
        ToolBar tbl = (ToolBar) ctl;
        tbl.Buttons.Add(btnClose);
    }
}

Con un ciclo foreach sulla collection Controls, trovo l'oggetto ToolBar e faccio il solito tbl.Buttons.Add (btnClose) per aggiungere il nuovo pulsante alla ToolBar. Ho evitato di postare il codice per creare il Button btnClose perchè il solito che si può vedere in qualsiasi InitializeComponent() delle WF. L'unico problema è che il Button aggiuntivo viene posizionato (chissà perchè) sotto il Button Close previsto per default: Il ToolBarButton non espone la property Location, per cui......la soluzione c'è, ma esula dallo scopo di questo post. Alla prossima...

powered by IMHO 1.2

Print | posted on martedì 19 luglio 2005 15:05 | Filed Under [ MCAD ]

Feedback

Gravatar

# [MCAD.15] PageSetupDialog e PrintDialog, pi

21/07/2005 15:18 | Technology Experience
Gravatar

# [MDAC.17] Punto della situazione e qualche miglioramento, per la serie...ciliegine sulla torta!

26/07/2005 14:10 | Technology Experience
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET