28/08/12 21:51
peteruncle9
Salve, devo caricare le righe di una tabella di un database durante la digitazione in una casella di testo, in modo che i risultati compaiano durante la digitazione. Per non rallentare l'UI vorrei far avvenire il caricamento in un BackgroundWorker, ma ci sono due problemi:
1) Se digito due lettere velocemente non fa in tempo a caricare la tabella e si sovrappongono i due comandi RunWorkerAsync(), invece se provo a fermare il backgroundworker, la datagridview a cui è associata la tabella manda un messaggio di errore nei dati
2) Quando un backgroundworker carica una tabella per cui nella datagridview è necessaria la scrollbar, resta lo spazio nero sulla destra ma non viene disegnata. Ho provato con doublebuffering ma nemmeno questo risolve.
aaa
29/08/12 6:19
ampeg
prova a caricare la tabella su una DataTable e fai la ricerca tramite il metodo Select, dovrebbe laveorare più velocemente che non direttamente sul DB
aaa
29/08/12 8:02
peteruncle9
Ho provato questo, ed era una buona soluzione visto che il metodo BindingSource.Filter è molto veloce, ma il problema era lo stesso: al primo caricamento impiegava più di 30 secondi (la tabella ha circa 50.000 righe). La soluzione sarebbe l'utilizzo del BackgroundWorker solo per caricare la tabella la prima volta, ma non riesco a capire per quale motivo si blocca la barra di scorrimento della datagridview (resta lo spazio a destra ma non viene più disegnata)
aaa
29/08/12 10:24
ampeg
cercare di ottenere quello che vuoi fare con oltre un certo numero di righe diventa problematico e non ha molto senso, perché mentre digiti il testo da ricercare potresti ottenere una griglia con migliaia di record... a chi interessa visualizzare "al volo" una griglia con tutti quei record ? ... è una situazione che si verifica senza alcuna utilità per nessuno, perché poi immagino che il dato rilevante sia il risultato finale che porta a visualizzare un numero di righe "umanamente visionabili"... non so se mi son spiegato
quindi potresti prendere in considerazione un metodo differente
spiega nel dettaglio cosa devi fare, che tipo di ricerca, su quale campo perché se questo campo contiene molti valori che si ripetono puoi caricare una distinta di quel campo in una combobox, riducendo notevolmente i tempi di lettura dal DB
impostare la proprietà della combo "AutoCompleteSource = AutoCompleteSource.ListItems" e "AutoCompleteMode = AutoCompleteMode.SuggestAppend" affinché quando digiti il testo nella combo ti venga suggerito un autocompletamento della parola, quando vuoi che vengano visualizzati i dati nella gridview relativi alla parola, lo fai premendo il tasto invio
aaa
29/08/12 15:31
ampeg
ok per la griglia "persone", anch'io uso quel metodo per filtrare clienti e fornitori, ma solo perché so già che in prospettiva non avrò ma i un numero tale di record da rallentare l'operazioni in tempi disumani
x la seconda grigla "ordini" tu dici:
Subito dopo aver filtrato la tabella Persone (questo avviene in modo istantaneo), devo caricare la seconda tabella "Ordini", in modo che visualizzi in una seconda DataGridView gli ordini associati alla prima riga della tabella "Persone"
se ho capito bene in pratica la seconda griglia si popola in atomatico poiché ogni volta che vengono trovati record nella prima griglia "persone" si autoseleziona la prima riga ed invoca l'evento per popolare la seconda griglia
è necessario che si comporti in questo modo, cioè è voluta questa condizione o ne puoi fare a meno ?
poi gli altri dati x gli ordini degli altri nominativi possono essere ottenuti selezionando manualmente le righe della prima griglia e questo è ok
al la dell'uso del thread separato (che secondo me non è la soluzione adatta al tuo problema) il motivo per cui rallenta la ricerca è perché ad ogni carattere digitato avvengono 2 ricerche, la prima sulle persone da filtrare, la seconda sugli ordini relativi alla prima riga trovata di "persone", mentre dovrebbe esserci una sola ricerca, quella sulle "persone", la seconda ricerca deve avvenire solo se decidi di selezionare manualmente una riga dalla prima griglia
aaa
29/08/12 16:43
peteruncle9
Il riempimento della tabella "Ordini" automatico con la ricerca con l'autoselezione della prima riga della tabella "Persone" è utile perchè fa risparmiare un passaggio, dato che l'obiettivo della ricerca è prevalentemente ApriOrdine(...).
Con il thread separato non ho rallentamenti ma l'applicazione diventa più instabile, la datagridview genera un DataError se annullo il thread durante l'esecuzione per riavviarlo (cioè digito un'altro carattere nella textbox di ricerca), e potrei gestirlo (e.cancel = true); ma non riesco a capire per quale motivo se carico una tabella da un thread separato la datagridview si blocca e non visualizza la barra di scorrimento (lasciando lo spazio nero a destra, ma con le freccette riesco a scorrerla).
C'è lo stesso problema nella datagridview se provo a mettere in un thread separato il caricamento iniziale delle "Persone", ed è un peccato perchè mi consentirebbe di evitare di bloccare per circa dieci secondi l'interfaccia grafica.
aaa
29/08/12 18:32
ampeg
ho provato a ricostruire uno scenario simile con il mio DB, ti posto il codice come l'ho pensato io
a me funziona bene anche con una quantità considerevole di record, è veloce e non da problemi di instabilità vedi se può esserti utile
tieni presente che l'ho provato su un DB SQL Server
sul Form1 c'è un TextBox1 per digitare la ricerca
ci sono 2 datagridview (dgv1, dgv2): nella prima ci sono i nominativi, nella seconda gli ordini
Imports System.Data.OleDb
Imports System.ComponentModel
Public Class Form1
Private _bw As New BackgroundWorker
Private _cn As New OleDb.OleDbConnection
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
_bw.WorkerSupportsCancellation = True
AddHandler _bw.DoWork, AddressOf _bw_DoWork
AddHandler _bw.RunWorkerCompleted, AddressOf _bw_RunWorkerCompleted
'impostare la stringa di connessione del DB
_cn.ConnectionString = "connection string"
_cn.Open()
End Sub
Private Sub Form1_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
_cn.Close()
End Sub
Private Sub TextBox1_TextChanged(sender As System.Object, e As System.EventArgs) Handles TextBox1.TextChanged
Call Fill()
End Sub
Private Sub Fill()
Dim dt As New DataTable
Dim cmd As New OleDbCommand
Dim da As New OleDbDataAdapter
cmd.Connection = _cn
da.SelectCommand = cmd
'qui va la query per la selezione nominativi, mettere i nomi tabelle e campi desiderati
cmd.CommandText = "SELECT * FROM Persone WHERE Nome LIKE '" & TextBox1.Text & "%'"
da.Fill(dt)
dgv1.DataSource = dt
If dgv1.Rows.Count > 0 Then
If _bw.IsBusy Then
_bw.CancelAsync()
Else
_bw.RunWorkerAsync()
End If
Else
dgv2.DataSource = Nothing
End If
End Sub
Private Sub _bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
Dim cmd As New OleDbCommand
Dim da As New OleDbDataAdapter
Dim dt As New DataTable
'recupero l'idpersona dalla prima riga di dgv1
'mettere il nome della colonna desiderato
Dim sID As String = dgv1.Rows(0).Cells("IDPersona").Value.ToString
cmd.Connection = _cn
da.SelectCommand = cmd
'qui va la query per la selezione ordini, mettere i nomi tabelle e campi desiderati
cmd.CommandText = "SELECT * FROM Ordini WHERE IDPersona = " & sID
da.Fill(dt)
e.Result = dt
End Sub
Private Sub _bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
If e.Cancelled = True Then
ElseIf e.Error IsNot Nothing Then
Else
Dim dtResult As DataTable = e.Result
dgv2.DataSource = dtResult
End If
End Sub
End Class
Ultima modifica effettuata da ampeg 30/08/12 5:51
aaa