Le avventure in VB.Net di un principiante ex-VB6 - 22
a cura di Oscar Zanin e Diego Cattaruzza (requisiti: Visual Basic Express e SqlServer)Premessa
Nell'articolo precedente sono state illustrate alcune correzioni di difetti riscontrati nel lungo intervallo tra la ventesima e la ventunesima puntata, ma lo sviluppo del progetto PrimiPassi non è andato oltre l'implementazione del menu per aprire la form Clienti. In compenso, erano state fornite le immagini di altre form, praticamente uguali a quelle già sviluppate, che i lettori avrebbero dovuto provare a creare, come compito per casa.In questo articolo faremo la correzione dei compiti, illustrando le nostre versioni - questo non significa che le vostre siano sbagliate: magari avete aggiunto qualche funzionalità cui noi non abbiamo pensato, o fatto meglio qualcosa che noi abbiamo fatto peggio: avete il blog di Diego per farcelo sapere e comunicarlo alla community.
Prima delle nuove form, verranno illustrati alcuni piccoli miglioramenti alle classi Formattazione, ConversioneTipo e ConvalidaDati.
I due nuovi metodi della classe Formattazione
Per evitare errori nel parsing del dato passato al metodo Formatta, causati dalla presenza di punti separatori di migliaia o del simbolo dell'euro, sono stati aggiunti i due metodi seguenti:Public Shared Function EliminaPuntiMigliaia(ByVal valore As String) As String If valore.Contains(".") Then valore = valore.Replace(".", "") Return valore End FunctionPublic Shared Function EliminaSimboloEuro(ByVal valore As String) As String If valore.StartsWith("\'80 ") Then valore = valore.Remove(0, 2) If valore.StartsWith("-\'80 ") Then valore = valore.Remove(1, 2) Return EliminaPuntiMigliaia(valore) End FunctionIl nome dei metodi è sufficientemente autoesplicativo, né il codice richiede spiegazioni, dato che usa metodi di String ampiamente illustrati nell'Help.
Piuttosto, è interessante vedere il nuovo metodo Formatta, che è stato modificato laddove è utile far uso dei nuovi metodi:Public Shared Function Formatta(ByVal valore As String, ByVal formato As TipoFor) As String If ControlloValorizzazioneStringa.IsNullOrTrimEmpty(valore) = True Then Return valore End If Select Case formato Case TipoFor.Data 'Data Return DateTime.Parse(valore).ToString("dd/MM/yyyy") Case TipoFor.Percentuale 'Percentuale Return Single.Parse(valore).ToString("0.00") Case TipoFor.Migliaia_senza_decimali 'Migliaia senza decimali Return Long.Parse(EliminaPuntiMigliaia(valore)).ToString("#,###") Case TipoFor.Migliaia_con_decimali 'Migliaia con decimali Return Double.Parse(EliminaPuntiMigliaia(valore)).ToString("#,##0.00") Case TipoFor.Valuta 'Valuta Return Decimal.Parse(EliminaSimboloEuro(valore)).ToString("\'80 #,##0.00") Case Else Return valore End Select End FunctionI nuovi metodi risultano molto utili anche altrove, e precisamente nelle classi ConversioneTipo e ConvalidaDati.
Nella prima, oltre alla nuova direttiva per riferire la classe appema modificata:Imports APP.UI.FormattazioneVengono modificati tutti i metodi tranne StringToDate:
Public Shared Function StringToInteger(ByVal dato As String) As Integer If String.IsNullOrEmpty(dato) Then Return Integer.MaxValue Else Return Integer.Parse(EliminaPuntiMigliaia(dato)) End If End Function Public Shared Function StringToLong(ByVal dato As String) As Long If String.IsNullOrEmpty(dato) Then Return Long.MaxValue Else Return Long.Parse(EliminaPuntiMigliaia(dato)) End If End Function Public Shared Function StringToSingle(ByVal dato As String) As Single If String.IsNullOrEmpty(dato) Then Return Single.MaxValue Else Return Single.Parse(EliminaPuntiMigliaia(dato)) End If End Function Public Shared Function StringToDouble(ByVal dato As String) As Double If String.IsNullOrEmpty(dato) Then Return Double.MaxValue Else Return Double.Parse(EliminaPuntiMigliaia(dato)) End If End Function Public Shared Function StringToDecimal(ByVal dato As String) As Decimal If String.IsNullOrEmpty(dato) Then Return Decimal.MaxValue Else Return Decimal.Parse(EliminaSimboloEuro(dato)) End If End FunctionNella classe ConvalidaDati, vengono usati i nuovi metodi nei casi di parsing di stringhe numeriche:
Imports APP.UI.Formattazione ' ... omissis ... Public Class ConvalidaDati Public Shared Function Convalida(ByVal valore As String, ByVal nomeCampo As String, _ ByVal tipoCampo As TipoConv, ByVal obbligatorio As Boolean) _ As Boolean ' ... omissis ... Case TipoConv.Migliaia_senza_decimali 'Migliaia senza decimali ' ... omissis ... Longc = Long.Parse(EliminaPuntiMigliaia(valore)) ' ... omissis ... Case TipoConv.Migliaia_con_decimali 'Migliaia con decimali ' ... omissis ... Doublec = Double.Parse(EliminaPuntiMigliaia(valore)) ' ... omissis ... Case TipoConv.Valuta 'Valuta Dim Decc As Decimal Try Decc = Decimal.Parse(EliminaSimboloEuro(valore)) ' ... omissis ... 'Migliaia senza decimali maggiore di zero Case TipoConv.Migliaia_senza_decimali_maggiore_di_zero ' ... omissis ... Longc = Long.Parse(EliminaPuntiMigliaia(valore)) ' ... omissis ... 'Migliaia senza decimali maggiore o uguale a zero Case TipoConv.Migliaia_senza_decimali_maggiore_o_uguale_a_zero ' ... omissis ... Longc = Long.Parse(EliminaPuntiMigliaia(valore)) ' ... omissis ... 'Migliaia con decimali maggiore di zero Case TipoConv.Migliaia_con_decimali_maggiore_di_zero ' ... omissis ... Doublec = Double.Parse(EliminaPuntiMigliaia(valore)) ' ... omissis ... 'Migliaia con decimali maggiore o uguale a zero Case TipoConv.Migliaia_con_decimali_maggiore_o_uguale_a_zero ' ... omissis ... Doublec = Double.Parse(EliminaPuntiMigliaia(valore)) ' ... omissis ... Case TipoConv.Valuta_maggiore_di_zero 'Valuta maggiore di zero Dim Decc As Decimal Try Decc = Decimal.Parse(EliminaSimboloEuro(valore)) ' ... omissis ... Case TipoConv.Valuta_maggiore_o_uguale_a_zero 'Valuta maggiore o uguale a zero Dim Decc As Decimal Try Decc = Decimal.Parse(EliminaSimboloEuro(valore)) ' ... omissis ... End Function End ClassLa classe di dettaglio
Se avete diligentemente seguito la serie, e non scopiazzato, avrete certo notato come la form Fornitori sia molto simile alla form Client. Di conseguenza avete immaginato che, come è stato fatto per la form Clienti (vedi puntata 18), anche per la form Fornitori è necessario implementare una classe di dettaglio, di nome FornitoriRiferimenti, la cui struttura è speculare alla tabella Fornitori_Riferimenti.
Quindi, facciamo clic destro sulla cartella Dettagli nel Solution Explorer e aggiungiamo la nuova classe, il cui semplicissimo codice è il seguente (si omette il classico codice interno alle proprietà):Namespace Dettagli Public Class FornitoriRiferimenti Dim mCodice As Integer Public Property Codice() As Integer ' ... omissis ... End Property Dim mRif As String = String.Empty Public Property Rif() As String ' ... omissis ... End Property Dim mTel As String = String.Empty Public Property Tel() As String ' ... omissis ... End Property Dim mFax As String = String.Empty Public Property Fax() As String ' ... omissis ... End Property Dim mCel As String = String.Empty Public Property Cel() As String ' ... omissis ... End Property Dim mMail As String = String.Empty Public Property Mail() As String ' ... omissis ... End Property End Class End NamespaceLa form FornitoriRif
Se avete intuito di dover implementare, prima di cominciare lo sviluppo della form Fornitori, la classe di dettaglio, avete certo dedotto che è necessaria anche una form accessoria per la griglia dei riferimenti. Come avete verificato dall'immagine proposta nel 'compito per casa' affidatovi nella puntata precedente, è una form del tutto simile alla FrmClientiRif. Quindi possiamo farne una copia, preoccupandoci di cambiare il nome della classe della form e della classe di dettaglio (FornitoriRif invece di ClientiRif).
Date queste minime differenze, non si ritiene necessario esporre l'immagine e il codice della form.La form Fornitori
Come per la form Clienti, anche per la form Fornitori Oscar e Diego implementano una interfaccia differente. Nell'articolo precedente è illustrata quella di Oscar, qui potete vedere le due immagini relative a quella di Diego (fare doppio clic per vederle a grandezza naturale):![]()
![]()
Dopo aver disegnato l'interfaccia come preferite, dovreste uniformare i nomi dei controlli alle nostre versioni. Quindi, per velocizzare il disegno dell'interfaccia, dato l'alto numero di controlli, vengono forniti i file di design in due distinti file di testo, FrmFornitori.designer.vb.txt e ozFrmFornitori.designer.vb.txt.
Quanto al codice, è praticamente identico a quello della FrmClienti, con i dovuti adeguamenti (per cui non viene illustrato nuovamente: potete fare riferimento alla puntata 19). Sono allegate entrambe le versioni, così potete fare un confronto anche sugli stili di programmazione di Oscar e di Diego.
I Tipi di Pagamento e le Scadenze: concetti
La FrmTipiPag è un'altra form di tipo master-details. Anche per essa bisogna implementare una classe di dettaglio, PagamentiScadenze, e una form accessoria, FrmTipiPagScad, per insermenti e modifiche.
Per comodità, si riporta l'immagine pubblicata nella puntata precedente:![]()
Come potete intuire dalla figura, l'identificativo è la descrizione stessa del tipo di pagamento. Si possono anche indicare: l'eventuale giorno fisso giunto il quale il cliente dovrà effettuare il pagamento, l'indicazione se il pagamento sarà a fine mese e quella se il tipo di pagamento è appoggiato in banca.
Quest'ultimo campo deve essere spuntato nei casi in cui il pagamento deve "passare" per la banca dell'utente, come per esempio nel caso di un bonifico bancario. Così facendo, nelle fatture, si controlla il valore del campo booleano e se è vero si propongono automaticamente i dati bancari dell'utente (presi da una tabella dei parametri di programma).
La parte dettaglio è costituita dalla griglia delle scadenze, che hanno la funzione di indicare a quanti giorni e in che percentuale effettuare le tranche di pagamento. Se per un pagamento in rimessa diretta ha poco senso (si devono infatti impostare i giorni a 0 e la percentuale a 100), ne ha molto più nel caso esemplificato in figura.
Gestendo correttamente le scadenze si potrà costruire uno scadenziario dei pagamenti.
La classe PagamentiScadenze
La classe PagamentiScadenze riflette la struttura della tabella Pagamenti_dettaglio (vedi puntata 18) e va posta nella cartella Dettagli.Namespace Dettagli Public Class PagamentiScadenze Dim mPagam As String = String.Empty Public Property Pagam() As String ' ... omissis ... End Property Dim mScad As Integer Public Property Scadenza() As Integer ' ... omissis ... End Property Dim mPerc As Single Public Property Perc() As Single ' ... omissis ... End Property End Class End NamespaceLa form FrmTipiPagScad
Questa è l'immagine in fase di progettazione (con la LblPagam che è invisibile nella figura pubblicata nella puntata precedente).![]()
Del codice, del tutto simile alle altre form accessorie già viste, si riporta solo una parte significativa del metodo di controllo della validità dei dati:
If Single.Parse(TxtPerc.Text) > 100 Then Messaggi.Avviso("La percentuale non può essere superiore a 100 !", "Attenzione") Return False End IfLa form FrmTipiPag
Di questa form, il cui codice è quasi identico alle altre form master-detail già implementate, vale la pena evidenziare il metodo DatiValidi:Private Function DatiValidi() As Boolean If Convalida(TxtPagam.Text, "Tipo di pagamento", TipoConv.Testo, True) = False Then Return False End If If Convalida(TxtGiornoFisso.Text, "Giorno fisso", _ TipoConv.Migliaia_senza_decimali_maggiore_di_zero, False) = False Then Return False End If If DgvScad.RowCount < 1 Then Messaggi.Avviso("Per salvare deve essere presente almeno una scadenza di pagamento. " & _ "Si ricorda che per le rimesse dirette a vista si dovrà inserire una scadenza di " & _ "pagamento a zero giorni con percentuale di pagamento al 100%.", "Attenzione") Return False End If 'Controllo che il totale delle percentuali di pagamento sia uguale a 100 Dim totperc As Single = 0 For i As Integer = 0 To DgvScad.RowCount - 1 totperc += Convert.ToSingle(DgvScad.Rows(i).Cells("Percentuale").Value) Next i If totperc <> 100 Then Messaggi.Avviso( _ "Per salvare il totale delle percentuali di pagamento deve essere uguale a 100.", _ "Attenzione") Return False End If Return True End FunctionIl testo dei messaggi di avviso dovrebbe essere sufficiente a capire il resto del codice della procedura.
Conclusione
In questo articolo sono stati illustrati alcuni piccoli miglioramenti alle classi Formattazione, ConversioneTipo e ConvalidaDati, nonché le form master-detail con relative form accessorie e classi di dettaglio, oggetto dei compiti per casa assegnati nella puntata precedente.
Nella prossima puntata si completeranno i menu relativi alle form illustrate in questo articolo e si proporrà altro codice di perfezionamento.Il codice di PrimiPassi sviluppato fino a questo momento è come al solito disponibile in area download.
Anche per questa puntata, Diego mette a disposizione nel suo blog un post cui scrivere critiche, suggerimenti, richieste di chiarimento.