Le avventure in VB.Net di un principiante ex-VB6 - 12
a cura di Oscar Zanin e Diego Cattaruzza (requisiti: Visual Basic Express e SqlServer)Premessa
Nell'articolo precedente non è stata illustrata l'implementazione del codice per richiamare la form di ricerca, perché a una prima analisi Diego aveva riscontrato diversi elementi nel codice originale di Oscar che non gli piacevano, a cominciare proprio dal modo di chiamare la form. Così, ha approfondito l'analisi e ha scoperto altri difetti.In questo articolo Diego critica il lavoro di Oscar - che, si ribadisce, funziona perfettamente - cercando di evidenziarne l'errore di fondo, poiché ritiene possibile che questo errore sia piuttosto comune.
Pertanto, tutto il codice mostrato in questo articolo, benché funzionante, non troverà posto nella nostra applicazione PrimiPassi, ma ne verrà scritto altro, che sarà argomento della prossima puntata.
La logica di funzionamento
Nel codice originale di Oscar, la FrmRicerca è una finestra di dialogo che riceve un parametro di impostazione e espone un metodo che 'apre' la form stessa e restituisce la lista dei campi chiave con i quali identificare il record cercato e mostrarlo sulla form chiamante.Private Sub btnCerca_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles btnCerca.Click Dim frm As New frmRicerca Dim campiChiave As List(Of String) 'Indico alla form di ricerca quale è la form chiamante frm.TipoRicerca = "stati" 'Aprire finestra di ricerca campiChiave = frm.ApriRicerca()Segue l'utilizzo dei metodi ResettaValoriChiavi, AggiungiValoreChiave e TrovaRecord dell'oggetto Tabella sotteso dalla form chiamante, di cui infine si esegue il metodo VisualizzaDati.
Il metodo ApriRicerca, nella sua forma più semplice, contiene il codice seguente:Public Function ApriRicerca() As List(Of String) Me.ShowDialog() Return CampiChiave End FunctionCiò che non va bene, in questa impostazione, non è l'uso di un metodo di FrmRicerca per farsi restituire un valore, come qualcuno potrebbe pensare, ma il fatto che non viene considerato che FrmRicerca è una form di dialogo, che restituisce quindi un DialogResult, del cui valore bisogna tenere conto. Infatti, l'utente potrebbe anche 'trovare' un record, ma non volere che poi esso venga visualizzato nella form chiamante. Con il codice di Oscar, viene sempre restituito un risultato (magari vuoto, ed è questo l'unico caso considerato) da passare ai metodi di Tabella sopraindicati.
Inoltre, viene impostata la proprietà TipoRicerca subito dopo l'istanza di FrmRicerca, dato che questo valore è discriminante per l'impostazione della form. Dato che questo valore è necessario e indispensabile, è meglio che esso venga passato come parametro del costruttore della FrmRicerca.
Secondo me, bisogna che la FrmRicerca esponga una proprietà (di sola lettura per impedire eventuali interventi esterni) con il risultato, che verrà letto dalla form chiamante se il DialogResult è Ok. A esempio:
Public ReadOnly Property CampiChiave() As List(Of String) Get Return mCampiChiave End Get End PropertyI componenti della FrmRicerca
Oscar intende centralizzare nella FrmRicerca la ricerca di un record per tutte le form del progetto, quindi ha cercato di renderla il più versatile possibile. A seguito del valore impostato dalla form chiamante nella proprietà TipoRicerca, la FrmRicerca si autoconfigura, partendo dalla presenza iniziale dei seguenti componenti:
- una barra con quattro pulsanti (esci, imposta filtro, resetta filtro, conferma);
- sei coppie Label ComboBox per sei possibili criteri di filtro da impostare. In base alle necessità, vengono resi invisibili i controlli che non servono;
- una griglia della quale vengono impostate le colonne per visualizzare i dati.
In teoria, il numero massimo dei criteri impostabili (sei) potrebbe essere un limite, ma non è ragionevolmente prevedibile che venga superato, quindi è accettabile l'impostazione a priori dei controlli in fase di progettazione, anziché la creazione degli stessi in fase di esecuzione, ma sarebbe più economico segure quest'ultimo sistema.
L'utente compila uno o più caselle combinate, clicca sul pulsante di filtro e la griglia viene riempita con tutti i record che corrispondono ai criteri impostati. L'utente sceglie una riga e clicca sul pulsante di conferma. Viene così composta (e restituita alla form chiamante) una lista di stringhe che rappresentano l'insieme dei valori dei campi chiave necessari all'individuazione di un preciso record.
C'è anche la possibilità di passare alla FrmRicerca un ulteriore parametro ovvero un campo di filtro preventivo che valorizzerà la prima ComboBox disabilitandola all'utente, costituendo quindi un vincolo a qualsiasi filtro l'utente voglia poi applicare. Questa alternativa si rende utile, a esempio, quando, data una banca, si deve cercarne una agenzia.Public Function ApriRicerca(ByVal filtro As String) As List(Of String) Me.Filtro = filtro Me.ShowDialog() Return CampiChiave End FunctionL'autoconfigurazione
La configurazione 'su misura' viene impostata nella gestione dell'evento Load della FrmRicerca, richiamando un metodo diverso per ciascun valore della discriminante TipoRicerca:Private Sub FrmRicerca_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load RigaSelezionata = -1 Select Case myTiporicerca Case "agenzia-clienti", "agenzia-fornitori" Agenzie() Case "codice-clienti" CodCliFor(1) Case "codice-fornitori" CodCliFor(2) Case "codice-prodotti" CodProd() Case "banche" Banche(1) Case "clienti" Clienti(1) Case "fornitori" Fornitori(1) Case "prodotti" Prodotti(1) Case "stati" Stati(1) Case "pagamenti" TipiPag(1) Case "unita-misura" UnMis(1) Case "carico-magazzino" CarMag(1) End Select End SubQuesto è un disastro, dal mio punto di vista. Che funzioni, non c'è alcun dubbio. Ma è anche indubbiamente sbagliato.
Questa non è una configurazione dinamica, ma una scelta tra diverse configurazioni statiche. L'effetto finale è lo stesso, ma il lavoro svolto (e da svolgere) è molto maggiore (da parte del programmatore).Per ogni nuova form (mettiamo caso che si aggiunga una tabella Veicoli al nostro database) bisogna non solo preparare la form, appunto, ma anche predisporre la 'sua' specifica personalizzazione in questa form, sia aggiungendo un Case al costrutto Select presente nella procedura sopra esposta, sia aggiungendo il metodo dedicato (e tutte le altre modifiche in altri metodi, senza dimenticarsene nessuno).
Bisognerebbe invece sforzarsi di costruire un metodo unico che elabori tutte le informazioni che la form chiamante può passare, distinguendo il meno possibile tra le diverse eventualità. Bisogna cioè analizzare che cosa serve alla FrmRicerca per 'costruirsi'.
I metodi di configurazione
Poiché sono molto simili l'uno all'altro, ne spiegherò estesamente uno, limitandomi a esporre le differenze presentate dagli altri. Il punto di vista non è quello di spiegare il codice, ma di prendere nota di quanto è necessario al funzionamento di FrmRicerca.Private Sub Agenzie() 'Titolo della form Me.Text = "Selezione agenzia" 'Disabilito i pulsanti di filtro e svuotamento dei criteri btnFiltra.Enabled = False btnSvuota.Enabled = False 'Impostazione delle label lblCrit1.Text = "Banca" lblCrit2.Text = "" lblCrit2.Visible = False lblCrit3.Text = "" lblCrit3.Visible = False lblCrit4.Text = "" lblCrit4.Visible = False lblCrit5.Text = "" lblCrit5.Visible = False lblCrit6.Text = "" lblCrit6.Visible = False
- Vediamo che serve un titolo.
- Vengono disabilitati due pulsanti, cosa che sembrerebbe doversi fare in fase di progettazione, ma viene fatta in fase di esecuzione perché il metodo di configurazione può venir richiamato più volte, a esempio se si cambiano i criteri di ricerca.
- Viene impostato il testo dell'unica Label necessaria e rese invisibili le altre. Quest'ultima impostazione sì che va fatta in fase di progettazione.
Prendiamo nota che serve un vettore dei testi per le Label.'preparo gli oggetti per valorizzare ComboBox e griglia Dim dst As DataSet Dim cmd As SqlCommand Dim adp As SqlDataAdapter Dim cbl As SqlCommandBuilder Dim StringaSQL As String = String.Empty cmd = New SqlCommand cmd.Connection = SqlHelper.ConnessioneDatabase cmbCrit1.Text = Filtro StringaSQL = "SELECT Banca, Agenzia, ABI, CAB, Citta, Stato " & _ "FROM Banche WHERE Banca = @Banca ORDER BY Banca, Agenzia" cmd.Parameters.AddWithValue("@Banca", cmbCrit1.Text) cmd.Parameters("@Banca").SqlDbType = SqlDbType.VarChar cmd.CommandText = StringaSQL adp = New SqlDataAdapter(cmd) cbl = New SqlCommandBuilder(adp) dst = New DataSet adp.Fill(dst, "Dati")
- Viene impostato il testo della prima ComboBox, ma non viene (non qui, che sarebbe il posto più adatto) disabilitato il controllo, se Filtro è valorizzato.
Prendiamo nota che serve un parametro Filtro, impostabile o meno.- Viene impostata l'espressione SQL del comando da eseguire, completa di parametri e ordinamento (non è che abbiamo già fatto questo lavoro nella definizione della classe Tabella?).
Prendiamo nota che serve una espressione SQL. Più precisamente, servono il nome della tabella e quattro vettori con nomi di campi, rispettivamente per la visualizzazione, per la preparazione dei parametri (nome e tipo) e per l'ordinamento.- Vengono aggiunti i parametri necessari al comando e si giunge al riempimento del DataSet nel modo che abbiamo già visto nella classe Tabella (non è che potremmo utilizzare 'quel' DataSet?).
'Valorizzo la ComboBox cmbCrit1.DataSource = dst.Tables(0) cmbCrit1.DisplayMember = "Banca" cmbCrit1.ValueMember = "Banca" cmbCrit2.Visible = False cmbCrit3.Visible = False cmbCrit4.Visible = False cmbCrit5.Visible = False cmbCrit6.Visible = False
- Viene associato il DataSet alla prima ComboBox (ma se deve essere disabilitata, quando il Text è già valorizzato con Filtro, questo è un lavoro inutile), e ne vengono impostati DisplayMember e ValueMember.
Prendiamo nota che serve un vettore di coppie di campi per queste proprietà delle ComboBox.- Vengono resi invisibili le altre ComboBox, cosa fattibile già in fase di progettazione.
'Sposto più in alto la griglia dgvDati.Top = 100 'Diminuisco le dimensioni della form Me.Height = 478
- Nel caso la seconda riga di Label ComboBox sia invisibile (le sei coppie sono disposte su due righe), si riposiziona la griglia e si ridimensiona la form. Io preferirei sottrarre la stessa quantità a entrambi valori, ma bisogna tenere conto che questi metodi possono essere chiamati più volte.
Prendiamo nota che bisogna distinguere tra disegno della form e rielaborazione dei dati da visualizzare.'Formattazione della griglia 'Nomi colonne e intestazioni Dim Nomicol() As String = {"Banca", "Agenzia", "ABI", "CAB", "Citta", "Stato"} 'Aggiungo le colonne For i As Integer = 0 To Nomicol.Length - 1 dgvDati.Columns.Add(Nomicol(i), Nomicol(i)) Next i
- Viene predisposto un vettore dei nomi dei campi e delle intestazioni per le colonne della griglia
Prendiamo nota della sua necessità.- Viene quindi scandito questo vettore per aggiungere le colonne alla griglia.
'Imposto le proprietà delle colonne dgvDati.RowHeadersWidth = 20 dgvDati.Columns(0).DataPropertyName = "Banca" dgvDati.Columns(0).Width = 290 dgvDati.Columns(1).DataPropertyName = "Agenzia" dgvDati.Columns(1).Width = 290 dgvDati.Columns(2).DataPropertyName = "ABI" dgvDati.Columns(2).Width = 45 dgvDati.Columns(3).DataPropertyName = "CAB" dgvDati.Columns(3).Width = 45 dgvDati.Columns(4).DataPropertyName = "Citta" dgvDati.Columns(4).HeaderText = "Città" dgvDati.Columns(4).Width = 290 dgvDati.Columns(5).DataPropertyName = "Stato" dgvDati.Columns(5).Width = 290 dgvDati.DefaultCellStyle.NullValue = ""
- Viene specificata la DataPropertyName e la Width di ciascuna colonna. Per una di esse viene specificata l'intestazione, perchè diversa dal nome del campo.
Prendiamo nota che serve un vettore di nomi e uno per le intestazioni speciali (ma non sono gi stessi già preparati prima?), nonché uno per le larghezze della colonne.- Viene anche impostata la RowHeadersWidth e il DefaultCellStyle.NullValue della griglia.
'Compilo la griglia dgvDati.DataSource = dst dgvDati.DataMember = "Dati" End Sub
- Viene infine compilata la griglia.
Altri metodi analoghi a questo si differenziano per la presenza di un parametro, che - per giunta - non ha lo stesso significato. A esempio:
Private Sub CodCliFor(ByVal Tipo As Byte) 'Tipo può assumere i valori 1 Clienti, 2 Fornitori 'Titolo della form Select Case Tipo Case 1 Me.Text = "Selezione codice cliente" Case 2 Me.Text = "Selezione codice fornitore" End Select
- Se il metodo fosse realmente generico, il parametro Tipo non sarebbe necessario. Cambia solo il nome della tabella di riferimento, in effetti.
Private Sub Banche(ByVal Completa As Byte) 'Completa può assumere i valori 1 formattazione completa e 2 formattazione solo della griglia If Completa = 1 Then 'Titolo della form Me.Text = "Ricerca banche" 'Impostazione delle label lblCrit1.Text = "Banca" lblCrit2.Text = "Agenzia" lblCrit3.Text = "ABI" lblCrit4.Text = "CAB" lblCrit5.Text = "Città" lblCrit6.Text = "Stato" 'Compilazione dei ComboBox PopolaCombo(1, "Banca", "Banche") PopolaCombo(2, "Agenzia", "Banche") PopolaCombo(3, "ABI", "Banche") PopolaCombo(4, "CAB", "Banche") PopolaCombo(5, "Citta", "Banche") PopolaCombo(6, "Stato", "Banche") 'Svuoto i ComboBox SvuotaCombo() End If
- il parametro Completa ha molto più senso: distingue tra preparazione dei controlli e compilazione del contenuto della griglia. Ma non serve nemmeno questo, in realtà. In effetti, si discrimina tra la prima esecuzione e le successive: per questo non si usa un parametro, ma una variabile statica booleana. A esempio:
Private Sub Alternativa() Static once As Boolean If Not once Then '... once = Not once End IfIl metodo PopolaCombo richiede il numero della ComboBox e i nomi dei campi da assegnare a DisplayMember e ValueMember.
- il numero? Io passerei direttamente il controllo. Però, in un metodo generico, cui si passano due vettori, ha senso predisporre un vettore di riferimenti alle ComboBox.
Nel metodo che configura la FrmRicerca per le tabelle Clienti, Fornitori e Prodotti viene resa invisibile la colonna Codice.
- Prendiamo nota che serve anche un vettore delle visibilità delle colonne.
Private Sub TipiPag(ByVal Completa As Byte) 'Completa può assumere i valori 1 formattazione completa e 2 formattazione solo della griglia If Completa = 1 Then 'Titolo della form Me.Text = "Ricerca tipi di pagamento" 'Impostazione delle label lblCrit1.Text = "Tipo di pagamento" lblCrit2.Text = "Fine mese" lblCrit3.Text = "Giorno fisso" lblCrit4.Text = "Appoggio in banca" lblCrit5.Text = "" lblCrit5.Visible = False lblCrit6.Text = "" lblCrit6.Visible = False 'Compilazione dei ComboBox PopolaCombo(1, "Pagamento", "Pagamenti") cmbCrit2.Items.Add("No") cmbCrit2.Items.Add("Si") PopolaCombo(3, "Giorno_fisso", "Pagamenti") cmbCrit4.Items.Add("No") cmbCrit4.Items.Add("Si") cmbCrit5.Visible = False cmbCrit6.Visible = False 'Svuoto i ComboBox SvuotaCombo() End If
- In questo metodo, due ComboBox vengono compilate con un elenco di valori, non con una sorgente dati
Prendiamo nota che serve un parametro per distinguere l'origine della lista, nonché il vettore dei valori.'Aggiungo le colonne Dim ColBoolFM As New DataGridViewCheckBoxColumn Dim ColBoolAB As New DataGridViewCheckBoxColumn For i As Integer = 0 To Nomicol.Length - 1 If i = 1 Then ColBoolFM.DataPropertyName = "Fine_mese" ColBoolFM.HeaderText = "Fine mese" dgvDati.Columns.Add(ColBoolFM) ElseIf i = 3 Then ColBoolAB.DataPropertyName = "Appoggio_banca" ColBoolAB.HeaderText = "Appoggio banca" dgvDati.Columns.Add(ColBoolAB) ElseIf i = 0 Or i = 2 Then dgvDati.Columns.Add(Nomicol(i), Nomicol(i)) End If Next i
- Le due colonne corrispondenti ai campi relativi alle ComboBox con elenco valori sono di diverso tipo.
Prendiamo nota che serve un vettore dei tipi delle colonne.I metodi di evento dei pulsanti
Se per quanto riguarda la configurazione ci siamo trovati davanti a una serie di metodi molto simili l'uno all'altro, nel codice che gestisce gli eventi Click dei pulsanti BtnFiltra e BtnConferma ci troviamo di fronte al festival della ripetizione della stessa sequenza di istruzioni con lievi variazioni. E' evidente che Oscar, per programmare velocemente, ha fatto più volte copia-incolla. E non ha pensato che si potesse avere un vettore di controlli anche in .Net (non è quasi mai realmente necessario, ma talvolta capita).Se però pensiamo a quante volte l'ha dovuto fare, ci possiamo rendere conto che 'pensare prima fa fare prima' e meglio.
Ecco la prima parte del codice di BtnFiltra_Click, da me resa un pochino più leggibile, aggiungendo qualche 'a capo':Private Sub btnFiltra_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnFiltra.Click Dim dst As DataSet Dim cmd As SqlCommand Dim adp As SqlDataAdapter Dim cbl As SqlCommandBuilder Dim StringaSQL As String = String.Empty cmd = New SqlCommand cmd.Connection = SqlHelper.ConnessioneDatabase Select Case myTiporicerca Case "banche" StringaSQL = "SELECT Banca, Agenzia, Citta, Stato FROM Banche" If cmbCrit1.Text.Trim <> String.Empty Or cmbCrit2.Text.Trim <> String.Empty Or _ cmbCrit3.Text.Trim <> String.Empty Or cmbCrit4.Text.Trim <> String.Empty Or _ cmbCrit5.Text.Trim <> String.Empty Or cmbCrit6.Text.Trim <> String.Empty Then StringaSQL = StringaSQL & " WHERE " End If If cmbCrit1.Text.Trim <> String.Empty Then StringaSQL = StringaSQL & "Banca = @Banca" cmd.Parameters.AddWithValue("@Banca", cmbCrit1.Text) cmd.Parameters("@Banca").SqlDbType = SqlDbType.VarChar End If If cmbCrit2.Text.Trim <> String.Empty Then If cmbCrit1.Text.Trim <> String.Empty Then StringaSQL = StringaSQL & " AND " End If StringaSQL = StringaSQL & "Agenzia = @Agenzia" cmd.Parameters.AddWithValue("@Agenzia", cmbCrit2.Text) cmd.Parameters("@Agenzia").SqlDbType = SqlDbType.VarChar End If If cmbCrit3.Text.Trim <> String.Empty Then If cmbCrit1.Text.Trim <> String.Empty Or cmbCrit2.Text.Trim <> String.Empty Then StringaSQL = StringaSQL & " AND " End If StringaSQL = StringaSQL & "ABI = @ABI" cmd.Parameters.AddWithValue("@ABI", cmbCrit3.Text) cmd.Parameters("@ABI").SqlDbType = SqlDbType.VarChar End If If cmbCrit4.Text.Trim <> String.Empty Then If cmbCrit1.Text.Trim <> String.Empty Or _ cmbCrit2.Text.Trim <> String.Empty Or cmbCrit3.Text.Trim <> String.Empty Then StringaSQL = StringaSQL & " AND " End If StringaSQL = StringaSQL & "CAB = @CAB" cmd.Parameters.AddWithValue("@CAB", cmbCrit4.Text) cmd.Parameters("@CAB").SqlDbType = SqlDbType.VarChar End If If cmbCrit5.Text.Trim <> String.Empty Then If cmbCrit1.Text.Trim <> String.Empty Or cmbCrit2.Text.Trim <> String.Empty Or _ cmbCrit3.Text.Trim <> String.Empty Or cmbCrit4.Text.Trim <> String.Empty Then StringaSQL = StringaSQL & " AND " End If StringaSQL = StringaSQL & "Citta = @Citta" cmd.Parameters.AddWithValue("@Citta", cmbCrit5.Text) cmd.Parameters("@Citta").SqlDbType = SqlDbType.VarChar End If If cmbCrit6.Text.Trim <> String.Empty Then If cmbCrit1.Text.Trim <> String.Empty Or cmbCrit2.Text.Trim <> String.Empty Or _ cmbCrit3.Text.Trim <> String.Empty Or cmbCrit4.Text.Trim <> String.Empty Or _ cmbCrit5.Text.Trim <> String.Empty Then StringaSQL = StringaSQL & " AND " End If StringaSQL = StringaSQL & "Stato = @Stato" cmd.Parameters.AddWithValue("@Stato", cmbCrit6.Text) cmd.Parameters("@Stato").SqlDbType = SqlDbType.VarChar End If StringaSQL = StringaSQL & " ORDER BY Banca, Agenzia"Come potete vedere, si controlla il Text di ciascuna ComboBox per costruire la clausola WHERE dell'espressione SQL del comando che leggerà di dati da visualizzare nella griglia e per impostare i parametri del comando stesso.
Se si pensasse all'espressione query come a una struttura costituita da tre strutture (selezione, criteri, ordinamento), verrebbe naturale separarle. In particolare sarebbe semplice costruire 'a parte' il contenuto della clausola WHERE, aggiungendo criteri e parametri per ciascuna ComboBox non vuota, senza preoccuparsi delle altre. A esempio, se si aggiunge a una stringa clausolaWhere l'operatore " AND " e il criterio "nomecampo =@nomecampo", ci si ritrova alla fine con la clausolaWhere completa, solo che comincia con " AND", parte da togliere e sostituire con "WHERE". Molto più economico di tante If.Segue una copia conforme del codice sopraesposto per ciascuna configurazione, per un totale di più di trecento righe di istruzioni.
Mi permetto di proporvi uno dei tanti acronimi per cui gli Americani vanno pazzi, perché, una volta tanto, assume un significato facilmente memorizzabile: DRY . E' l'acronimo di Don't Repeat Yourself (non ripeterti), che è una delle massime auree del programmatore.
Il fenomeno del copia-incolla di Oscar (ribadisco che la critica è più estetica che altro: ricordiamoci bene che il codice, nonostante sembri così brutto, è perfettamente funzionante - un difetto pratico sta solo nella confusione, nella difficoltà di manutenzione) si ripete nel metodo btnConferma_Click:
Private Sub btnConferma_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnConferma.Click If dgvDati.Rows.Count < 1 Then Exit Sub End If Dim Riga As DataGridViewRow = dgvDati.CurrentCell.OwningRow If RigaSelezionata < 0 Then Messaggi.Avviso("Selezionare una riga cliccando nella colonna fissa all'estrema " & _ "sinistra della stessa per proseguire con la ricerca", "Selezione riga") Else Select Case myTiporicerca Case "agenzia-clienti", "agenzia-fornitori" If Not (Riga.Cells("Banca").Value) Is Nothing Then If Riga.Cells("Banca").Value Is DBNull.Value Then Messaggi.Avviso("Selezionare una riga cliccando nella colonna fissa all'estrema " & _ "sinistra della stessa per proseguire con la ricerca", "Selezione riga") Else CampiChiave.Add(Riga.Cells("Banca").Value.ToString) CampiChiave.Add(Riga.Cells("Agenzia").Value.ToString) CampiChiave.Add(Riga.Cells("ABI").Value.ToString) CampiChiave.Add(Riga.Cells("CAB").Value.ToString) Me.Close() End If Else Messaggi.Avviso("Selezionare una riga cliccando nella colonna fissa all'estrema " & _ "sinistra della stessa per proseguire con la ricerca", "Selezione riga") End IfCome potete vedere, si controlla che esista un record congruo da leggere e lo si legge per popolare la lista CampiChiave da restituire alla form chiamante. Salta all'occhio che si risparmierebbe un po' di fatica e si eliminerebbe un po' di confusione impostando una costante per il messaggio. Anche qui, il codice si ripete per ciascuna configurazione, per un totale di più di centocinquanta righe di istruzioni.
Entrambi questi ultimi due metodi sono abbastanza facilmente generalizzabili, dato che analizzano sempre il valore presente nella prima colonna e restituiscono sempre una lista di campi chiave, i cui nomi conosciamo già dalla classe Tabella.
- Prendiamo nota che serve anche quell'elenco.
Conclusione
In questo articolo Diego ha criticato l'impostazione di Oscar della FrmRicerca, illustrando quelli che considera difetti e preparando l'analisi che servirà a sviluppare codice migliore.Precisando che Oscar è completamente d'accordo con le critiche di Diego e sulla loro pubblicazione nell'intento di far imparare anche gli altri dal suo errore di impostazione, chiaramente residuo della mentalità VB6, si dà appuntamento alla prossima puntata, in cui verrà illustrata la 'vera' FrmRicerca.
Stavolta non è stato aggiunto codice, quindi non c'è alcunché da scaricare, né troverete nel blog un companion per questo articolo, ma colgo l'occasione per segnalare l'ottima variazione proposta da Edoardo Casella, relativamente al metodo GetSchema della classe Tabella.