Una calcolatrice a nastro in Visual Basic 2005 - seconda parte
a cura di Mario De Ghetto (requisiti: VB2005)Il codice del form Calcolatrice.vb
Dopo aver esaminato attentamente la classe clsNastro è possibile fare la stessa operazione con il codice del form.
La prima parte, come al solito, contiene l'intestazione della classe form e le dichiarazioni delle variabili. Le variabili hanno le seguenti finalità:
- flagPuntoDecimale è un indicatore che rileva se il punto decimale è già stato inserito nel valore corrente, per impedire che possa essere inserito un secondo punto decimale;
- cNastro è un riferimento all'istanza di classe clsNastro;
- Valore contiene il valore memorizzato durante i calcoli;
- documento è un riferimento ad un oggetto di tipo PrintDocument, per realizzare l'anteprima di stampa.
Public Class Calcolatrice Private flagPuntoDecimale As Boolean = False Private cNastro As clsNastro Private Valore As Double = 0 ' dichiara un oggetto PrintDocument di nome documento (per anteprima) Private WithEvents documento As New System.Drawing.Printing.PrintDocumentA seguire, viene gestito l'evento Load del form Calcolatrice che viene scatenato al caricamento del form stesso. Nel gestore di questo evento vengono effettuate due operazioni: la creazione dell'istanza di classe clsNastro, passandogli il riferimento alla ListBox, e la chiamata al metodo InizializzaPrintPreviewDialog che sarà analizzato in seguito.
Private Sub Calcolatrice_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load cNastro = New clsNastro(Me.Nastro) InizializzaPrintPreviewDialog() End SubA questo punto viene riportato il codice di gestione dell'evento Click dei dieci pulsanti numerici (da 0 a 9) che semplicemente chiamano il metodo premiTastoNumerico, passando un carattere che indica il tasto premuto.
Private Sub Tasto1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Tasto1.Click premiTastoNumerico("1") End Sub Private Sub Tasto2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Tasto2.Click premiTastoNumerico("2") End Sub Private Sub Tasto3_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Tasto3.Click premiTastoNumerico("3") End Sub Private Sub Tasto4_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Tasto4.Click premiTastoNumerico("4") End Sub Private Sub Tasto5_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Tasto5.Click premiTastoNumerico("5") End Sub Private Sub Tasto6_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Tasto6.Click premiTastoNumerico("6") End Sub Private Sub Tasto7_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Tasto7.Click premiTastoNumerico("7") End Sub Private Sub Tasto8_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Tasto8.Click premiTastoNumerico("8") End Sub Private Sub Tasto9_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Tasto9.Click premiTastoNumerico("9") End Sub Private Sub Tasto0_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Tasto0.Click premiTastoNumerico("0") End SubEcco quindi il codice del metodo premiTastoNumerico. Questo metodo accoda la corrispondente cifra, ricevuta come parametro, sposta nuovamente il focus sul display e posiziona il cursore nel punto più a destra dell'ultima cifra inserita:
Private Sub premiTastoNumerico(ByVal tastoNumerico As String) Display.Text = Display.Text & tastoNumerico Me.Display.Focus() Me.Display.SelectionStart = Me.Display.Text.Length End SubUn frammento di codice interessante è quello riportato nel seguito. Il gestore dell'evento KeyPress del display permette di verificare, mentre il focus è impostato sul display, se il tasto premuto sia uno di quelli relativi a cifre numeriche. Nel caso la verifica fosse positiva, sarà accodata la cifra al valore già presente nel display.
Questo metodo realizza una funzione estremamente utile: infatti permette di inserire velocemente dei numeri utilizzando il tastierino della tastiera fisica, piuttosto che obbligare l'utente a fare clic sui pulsanti numerici del tastierino presente sul form.
Si noti che il metodo chiamato nel caso di tasti numerici è lo stesso presente nei gestori degli eventi click dei corrispondenti pulsanti da zero a nove.
In questo stesso gestore dell'evento KeyPress sono state codificate le azioni da eseguire nel caso di altri tasti premuti: i tasti delle operazioni (+, -, *, /), il punto decimale, il tasto di eguaglianza (=) e perfino il tasto Invio che viene parificato all'inserimento di un segno di eguaglianza. Questo significa che per stampare il totale dell'operazione è possibile premere il tasto "=" così come è sufficiente premere Invio.
Infine, l'istruzione Case Else permette di escludere qualsiasi altro tasto premuto, per evitare che nel display venga fatto il tentativo di inserire caratteri non ammessi, come caratteri alfabetici, simboli non corretti e così via. Infatti l'istruzione e.KeyChar = "" non fa altro che "buttare via" il carattere che è stato premuto, per evitare che il gestore della pressione dei tasti di default, che viene comunque chiamato subito dopo il termine di questo codice, tenti di interpretare un tasto non ammesso.Private Sub Display_KeyPress(ByVal sender As Object, _ ByVal e As System.Windows.Forms.KeyPressEventArgs) _ Handles Display.KeyPress Dim tasto As New Control Select Case e.KeyChar Case "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" premiTastoNumerico(e.KeyChar) Case "." Call Me.PuntoDecimale_Click(sender, e) Case "+" Call Me.TastoPiu_Click(sender, e) Case "-" Call Me.TastoMeno_Click(sender, e) Case "*" Call Me.TastoPer_Click(sender, e) Case "/" Call Me.TastoDiviso_Click(sender, e) Case "=" Call Me.TastoUguale_Click(sender, e) Case Environment.NewLine Call Me.TastoUguale_Click(sender, e) Case Else e.KeyChar = "" End Select Me.Display.Focus() Me.Display.SelectionStart = Me.Display.Text.Length End SubNel tastierino numerico sono presenti altri due elementi che si riferiscono a elementi numerici che devono essere aggiunti al numero digitato e pertanto anche questo codice viene riportato qui di seguito. Il primo accoda tre zeri (per indicare le migliaia, per esempio):
Private Sub Tasto00_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Tasto00.Click Display.Text = Display.Text & "000" Me.Display.Focus() Me.Display.SelectionStart = Me.Display.Text.Length End SubIl secondo metodo accoda il simbolo del punto decimale. Si noti che il punto decimale viene accodato solo se l'indicatore flagPuntoDecimale non è già attivato (True). Nel caso tale indicatore fosse ancora impostato a False, viene aggiunto il punto decimale e impostato a True, per impedire l'inserimento di più punti decimali nello stesso valore.
Private Sub PuntoDecimale_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles PuntoDecimale.Click If flagPuntoDecimale = False Then Display.Text = Display.Text & "," flagPuntoDecimale = True End If Me.Display.Focus() Me.Display.SelectionStart = Me.Display.Text.Length End SubAltri due gestori di eventi Click permettono, rispettivamente, di eseguire le operazioni relative alla pressione dei pulsanti C e CE:
- il pulsante C permette di cancellare il contenuto del display e di annullare l'indicatore di punto decimale. Il contenuto della variabile Valore e il contenuto del nastro di carta non vengono modificati;
- il pulsante CE permette invece di cancellare il contenuto del display, della variabile Valore e del nastro di carta. In pratica la calcolatrice viene reimpostata come se fosse appena stata avviata.
Private Sub TastoC_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles TastoC.Click Display.Text = "" flagPuntoDecimale = False Me.Display.Focus() End Sub Private Sub TastoCE_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles TastoCE.Click Call cNastro.AzzeraNastro() Call cNastro.SvuotaNastro() Call cNastro.AggiornaNastro() Display.Text = "" flagPuntoDecimale = False Valore = 0 Me.Display.Focus() Me.Display.SelectionStart = Me.Display.Text.Length End SubIl gestore dell'evento Click del pulsante TastoCancellaUno (il pulsante presente sulla destra del display) verifica se il carattere meno significativo della stringa presente nel display è un punto decimale. In caso positivo, viene reimpostato a False il corrispondente indicatore. In ogni caso viene eliminato il carattere più a destra e aggiornato il display.
Private Sub TastoCancellaUno_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles TastoCancellaUno.Click If Display.Text.Substring(Display.Text.Length - 1, 1) = "," Then flagPuntoDecimale = False End If Display.Text = Display.Text.Substring(0, Display.Text.Length - 1) Me.Display.Focus() Me.Display.SelectionStart = Me.Display.Text.Length End SubNel caso vengano premuti i pulsanti relativi alle operazioni matematiche (+, -, *, /), viene effettuata una chiamata al metodo eseguiOperazione, al quale viene passato il simbolo come parametro:
Private Sub TastoPiu_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles TastoPiu.Click Call eseguiOperazione("+") End Sub Private Sub TastoMeno_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles TastoMeno.Click Call eseguiOperazione("-") End Sub Private Sub TastoPer_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles TastoPer.Click Call eseguiOperazione("*") End Sub Private Sub TastoDiviso_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles TastoDiviso.Click Call eseguiOperazione("/") End SubViene quindi riportato il codice del metodo eseguiOperazione, dove è possibile notare che nella prima parte vengono eseguite le operazioni matematiche specifiche del simbolo ricevuto come parametro e viene inserito nel nastro di carta il valore e il simbolo corrispondente, mentre nella seconda parte viene svuotato il display, reimpostato a False l'indicatore del punto decimale e spostato il focus sul display:
Private Sub eseguiOperazione(ByVal operatore As String) Select Case operatore Case "+" Valore += CType(Display.Text, Double) Call cNastro.Inserisci(Display.Text, "+") Case "-" Valore -= CType(Display.Text, Double) Call cNastro.Inserisci(Display.Text, "-") Case "*" Valore *= CType(Display.Text, Double) Call cNastro.Inserisci(Display.Text, "*") Case "/" Valore /= CType(Display.Text, Double) Call cNastro.Inserisci(Display.Text, "/") End Select Display.Text = "" flagPuntoDecimale = False Me.Display.Focus() End SubL'ultimo gestore di evento Click che interessa il tastierino numerico del form è quello relativo al tasto di eguaglianza (TastoUguale). Il codice che segue:
- chiama il metodo Inserisci dell'istanza inerente al nastro di carta;
- aggiunge il risultato anche nel display, permettendo quindi il suo riutilizzo, per esempio in una somma successiva;
- azzera il Valore, reimposta a False l'indicatore del punto decimale;
- aggiorna il nastro di carta, dopo averne cancellato il contenuto;
- infine sposta il focus sul display, posizionando il cursore a destra della stringa:
Private Sub TastoUguale_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles TastoUguale.Click Call cNastro.Inserisci(Valore.ToString, "=") Display.Text = Valore Valore = 0 flagPuntoDecimale = False Call cNastro.SvuotaNastro() Call cNastro.AggiornaNastro() Me.Display.Focus() Me.Display.SelectionStart = Me.Display.Text.Length End SubLe funzionalità di salvataggio su file e di recupero del contenuto del nastro di carta da un file di testo sono piuttosto interessanti, perché permettono di memorizzare permanentemente anche lunghe sequenze di operazioni per poterle verificare in seguito o magari per inviarle via posta elettronica a un collega.
Il salvataggio su file di testo è realizzato con il gestore dell'evento Click del pulsante TastoSalva, riportato qui di seguito. Una volta impostate le proprietà del controllo SaveFileDialog1, viene mostrata la finestra di dialogo per ottenere il nome del file su cui salvare, dopo di che viene semplicemente chiamato il metodo SalvaNastro della classe clsNastro:Private Sub TastoSalva_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles TastoSalva.Click Dim nomeFile As String = "" SaveFileDialog1.InitialDirectory = "c:\" SaveFileDialog1.FileName = "C:\prova.txt" SaveFileDialog1.Filter = "file di testo (*.txt)|*.txt" SaveFileDialog1.FilterIndex = 1 SaveFileDialog1.RestoreDirectory = True If SaveFileDialog1.ShowDialog() = Windows.Forms.DialogResult.OK Then nomeFile = Me.SaveFileDialog1.FileName cNastro.SalvaNastro(nomeFile) End If Me.Display.Focus() Me.Display.SelectionStart = Me.Display.Text.Length End SubAllo stesso modo il gestore dell'evento Click del pulsante TastoRecupera, imposta le proprietà del controllo OpenFileDialog1, apre la relativa finestra di dialogo per la scelta del file da ricaricare e chiama il metodo RecuperaNastro della classe clsNastro:
Private Sub TastoRecupera_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles TastoRecupera.Click OpenFileDialog1.InitialDirectory = "c:\" OpenFileDialog1.FileName = "C:\prova.txt" OpenFileDialog1.Filter = "file di testo (*.txt)|*.txt" OpenFileDialog1.FilterIndex = 1 OpenFileDialog1.RestoreDirectory = True If OpenFileDialog1.ShowDialog() = Windows.Forms.DialogResult.OK Then Dim nomeFile As String = Me.OpenFileDialog1.FileName cNastro.RecuperaNastro(nomeFile) End If Me.Display.Focus() Me.Display.SelectionStart = Me.Display.Text.Length End SubPer le funzionalità di anteprima di stampa è stato utilizzato un codice molto simile a quello mostrato nell'esempio del capitolo 9, nel paragrafo che ha trattato il controllo PrintPreviewDialog.
Il metodo InizializzaPrintPreviewDialog e i gestori degli eventi Click del pulsante TastoAnteprima e PrintPage dell'istanza documento, sono quindi stati adattati con poche modifiche.' inizializza il controllo Private Sub InizializzaPrintPreviewDialog() ' imposta dimensione, posizione e nome Me.PrintPreviewDialog1.ClientSize = _ New System.Drawing.Size(400, 500) Me.PrintPreviewDialog1.Location = New System.Drawing.Point(29, 29) Me.PrintPreviewDialog1.Name = "PrintPreviewDialog1" ' imposta la dimensione minima della finestra di dialogo Me.PrintPreviewDialog1.MinimumSize = _ New System.Drawing.Size(375, 250) ' imposta la proprietà UseAntiAlias a True per permettere ' al sistema operativo di utilizzare l'effetto antialiasing Me.PrintPreviewDialog1.UseAntiAlias = True End Sub Private Sub TastoAnteprima_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles TastoAnteprima.Click Me.TestoDaStampare.Text = cNastro.componiNastro ' imposta la proprietà PrintPreviewDialog.Document all'oggetto ' PrintDocument selezionato dall'utente PrintPreviewDialog1.Document = documento ' chiama il metodo ShowDialog che scatenerà l'evento ' PrintPage del documento PrintPreviewDialog1.ShowDialog() Me.Display.Focus() Me.Display.SelectionStart = Me.Display.Text.Length End SubLa modifica più consistente è quella presente appunto nel gestore dell'evento PrintPage dell'istanza documento. Infatti il testo da stampare non è, in questo caso, una stringa fissa e preimpostata, ma è il risultato ottenuto dal metodo Function formattaStampa, al quale è stato passato il contenuto della Label nascosta presente sul form:
Private Sub documento_PrintPage(ByVal sender As Object, _ ByVal e As System.Drawing.Printing.PrintPageEventArgs) _ Handles documento.PrintPage ' Qui va inserito il codice per disegnare la pagina. ' Questo codice sarà chiamato da PrintPreviewDialog.Show ' Il seguente codice prepara un semplice messaggio ' sul documento creato nella finestra di dialogo Dim text As String = formattaStampa(Me.TestoDaStampare.Text) Dim printFont As New System.Drawing.Font _ ("Courier New", 14, System.Drawing.FontStyle.Regular) e.Graphics.DrawString(text, printFont, _ System.Drawing.Brushes.Black, 500, 100) End SubInfine viene riportato proprio il metodo Function formattaStampa che si accolla l'onere di:
- prendere il testo ricevuto come parametro e distribuirne le singole righe in un array, con la stessa tecnica delle espressioni regolari già presentata;
- formattare ogni singolo elemento dell'array, aggiungendo degli spazi a sinistra, per raggiungere una lunghezza fissa complessiva di 22 caratteri;
- restituire nuovamente il testo completo, componendolo mediante accodamento di ogni singola riga con un elemento di ritorno a capo.
Private Function formattaStampa(ByVal testoNastro As String) As String Dim expreg As New Regex(Environment.NewLine) Dim arrayStampa() As String = _ Regex.Split(testoNastro, Environment.NewLine) Dim riga As String = "" Dim spazi As String = " " ' 22 spazi Dim i As Integer For i = 0 To arrayStampa.GetUpperBound(0) riga = arrayStampa(i) riga = spazi & riga riga = riga.Substring(riga.Length - 22, 22) arrayStampa(i) = riga Next testoNastro = arrayStampa(0) For i = 1 To arrayStampa.GetUpperBound(0) testoNastro &= Environment.NewLine & arrayStampa(i) Next Return testoNastro End Function End ClassLa calcolatrice funzionante è quella mostrata in Figura 12.2.
![]()
Figura 12.2 La calcolatrice a nastro funzionante.In Figura 12.3, invece, è presentata la finestra di anteprima di stampa, con zoom pari al 100%. In tale finestra è possibile modificare il fattore di zoom, scorrere le pagine e impostare la visualizzazione su più pagine (per documenti multipagina) e stampare sulla stampante di sistema predefinita.
![]()
Figura 12.3 La finestra di anteprima di stampa.NOTA DELL'AUTORE
Il presente capitolo è un capitolo inedito, in quanto escluso dalla pubblicazione per motivi di spazio. Avrebbe dovuto essere pubblicato come ultimo capitolo del libro i cui riferimenti sono riportati nel seguito della presente pagina.Su questo sito (in area Download) e su quello dell'autore (www.deghetto.it) saranno pubblicati anche tutti gli esempi del libro, compreso l'esempio di questo capitolo inedito. Gli esempi sono forniti in sorgente e in eseguibile. Per utilizzare gli esempi è necessario avere almeno una copia di Visual Basic 2005 Express.
Chi dovesse trovare errori o imprecisioni di qualunque genere è invitato gentilmente a comunicarlo a mezzo e-mail all'autore, sul forum "dotnet" di questo sito oppure sul blog dell'autore.
"VISUAL BASIC 2005 e il Framework .NET 2.0" Autore: Mario De Ghetto Numero 15 - Bimestrale, maggio 2006 - Distribuzione Parrini Collana "Guide Pratiche" di Computer Magazine Codice 9771825062009 Editore: Future Media Italy Via Asiago, 45 - 20128 Milano Tel. 02-252916.1 - fax 02-26005121 Arretrati: tel. 02-252916209 - fax 02-26005512 dal lunedì al venerdì: 9.00/12.30 - 13.30/17.30 e-mail arretrati@futureitaly.it Distribuzione per l'edicola: Parrini & C. SpA Viale Forlanini 23 - 20134 Milano Via Vitorchiano 81 - 00189 Roma