Popolare velocemente comboBox da SQL

venerdì 21 gennaio 2011 - 10.42
Tag Elenco Tags  VB.NET  |  Windows XP  |  Visual Studio 2005  |  SQL Server 2008

giaarcix Profilo | Newbie

Ciao a tutti,
vi espongo un problema che mi è capitato sviluppando con VB.NET 2005 su database SQL2008.
Ho una comboBox che va popolata in modo diverso a seconda del valore di alcuni radioButton.
In ognuna delle varie configurazioni questa comboBox deve contenere circa diecimila righe.
Il popolamento del comboBox funziona con il codice seguente, ma penso che potrebbe funzionare meglio:

Dim conn As New SqlConnection("................")
Dim strSQL As String = ""
strSQL = "SELECT ID,NAME FROM TABELLA ORDER BY NAME
Dim da As New SqlDataAdapter(strSQL, conn)
Dim ds As New DataSet
ComboBoxTest.DataSource = Nothing
da.Fill(ds, "centraline")
With ComboBoxTest
.DataSource = ds.Tables("TABELLA")
.DisplayMember = "NAME"
.ValueMember = "ID"
.SelectedIndex = 0
End With
conn.Close()

Dico che forse potrebbe funzionare meglio perchè così facendo il comboBox impiega circa 2 o 3 secondi a riempirsi, il che potrebbe essere un tempo abbastanza lungo perchè il comboBox non viene popolato una volta sola all'inizio, ma a runtime potrebbe essere necessario ripopolarlo a seconda dell'opzione selezionata dall'utente con il radioButton.
Ho fatto un test fatto con VB6, con un DataCombo con DataSource = mioAdodc (dove quest'ultimo adodc contiene i dati della connessione al database): e ho scoperto che così facendo il DataCombo viene popolato istantaneamente dagli stessi dati con un semplice:

DataComboTest.RecordSource = "SELECT NAME FROM TABELLA ORDER BY NAME"
DataComboTest.Refresh

Non credo che il mio DataCombo in VB6 si prepopoli qualche cosa (il che forse potrebbe essere il motivo per cui è più veloce), quindi sicuramente mi manca qualche passaggio... qualcuno può aiutarmi per far sì che il mio ComboBox in VB.NET 2005 sia veloce come il DataCombo in VB6 con ADODC??
Ho provato anche ad usare invece di una SqlConnection e di un SqlDataAdapter una OleDbConnection ed un OleDbDataAdapter, ma il tempo di popolamento è praticamente identico.

Grazie in anticipo per le vostre opinioni,

Giacomo da Pavia

AntCiar Profilo | Expert

Ciao Giacomo.

Penso che nei 2-3 secondi che impiega per il caricamento un buon 50% è dovuto al tempo che impiega a caricare i dati (Connessione al DB e recupero dei dati) e il restante è il tempo utilizzato per creare graficamente gli oggetti nella combo.

Non so se sei in ascolto anche dell'evento SelectIndexChange sulla combo box. Se si, allora ti conviene utilizzare un flag. Una cosa del tipo:

flag = true combo.datasource = ..... combo.displaymember = ..... combo.Valuemember = ..... flag = false private sub .......... handles combo.SelectIndexChange if flag = false end if end sub

questo perchè non appena associ il datasource alla combo, per ogni elemento della tabella viene scatenato l'evento selectIndexChange e quindi potrebbe rellentarti la cosa.

Prova a fare anche questo. se ho capito bene i dati che carichi nella combo cambiano a seconda della scelta che fai su un altro componente. A questo punto allora puoi caricare nella sorgente dati della combo tutti i possibili valori. Poi invece di associare l'intera tabella, ti fai un DataView sulla sorgente dati filtrando secondo il parametro di ricerca e poi associ il dataview.ToTable() al datasource della combo. Poi ti basta cambiare il RowFilter del dataview e riassociarlo alla combo ogni qualvolta devi visualizzare altri dati. In questo fai un solo accesso al DB. La cosa dovrebbe essere un po più veloce (sicuramente sul cambio di visualizzazione dei valori)

ciao

Cristian Barca

giaarcix Profilo | Newbie

Ciao e grazie per la risposta prontissima!
Allora: il flag lo utilizzo già per evitare di scatenare ogni volta l'evento SelectIndexChange.
Ho fatto una prova lanciando la stessa query direttamente su SQL (sql manager) ed il risultato viene fuori istantaneamente.
Da codice, giusto per provare connessione e caricamento dati, ho provato anche a fare questo:

Dim dataTest As ADODB.Recordset
dataTest = New ADODB.Recordset
dataTest.Open("SELECT NAME FROM TABELLA ORDER BY NAME", conn, 0, 1)
dataTest.Close()

Dove conn è una ADODB.connection, e il tutto fila via liscio in meno di un secondo. Quindi la connessione e la lettura dei dati in sè non dovrebbe essere una cosa lunga, anche perchè con l'ADODC che ho provato in VB6 i dati erano sempre quelli e la connessione pure.

Tra l'altro il problema per cui (penso) non posso usare la DataView è che a seconda che io clicchi uno o l'altro radioButton, il comboBox deve essere popolato con dei dati che si trovano tutti su tabelle separate. Immagina che l'utente selezioni con il radioButton se vuole vedere l'elenco dei nomi, delle città, delle targhe, ecc. Tutti questi dati si trovano su tabelle separate quindi una volta dovrò fare un "select name from tabella1", l'altra volta "select targhe from tabella2", ecc.ecc.!

Tornando al mio codice, e debuggando punto per punto questa parte:

With ComboBoxTest
.DataSource = ds.Tables("TABELLA")
.DisplayMember = "NAME"
.ValueMember = "ID"
End With

Vedo che ci mette "tanto" ad eseguire tutti e tre i passaggi (tipo un secondo a testa!).
Allora mi chiedo: non è che c'è qualche impostazione del comboBox che può essere modificata? Tipo per effettuare tipo un popolamento sincrono o asincrono? Per fare in modo che il combo cominci ad essere disponibile da subito anche se non è del tutto riempito (tipo come se ci fosse un doEvents durante il caricamento dei dati).
Ditemi se farnetico...
Grazie!

Giacomo

totti240282 Profilo | Guru

Perche invece di un datatable non provi ad usare un datareader ???.
C'è solo un capitano !!!!!!

giaarcix Profilo | Newbie

Allora, ho provato come mi consigli:

Dim myCommand As SqlCommand
conn.Open()
myCommand = New SqlCommand(strSQL, conn)
Dim dr As SqlDataReader
dr = myCommand.ExecuteReader()
Do While dr.Read()
ComboBoxTest.Items.Add(dr.Item(0))
Loop
dr.Close()

In effetti va leggermente più veloce (tipo 2 secondi invece di 3). C'è un grosso però: nel caso precedente avevo la possibilità di associare l'ID ad ogni elemento inserito nel comboBox (DisplayMember e ValueMember) mentre in questo caso non so come fare!!!
Nel caso decida di percorrere questa strada, come posso fare per utilizzare ancora il ValueMember?
Non si può associare direttamente il DataSource del ComboBox al DataReader?
Grazie!

giaarcix Profilo | Newbie

Ho trovato una soluzione cercando qua e là su google e forum.

Se si setta la proprietà del ComboBox.IntegralHeight = False il popolamento va incredibilmente più veloce (il tutto viene fatto in meno di un secondo) e ora tutto mi sembra funzionare come deve...!
Sono andato a guardare per cercare di capire come mai questa semplice proprietà possa far cambiare così tanto le prestazioni del combobox, ed ho scoperto che IntegralHeight in pratica indica se far sì o meno che l'ultima riga visibile del combo risulti eventualmente visualizzata solo in parte.
Quindi si trattava di caricare la grafica del combo!

Sperando che serva ad altri, buona serata!

Giacomo

Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2017
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5