Le avventure in VB.Net di un principiante ex-VB6 - 15
a cura di Oscar Zanin e Diego Cattaruzza (requisiti: Visual Basic Express e SqlServer)Premessa
Nella puntata precedente era stata completata l'illustrazione della FrmRicerca, la form di dialogo utilizzata da molte altre form del progetto. Il codice della FrmRicerca era stato profondamente modificato in seguito alle critiche espresse nella puntata numero 12. Di conseguenza Oscar ha dovuto adeguare tutte le altre form del progetto.E' così emerso un difetto della progettazione di FrmRicerca, rimediando al quale si è aggiunta una qualità.
E' stato anche scoperto un errore di Diego (uno statement If fuori posto).In questo articolo si descrive il difetto, il rimedio, la caratteristica aggiunta e la correzione dell'errore.
Il difetto
Per una delle configurazioni della FrmRicerca, la sorgente dati della griglia non era una sola tabella, ma un set di record risultante da una query di più tabelle correlate. In altre parole, la proprietà InfoRicerca.NomeTabella non poteva essere solo il nome di una tabella, ma anche una espressione query più complessa.Quindi, tanto per cominciare, quella proprietà doveva cambiare nome. Adesso si chiama SorgenteDati. Ecco la nuova struttura InfoRicerca:
Public Structure InfoRicerca ''' <summary> ''' Titolo per la FrmRicerca ''' </summary> Dim TitoloForm As String ''' <summary> ''' Nome della tabella di riferimento o ''' espressione query di selezione tra più tabelle ''' </summary> Dim SorgenteDati 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 StructureQuesta modifica comporta anche un cambiamento nella struttura Criterio, per la quale diventava necessario un ulteriore membro, NomeTabella. Se, infatti, InfoRicerca.SorgenteDati è una tabella, allora per ciascun Criterio varrà la stessa tabella; ma se abbiamo a che fare con una query, occorre indicare a quale tabella fa riferimento un Criterio. Ecco quindi la nuova struttura Criterio:
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> ''' Nome della tabella di riferimento ''' </summary> ''' <remarks>Opzionale. Se InfoRicerca.SorgenteDati è una query, è obbligatorio</remarks> Dim NomeTabella As String ''' <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 nomeTabella As String = Nothing, _ 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.NomeTabella = nomeTabella Me.Valori = valori End Sub End StructureNaturalmente, avendo aggiunto un membro, si deve adeguatamente modificare il costruttore.
La qualità
Nell'apportare le modifiche sopra descritte, a Oscar è venuta la buonissima idea di arricchire l'enumerato TipoConfronto aggiungendo un membro IniziaPer, corrispondente alla clausola "LIKE criterio%". Così è stato naturale, per Diego, aggiungere un membro Contiene:Public Enum TipoConfronto Uguale Maggiore MaggioreUguale Minore MinoreUguale IniziaPer Contiene End EnumConseguenze
La prima cosa di cui preoccuparsi, quando la FrmRicerca riceve la struttura InfoRicerca in base alla quale configurarsi, è stabilire se SorgenteDati è il nome di una tabella o una query di selezione.
Si aggiunge quindi un campo:Private mIsQuery As Boolean = False ' InfoRicerca.SorgenteDati è una query di selezioneQuesto campo va adeguatamente valorizzato nel costruttore:
If mInfoRicerca.SorgenteDati.StartsWith("SELECT") Then mIsQuery = TrueNel metodo PopolaCombo la variabile tabella viene valorizzata a seconda del valore di mIsQuery:
Dim tabella As String = If(mIsQuery, _ mInfoRicerca.Criteri(numeroCombo).NomeTabella, _ mInfoRicerca.SorgenteDati)Nel metodo PreparaGriglia viene trattato in modo più congruo la visibilità di una colonna:
If mInfoRicerca.Colonne(c).Visibile = True Then DgvDati.Columns(c).Width = mInfoRicerca.Colonne(c).Larghezza Else DgvDati.Columns(c).Visible = False End IfNel metodo PopolaGriglia la variabile stringaSQL viene anch'essa valorizzata a seconda del valore di mIsQuery:
Dim stringaSQL As String = String.Empty If mIsQuery = False Then ' se la sorgente dati è una tabella ' preparo la query di selezione dei dati che verrà costruita stringaSQL = "SELECT " ' aggiungo l'elenco dei campi da esporre nella griglia For c As Integer = 0 To mInfoRicerca.Colonne.Count - 1 stringaSQL &= mInfoRicerca.Colonne(c).NomeCampo & ", " Next ' aggiungo la clausola From e l'inizio della clausola Where stringaSQL &= stringaSQL.Substring(0, (stringaSQL.Length - 2)) & _ " FROM " & mInfoRicerca.SorgenteDati Else ' se la sorgente dati è una query di selezione stringaSQL = mInfoRicerca.SorgenteDati End If ' aggiungo la clausola WHERE stringaSQL &= " WHERE "La variabile operatoriConfronto contiene due nuovi elementi, per i corrispondenti membri IniziaPer e Contiene dell'enumerato TipoConfronto:
Dim operatoriConfronto As String() = {" = @", " > @", " >= @", " < @", " <= @", _ " LIKE @", " LIKE @"}Sono ovviamente uguali, però cambia il modo di applicarli, quando è il momento di aggiungere il relativo parametro:
'Aggiungo il parametro al Command Select Case mInfoRicerca.Criteri(c).Confronto Case TipoConfronto.IniziaPer cmd.Parameters.AddWithValue("@" & mInfoRicerca.Criteri(c).NomeCampoValore, _ mArrCombo(c).Text.Trim & "%") Case TipoConfronto.Contiene cmd.Parameters.AddWithValue("@" & mInfoRicerca.Criteri(c).NomeCampoValore, _ "%" & mArrCombo(c).Text.Trim & "%") Case Else cmd.Parameters.AddWithValue("@" & mInfoRicerca.Criteri(c).NomeCampoValore, _ mArrCombo(c).Text.Trim) End Select ' imposto il tipo del parametro cmd.Parameters("@" & mInfoRicerca.Criteri(c).NomeCampoValore).SqlDbType = _ mInfoRicerca.Criteri(c).TipoCampoValoreLo statement End If corrispondente alla If mArrCombo(c).Text.Trim <> String.Empty deve essere spostato subito prima dello statement Next corrispondente al For c As Integer = 0 To mInfoRicerca.Criteri.Count - 1.
E' questo l'errore di Diego, che aveva lasciato questo End If prima del controllo di validità, dimenticandosi l'appunto appositamente segnatosi per la 'cosa da fare' e poi non fatta.
Dopo aver posto riparo a questo errore, sono stati aggiunti commenti agli statement End If e Next troppo lontani dal rispettivo statement If o For.Nel metodo BtnCerca_Click della FrmStati, ovviamente, cambia il nome del membro della struttura InfoRicerca:
ir.SorgenteDati = "Stati"Ma anche l'aggiunta del criterio:
ir.Criteri.Add(New Criterio("Stato", TipoOrigineDati.DatiTabella, "Stato", "Stato", _ SqlDbType.VarChar, False, TipoConfronto.IniziaPer))Come vedete, si sfrutta proprio uno dei tipi di confronto appena aggiunti.
Le preferenze estetiche di Oscar e Diego
Secondo Diego, la dimensione delle form non dovrebbe superare il formato 800x600. Secondo Oscar, bisogna stare il più comodi possibile, infatti la sua FrmRicerca è grande circa 980x540. Di conseguenza, il metodo PreparaCriteri contiene, adesso, un certo numero di costanti in più, per permettere a entrambi di avere ciascuno la propria configurazione preferita.
Queste impostazioni potrebbero anche essere rese persistenti in un file di impostazioni, conseguente a un menu appositamente predisposto per gestire le impostazioni dell'utente. Si vedrà più avanti, se aggiungerlo.
Intanto, qui trovate le impostazioni delle costanti di Oscar, mentre quelle di Diego sono nel metodo PreparaCriteri presente nel codice allegato all'articolo, che si ritiene superfluo riportare qui (ovviamente, cambiano leggermente le righe in cui vengono usate queste costanti).Const xLabel As Integer = 9 ' location.x della prima label Const xCombo As Integer = 12 ' location.x della prima combo Const ctlWidth As Integer = 310 ' size.width di label e combo Const ctlHeight As Integer = 21 ' size.height di label e combo Const ctlStep As Integer = 316 ' scostamento da un controllo all'altro dello stesso tipo Const yRiga1Label As Integer = 48 'Location.y delle label della prima riga Const yRiga1Combo As Integer = 67 'Location.y delle combo della prima riga Const yRiga2Label As Integer = 93 'Location.y delle label della seconda riga Const yRiga2Combo As Integer = 112 'Location.y delle combo della seconda riga Const labelAlignment As ContentAlignment = ContentAlignment.MiddleLeft ' allineamento labelIn questo modo, potete anche voi impostare le misure che preferite.
Conclusione
In questa puntata è stato illustrato come si è posto rimedio a una mancanza nello sviluppo della struttura InfoRicerca, fornendola di una ulteriore caratteristica che rende la FrmRicerca più versatile. Si è anche riparato un errore sorto in fase di collaudo.
Nelle prossime puntate verranno illustrate altre form (come dicono nei trailers: 'Non mancate!' :o))).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.