Le avventure in VB.Net di un principiante ex-VB6 - 16
a cura di Oscar Zanin e Diego Cattaruzza (requisiti: Visual Basic Express e SqlServer)

Premessa
Nell'articolo precedente si era terminato lo sviluppo della FrmRicerca. In questo articolo si esporrà lo sviluppo della FrmBanche, un'altra delle form associate a una sola tabella del database, mostrando solo le differenze tra questa e la FrmStati, in modo da far capire quanto poco cambia, essendo invariati i fondamenti strategici.
In seguito, si proprorrà lo sviluppo di altre due form, come 'compito a casa'.

Il disegno della FrmBanche
Come ormai si dovrebbe aver imparato, si aggiunge una Kripton Form al progetto PrimiPassi, con il nome FrmBanche.
L'aspetto finale, terminato il disegno, dovrà essere simile a quello esposto in figura.

La prima, ovvia, differenza con la FrmStati è costituita dal numero dei campi della tabella e quindi dal numero dei controlli presenti sulla form:

Il codice della FrmBanche
Le differenze tra questa form e FrmStati consistono, sostanzialmente, nell'implementazione del codice relativo ai diversi controlli presenti (molte più TextBox, e una ComboBox in più).

Innanzitutto, consideriamo la CmbStato, che richiede non solo di essere caricata all'inizio, ma anche di essere aggiornata, qualora i dati presenti in tabella Stati dovessero cambiare. Per questo motivo, il suo popolamento avviene a ogni attivazione della FrmBanche, cioè nella gestione dell'evento Activated, non in quella dell'evento Load).

  Private Sub FrmBanche_Activated(ByVal sender As System.Object, _
                                  ByVal e As System.EventArgs) Handles MyBase.Activated
    PopolaCombo()
  End Sub

Supponiamo di star inserendo una nuova banca sita in un nuovo stato nato dall'ennesima scissione dell'ex Jugoslavia, che non esiste ancora nella tabella Stati. Apriamo la ComboBox e naturalmente non lo troviamo. Dobbiamo inserirlo, pertanto apriamo la form degli Stati, aggiungiamo il nuovo record, torniamo alla form delle banche che ha l'inserimento in corso e questo scatena nuovamente l'evento Activated, che richiama il metodo PopolaCombo che ricompila la ComboBox con i dati aggiornati (nuovo stato compreso). Però, se non fossimo al primo accesso alla form e il ComboBox contenesse un valore, questo verrebbe perso a seguito del ripopolamento. Occorre quindi prevenire questa evenienza depositando l'eventuale valore in una apposita variabile, valoreAttuale, per riassegnarlo alla fine dell'operazione.

  Private Sub PopolaCombo()
    Dim dstStati As DataSet
    Dim adpStati As SqlDataAdapter
    Dim cmdStati As SqlCommand

    Dim valoreAttuale As String = CmbStato.Text

    cmdStati = New SqlCommand()
    cmdStati.Connection = SqlHelper.ConnessioneDatabase
    cmdStati.CommandText = "SELECT Stato FROM Stati ORDER BY Stato"
    adpStati = New SqlDataAdapter(cmdStati)
    dstStati = New DataSet
    adpStati.Fill(dstStati, "Stato")

    CmbStato.DataSource = dstStati.Tables(0)
    CmbStato.DisplayMember = "Stato"
    CmbStato.ValueMember = "Stato"

    CmbStato.Text = valoreAttuale
  End Sub

La FrmBanche è ovviamente associata alla tabella Banche, che è ordinata per Banca e Agenzia, quindi, nella gestione dell'evento Load si hanno queste istruzioni:

  Private Sub FrmBanche_Load(ByVal sender As System.Object, _
                           ByVal e As System.EventArgs) Handles MyBase.Load

    ' ...
    Dati = New Tabella(ConnessioneDatabase, "Banche")

    Dati.NomeCampoModifica = "Modifica"
    Dati.Ordinamento = "Banca, Agenzia"
    ' ...
  End Sub

La gestione del cambiamento di focus tra i controlli - nella quale viene scatenato l'evento ActiveControlChanged per impostare il messaggio di informazione nella barra di stato della FrmMain - deve ovviamente tener conto del maggior numero di controlli e prevedere l'informazione adeguata per ciascuno di essi. Si implementano quindi diversi metodi (di cui si presentano, per brevità, solo le firme):

  Private Sub Pulsante_MouseEnter(ByVal sender As Object, ByVal e As System.EventArgs) _
                                  Handles BtnAggiorna.MouseEnter, BtnAnnulla.MouseEnter, _
                                  BtnCancella.MouseEnter, BtnCerca.MouseEnter, _
                                  BtnConferma.MouseEnter, BtnEsci.MouseEnter, _
                                  BtnModifica.MouseEnter, BtnNuovo.MouseEnter, BtnPrec.MouseEnter, _
                                  BtnPrimo.MouseEnter, BtnSblocca.MouseEnter, BtnSucc.MouseEnter, _
                                  BtnUltimo.MouseEnter
    '...
  End Sub
  Private Sub Pulsante_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) _
                                  Handles BtnAggiorna.MouseLeave, BtnAnnulla.MouseLeave, _
                                  BtnCancella.MouseLeave, BtnCerca.MouseLeave, _
                                  BtnConferma.MouseLeave, BtnEsci.MouseLeave, BtnNuovo.MouseLeave,  _
                                  BtnModifica.MouseLeave, BtnPrec.MouseLeave, BtnPrimo.MouseLeave, _
                                  BtnSblocca.MouseLeave, BtnSucc.MouseLeave, BtnUltimo.MouseLeave
    '...
  End Sub
  Private Sub CaselleDiTesto_Enter(ByVal sender As Object, ByVal e As System.EventArgs) _
                                  Handles TxtBanca.Enter, TxtAgenzia.Enter, _
                                  TxtAbi.Enter, TxtCab.Enter, TxtSwift.Enter, TxtInd.Enter, _
                                  TxtCap.Enter, TxtCitta.Enter, TxtProv.Enter, TxtTel.Enter, _
                                  TxtFax.Enter
    '...
  End Sub
  Private Sub ElenchiATendina_Enter(ByVal sender As Object, _
                                    ByVal e As System.EventArgs) Handles CmbStato.Enter
    '...
  End Sub

Sostanzialmente, si deve tener conto della diversa origine dei dati. Spesso la modifica è semplice, consistendo solo nel nome dei controlli, come in BtnNuovo_Click e nei metodi VisualizzaDati e CaricaRiga (che qui si omettono, per brevità):

  Private Sub BtnNuovo_Click(ByVal sender As System.Object, _
                           ByVal e As System.EventArgs) Handles BtnNuovo.Click
    ' ...
    TxtBanca.Select()
    ' ...
  End Sub

Ma talvolta bisogna considerare la rilevanza dei dati:

  Private Sub BtnModifica_Click(ByVal sender As System.Object, _
                                ByVal e As System.EventArgs) Handles BtnModifica.Click
    ' ...
    TxtAbi.Select()
    TxtBanca.ReadOnly = True
    TxtAgenzia.ReadOnly = True
    ' ...
  End Sub

Si sposta il focus sul primo controllo editabile, impedendo l'edizione dei controlli relativi ai campi chiave.
Per altro verso, la si permette, laddove serve:

  Private Sub BtnConferma_Click(ByVal sender As System.Object, _
                              ByVal e As System.EventArgs) Handles BtnConferma.Click
    ' ...
    TxtBanca.ReadOnly = False
    TxtAgenzia.ReadOnly = False
    ' ...
  End Sub

  Private Sub BtnAnnulla_Click(ByVal sender As System.Object, _
                               ByVal e As System.EventArgs) Handles BtnAnnulla.Click
    ' ...
    TxtBanca.ReadOnly = False
    TxtAgenzia.ReadOnly = False
    ' ...
    TxtBanca.ReadOnly = False
    TxtAgenzia.ReadOnly = False
  End Sub

Si ragiona in modo analogo, quando si controlla la validità dell'immissione di dati:

  Private Function DatiValidi() As Boolean
    If ConvalidaDati.Convalida(TxtBanca.Text, "Banca", _
                               ConvalidaDati.TipoConv.Testo, True) = False Then
      Messaggi.Avviso("Il campo Banca non può essere lasciato vuoto !", "Attenzione")
      Return False
    End If
    If ConvalidaDati.Convalida(TxtAgenzia.Text, "Agenzia", _
                               ConvalidaDati.TipoConv.Testo, True) = False Then
      Messaggi.Avviso("Il campo Agenzia non può essere lasciato vuoto !", "Attenzione")
      Return False
    End If
    Return True
  End Function

In questa form il pulsante di modifica ha un senso, poiché ci sono campi modificabili (la FrmStati aveva un unico campo, non modificabile) quindi ne gestiamo l'abilitazione, dove occorre:

  Private Sub AbilitaPulsanti(ByVal flag As Boolean)
    ' ...
    BtnModifica.Enabled = flag
    ' ...
  End Sub

L'impostazione della ricerca richiede l'intervento più ragionato (bisogna cioè fare attenzione ai parametri da passare alla form di ricerca):

  Private Sub BtnCerca_Click(ByVal sender As System.Object, _
                             ByVal e As System.EventArgs) Handles BtnCerca.Click
    Dim ir As InfoRicerca

    ir.TitoloForm = "Ricerca Banche"
    ir.SorgenteDati = "Banche"
    ir.Ordinamento = "Banca, Agenzia"
    ir.Criteri = New List(Of Criterio)
    ir.Criteri.Add(New Criterio("Banca", TipoOrigineDati.DatiTabella, "Banca", "Banca", _
                                SqlDbType.VarChar, False, TipoConfronto.Uguale))
    ir.Criteri.Add(New Criterio("Agenzia", TipoOrigineDati.DatiTabella, "Agenzia", "Agenzia", _
                                SqlDbType.VarChar, False, TipoConfronto.Uguale))
    ir.Criteri.Add(New Criterio("ABI", TipoOrigineDati.DatiTabella, "ABI", "ABI", _
                                SqlDbType.VarChar, False, TipoConfronto.Uguale))
    ir.Criteri.Add(New Criterio("CAB", TipoOrigineDati.DatiTabella, "CAB", "CAB", _
                                SqlDbType.VarChar, False, TipoConfronto.Uguale))
    ir.Criteri.Add(New Criterio("Città", TipoOrigineDati.DatiTabella, "Citta", "Citta", _
                                SqlDbType.VarChar, False, TipoConfronto.Uguale))
    ir.Criteri.Add(New Criterio("Stato", TipoOrigineDati.DatiTabella, "Stato", "Stato", _
                                SqlDbType.VarChar, False, TipoConfronto.Uguale))
    ir.Colonne = New List(Of Colonna)
    ir.Colonne.Add(New Colonna("Banca", "Banca", 290, False, True, True, SqlDbType.VarChar))
    ir.Colonne.Add(New Colonna("Agenzia", "Agenzia", 290, False, True, True, SqlDbType.VarChar))
    ir.Colonne.Add(New Colonna("Citta", "Città", 290, False, True, False, SqlDbType.VarChar))
    ir.Colonne.Add(New Colonna("Stato", "Stato", 290, False, True, False, SqlDbType.VarChar))

Come si può constatare, a parte la novità della ComboBox e della gestione del suo popolamento, tutto il resto del codice di FrmBanche è concettualmente simile a quello di FrmStati. Quindi dovreste essere in grado di implementare da voi quello delle prossime form, di cui forniamo solo le specifiche di disegno.

La form Unità di misura - disegno
L'aspetto finale che dovrà avere la form è il seguente:

La form Prodotti - disegno
L'aspetto finale che dovrà avere la form è il seguente:

In questa form avete la novità delle CheckBox e il fatto che il campo della quantità a magazzino è di sola lettura.
Le prime rappresentano uno stato di visibilità del singolo prodotto nel resto del programma; il secondo è di sola lettura in quanto il valore viene modificato da altre form tipo quella dei carichi a magazzino e dei D.d.t.
Un prodotto potrebbe essere proposto solo nei carichi di magazzino se si è spuntato solo la prima CheckBox, oppure viceversa potrebbe essere proposto nelle fatture e nella stampa dell'anagrafica dei prodotti se si spuntano la seconda e la terza CheckBox e non apparire nel carico di magazzino.

Conclusione
In questo articolo è stato illustrato lo sviluppo della FrmBanche, la seconda delle form del progetto PrimiPassi associate a una sola tabella del database, e solo il disegno di altre due form, FrmUnMis e FrmProdotti, delle quali sono state fornite solo le specifiche per il disegno, mentre lo sviluppo del codice è assegnato come 'compito a casa'.

In seguito, quando verrà fornito anche il codice, potrete confrontare il vostro col nostro e imparare dalle differenze, se ce ne darete conto nel companion post che accompagnerà quella puntata.

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.