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

Premessa
Dopo la critica di Diego alla form di ricerca di Oscar - argomento del precedente articolo - c'è voluto un po' di tempo perché fosse pronta la correzione di Oscar e ancora di più tempo a Diego per le proprie considerazioni, poiché nel frattempo vari impegni accavallatisi hanno impedito una sollecita pubblicazione di questo articolo.

In questa puntata verrà illustrata la parte della FrmRicerca che interagisce con le altre form, in modo da terminare lo sviluppo della FrmStati.

La logica di funzionamento
La FrmRicerca è una finestra di dialogo che deve restituire alla form chiamante una lista di stringhe contenente i valori dei campi chiave necessari alla visualizzazione del record selezionato in fase di ricerca dall'utente.

La FrmRicerca espone una serie di caselle combinate (di numero e contenuto variabili) tramite le quali impostare una ricerca i cui risultati vengono esposti in una griglia.
L'utente sceglie (o no) un record, in base al quale viene compilata la lista dei campi chiave.

Come tutte le finestre di dialogo, FrmRicerca deve restituire un DialogResult. Quindi bisogna predisporre una proprietà per la lista dei campi chiave. La form chiamante, quando il controllo del programma torna ad essa alla chiusura della FrmRicerca, legge questa proprietà per i propri scopi (sempre che il DialogResult sia DialogResultOk).

La FrmRicerca deve funzionare per ciascuna form del progetto che ne possa aver bisogno, quindi deve essere in grado di autoconfigurarsi in base alla form chiamante. Occorre cioè un insieme di informazioni che la form chiamante possa preparare e poi passare alla FrmRicerca affinché questa possa autoconfigurarsi. Questo insieme costituisce la struttura InfoRicerca.

In base all'analisi svolta da Diego nel precedente articolo, Oscar ha modificato la struttura propostagli da Diego aggiungendo qualche membro e migliorandone l'impostazione e l'uso.
Questa struttura va posta nello stesso modulo della FrmRicerca, certamente il posto più adatto. Non è cioè il caso di creare un file a parte. Quindi creiamo la base della FrmRicerca. La completeremo in seguito.

La FrmRicerca - prima parte
In Esplora soluzioni clicchiamo con il tasto destro sul progetto PrimiPassi, selezioniamo la voce Aggiungi e quindi Nuovo elemento. Nella finestra che appare, nella sezione Modelli personali selezioniamo Krypton Form, assegnamo come nome FrmRicerca e clicchiamo sul pulsante Aggiungi.

Per il momento basta così. Le Label, le ComboBox e la DataGridView verranno aggiunte in fase di esecuzione, sulla base della struttura di informazioni che stiamo per implementare.

La struttura InfoRicerca
Per non creare confusione con il codice pertinente la form di ricerca, faremo buon uso delle Region e, per fornire a Intellisense le più ampie possibilità di aiutarci, commenteremo ogni membro (nel contempo, i commenti stessi aiutano nella comprensione dell'articolo):

Public Class FrmRicerca

End Class

#Region "RicercaHelper"

#Region "Strutture"

''' <summary>
''' Inforicerca: struttura principale di informazioni per la form di ricerca
''' </summary>
''' <remarks>fa uso di altre strutture</remarks>
Public Structure InfoRicerca
  ''' <summary>
  ''' Titolo per la FrmRicerca
  ''' </summary>
  Dim TitoloForm As String
  ''' <summary>
  ''' Nome della tabella di riferimento
  ''' </summary>
  Dim NomeTabella As String
  ''' <summary>
  ''' Ordinamento dei dati visualizzati nella griglia
  ''' </summary>
  Dim Ordinamento As String
  ''' <summary>
  ''' Insieme delle strutture di informazioni per i criteri di filtro
  ''' </summary>
  ''' <remarks>vedi struttura Criterio</remarks>
  Dim Criteri As List(Of Criterio)
  ''' <summary>
  ''' Insieme delle strutture di informazioni per le colonne della griglia
  ''' </summary>
  ''' <remarks>vedi struttura Colonna</remarks>
  Dim Colonne As List(Of Colonna)
End Structure

Nella Region RicercaHelper distinguiamo una Region Strutture, perché la struttura InfoRicerca usa altre due strutture, una per le informazioni relative alle Combobox per i criteri di selezione e l'altra per quelle relative alle colonne della DataGridView.

''' <summary>
''' Criterio: struttura informazioni per le combobox e le label 
''' con le quali si impostano i criteri di filtro
''' </summary>
''' <remarks>fa uso di due enumerati: TipoOrigineDati e TipoConfronto</remarks>
Public Structure Criterio
  ''' <summary>
  ''' Testo della Label descrittiva del criterio
  ''' </summary>
  ''' <remarks></remarks>
  Dim Etichetta As String
  ''' <summary>
  ''' Tipo di origine dei dati per l'elenco della combobox
  ''' </summary>
  ''' <remarks>vedi enumerato TipoOrigineDati. 
  ''' Se Filtro, Valori deve avere un elemento; 
  ''' se ElencoValori, Valori deve avere questo elenco</remarks>
  Dim OrigineDati As TipoOrigineDati
  ''' <summary>
  ''' Nome del campo i cui valori vengono visualizzati
  ''' </summary>
  Dim NomeCampoVisualizzato As String
  ''' <summary>
  ''' Nome del campo i cui valori vengono utilizzati
  ''' </summary>
  Dim NomeCampoValore As String
  ''' <summary>
  ''' Tipo Sql del campo
  ''' </summary>
  Dim TipoCampoValore As SqlDbType
  ''' <summary>
  ''' Indica se deve essere validata la scelta fatta tramite la ComboBox
  ''' </summary>
  Dim ControlloValidita As Boolean
  ''' <summary>
  ''' Tipo di confronto per l'operazione di filtro
  ''' </summary>
  ''' <remarks>vedi enumerato TipoConfronto</remarks>
  Dim Confronto As TipoConfronto
  ''' <summary>
  ''' Elementi dell'elenco della Combobox, se OrigineDati è diverso da DatiTabella
  ''' </summary>
  ''' <remarks>Opzionale. Se OrigineDati è Filtro, Valori deve avere un elemento; 
  ''' se OrigineDati è ElencoValori, Valori deve avere questo elenco</remarks>
  Dim Valori As String()

  Sub New(ByVal etichetta As String, ByVal origineDati As TipoOrigineDati, _
          ByVal nomeCampoVisualizzato As String, ByVal nomeCampoValore As String, _
          ByVal tipoCampoValore As SqlDbType, ByVal controlloValidita As Boolean, _
          ByVal confronto As TipoConfronto, Optional ByVal valori As String() = Nothing)

    Me.Etichetta = etichetta
    Me.OrigineDati = origineDati
    Me.NomeCampoVisualizzato = nomeCampoVisualizzato
    Me.NomeCampoValore = nomeCampoValore
    Me.TipoCampoValore = tipoCampoValore
    Me.ControlloValidita = controlloValidita
    Me.Confronto = confronto
    Me.Valori = valori
  End Sub
End Structure

Come si può notare, sono stati implementati anche due enumerati, che vedremo in seguito, dopo la terza struttura:

''' <summary>
''' Colonna: struttura informazioni per la griglia
''' </summary>
Public Structure Colonna
  ''' <summary>
  ''' Nome del campo esposto nella colonna
  ''' </summary>
  Dim NomeCampo As String
  ''' <summary>
  ''' Intestazione della colonna
  ''' </summary>
  Dim Intestazione As String
  ''' <summary>
  ''' Larghezza della colonna
  ''' </summary>
  Dim Larghezza As Integer
  ''' <summary>
  ''' Indica se la colonna deve essere di tipo CheckBox
  ''' </summary>
  Dim CheckBox As Boolean
  ''' <summary>
  ''' Indica se la colonna deve essere visibile
  ''' </summary>
  Dim Visibile As Boolean
  ''' <summary>
  ''' Indica se il valore deve essere restituito nel vettore per la form chiamante
  ''' </summary>
  ''' <remarks>E' importante indicare come chiave ogni campo restituito alla form chiamante,
  ''' Non necessariamente solo quelli della chiave univoca della tabella</remarks>
  Dim Chiave As Boolean
  ''' <summary>
  ''' Tipo Sql del campo
  ''' </summary>
  Dim TipoCampo As SqlDbType

  Sub New(ByVal nomeCampo As String, ByVal intestazione As String, ByVal larghezza As Integer, _
          ByVal checkBox As Boolean, ByVal visibile As Boolean, ByVal chiave As Boolean, _
          ByVal tipoCampo As SqlDbType)

    Me.NomeCampo = nomeCampo
    Me.Intestazione = intestazione
    Me.Larghezza = larghezza
    Me.CheckBox = checkBox
    Me.Visibile = visibile
    Me.Chiave = chiave
    Me.TipoCampo = tipoCampo
  End Sub
End Structure

#End Region

Per le due ultime strutture è più che opportuno predisporre un costruttore. L'ultimo parametro del costruttore della struttura Criterio è opzionale, per evidenti ragioni.
Oltre alla Region Strutture, c'è una Region Enumerati, di cui si fa uso nella struttura Criterio.

#Region "Enumerati"

''' <summary>
''' Tipo di origine dei dati per l'elenco della combobox
''' </summary>
''' <remarks>usato nella struttura Criterio</remarks>
Public Enum TipoOrigineDati
  ''' <summary>
  ''' l'unico elemento nel parametro Valori fa da filtro fisso
  ''' </summary>
  ''' <remarks>la combobox è disabilitata</remarks>
  Filtro
  ''' <summary>
  ''' gli elementi della lista della combobox sono elencati nel parametro Valori
  ''' </summary>
  ElencoValori
  ''' <summary>
  ''' gli elementi della lista della combobox sono tratti da una tabella
  ''' </summary>
  DatiTabella
End Enum

''' <summary>
''' Tipo di confronto per l'operazione di filtro
''' </summary>
''' <remarks>in base ad esso si costruiscono le clausole WHERE</remarks>
Public Enum TipoConfronto
  Uguale
  Maggiore
  MaggioreUguale
  Minore
  MinoreUguale
End Enum

#End Region

#End Region

La FrmRicerca - seconda parte
Adesso possiamo rifinire la FrmRicerca aggiungendo le direttive Imports e il costruttore necessario:

#Region "Dichiarazione degli imports"
Imports APP.Base
Imports APP.Data
Imports APP.Data.ConvalidaDati
Imports APP.UI
Imports System.Data.SqlClient
#End Region

APP.Base serve per accedere al metodo ValQ, App.Data per accedere alla ConnessioneDatabase e a metodi della classe ConvalidaDati, APP.UI per la classe Messaggi.
Ed ecco lo scheletro della FrmRicerca:

Public Class FrmRicerca

#Region "Variabili private"
  Private mInfoRicerca As InfoRicerca
#End Region

#Region "Proprietà"
  Private mCampiChiave As New List(Of String)
  Public ReadOnly Property CampiChiave() As List(Of String)
    Get
      Return mCampiChiave
    End Get
  End Property
#End Region

#Region "Costruttori"

  Private Sub New()
    InitializeComponent()
  End Sub

  Public Sub New(ByVal inforicerca As InfoRicerca)
    InitializeComponent()

    mInfoRicerca = inforicerca
    Me.Text = mInfoRicerca.TitoloForm

    PreparaCriteri()
    PreparaGriglia()
  End Sub

#End Region

#Region "Metodi di servizio"

  Private Sub PreparaCriteri()
    '
  End Sub

  Private Sub PreparaGriglia()
    '
  End Sub

#End Region

End Class

Il primo costruttore - quello di default, che sostituisce quello presente nel file FrmRicerca.Designer.vb, da cancellare - viene imposto privato per renderlo inaccessibile e quindi per rendere obbligatorio l'uso del secondo, quello che richiede il parametro InfoRicerca, che è indispensabile. Nel codice, si valorizza la relativa variabile privata (mInfoRicerca), si imposta il titolo della form e si completa la form con la creazione degli altri controlli (Label e ComboBox per i criteri secondo cui impostare la ricerca, la DataGridView per mostrare i risultati della ricerca impostata).

La FrmRicerca espone la proprietà di sola lettura CampiChiave, destinata a contenere i dati richiesti dalla form chiamante.

La FrmStati - Richiamo della FrmRicerca
A questo punto abbiamo predisposto tutto quel che serve per terminare lo sviluppo della FrmStati.
E' il momento infatti di implementare il codice per il pulsante BtnCerca:

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

    Dim ir As InfoRicerca

    ir.TitoloForm = "Ricerca Stato"
    ir.NomeTabella = "Stati"
    ir.Ordinamento = "Stato"
    ir.Criteri = New List(Of Criterio)
    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("Stato", "Stato", 290, False, True, True, SqlDbType.VarChar))

    Dim frm As New FrmRicerca(ir)
    Dim risposta As DialogResult = frm.ShowDialog()
    Dim campiChiave As List(Of String)

    If risposta = Windows.Forms.DialogResult.OK Then
      campiChiave = frm.CampiChiave
    Else
      Exit Sub
    End If
    If campiChiave.Count = 0 Then
      Exit Sub
    End If

    Dati.ResettaValoriChiavi()
    For i As Integer = 0 To campiChiave.Count - 1
      Dati.AggiungiValoreChiave(campiChiave(i).ToString)
    Next

    Dati.TrovaRecord()
    VisualizzaDati()

  End Sub

Come si può vedere, si imposta una struttura InfoRicerca, poi la si passa all'istanza della FrmRicerca, prima di aprire questa come dialogo.
Se al ritorno si ha un DialogResult.OK, la lista dei campi chiave per trovare il record che si cerca dovrebbe essere popolata, nel qual caso adoperiamo i già noti metodi della classe Tabella per giungere a visualizzare i dati richiesti.

Naturalmente, non è proprio con FrmStati che si può apprezzare la funzionalità della struttura InfoRicerca e della FrmRicerca, dato che abbiamo a che fare con un solo campo, ma a questo punto della serie è importante capire l'interazione tra questa fondamentale form di dialogo e le altre form del progetto.

Conclusione
In questa puntata è stata illustrata la parte della FrmRicerca che interagisce con le altre form, in modo da terminare lo sviluppo della FrmStati.

Nella prossima puntata si concluderà l'illustrazione della FrmRicerca.

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.