Italian Testo di una ListView

Touch

Active Member
Licensed User
Longtime User
Scusate, ma non riesco a venirne a capo!
ho popolato una listview con questo codice:

B4X:
Dim sqlStr As String
    sqlStr = "SELECT * FROM Aziende ORDER BY Descrizione"
  
    lvAziende.Clear

    Try
        modFunzioni.rs = modFunzioni.db.ExecQuery(sqlStr)
        'TotRow = modFunzioni.rs.RowCount
        If modFunzioni.rs.RowCount > 0 Then
            For i = 0 To modFunzioni.rs.RowCount - 1
                DoEvents
                modFunzioni.rs.Position = i
                lvAziende.AddSingleLine2(modFunzioni.rs.GetString("Descrizione"), modFunzioni.rs.GetString("ID"))
            Next
        End If
        modFunzioni.rs.Close
    Catch
        Msgbox(LastException,"Errore")
    End Try

Successivamente per il recupero dei valori, ho utilizzato la seguente routine:
B4X:
Sub lvAziende_ItemClick (Position As Int, Value As Object)
    txtId.Text = Value
    txtAzienda.Text = lvAziende.GetItem(Position)
End Sub

Il problema è che non riesco a recuperare il testo della riga cliccata.

con
B4X:
txtId.Text = Value
recupero l'ID della colonna associata, ma con

B4X:
txtAzienda.Text = lvAziende.GetItem(Position)

non visualizzo il testo della riga cliccata.

Dove Sbaglio?
 

LucaMs

Expert
Licensed User
Longtime User
Tu carichi la listview mettendo come testo il valore di un campo di una tabella (Descrizione) e l'ID di quella tabella come valore restituito dall'evento ItemClick.

Quindi, quando selezioni un elemento della listview, il "Value" restituito sarà l'ID della tabella (cosa utilissima, comunque!).

Per ottenere direttamente la Descrizione (ovvero il testo nella listview) tramite GetItem non dovresti passare l'ID, cioè dovresti aggiungere l'Item con AddSingleLine.

A questo punto, ottenuto l'ID, dovresti recuperare la Descrizione dalla tabella.

Consiglio spassionato: usa CheckList anziché la ListView: è un pochino più difficile da capire all'inizio, perché devi costruirti l'Item, ma è mooolto più utile e "potente".
 
Last edited:

Touch

Active Member
Licensed User
Longtime User
Se aggiungo la riga con AddSingleLine, dove memorizzo la colonna ID? E successivamente come recupero questa informazione?
Una listview non ha un metodo diretto per recuperare il testo di una riga selezionata?
 

udg

Expert
Licensed User
Longtime User
Ciao Touch,

se guardi la documentazione relativa alla ListView trovi ciò che ti ha detto Luca, ovvero che, utilizzando AddSingleLine2 per l'inserimento, da GetItem ottiene il valore che hai abbinato alla riga di testo (nel tuo caso ID nella tabella).
Invece utilizzando AddSingleLine per l'inserimento, GetItem restituisce la riga di testo (nel tuo caso descrizione).

Una "furbata" per avere sia ID che descrizione sarebbe quella di passare in AddSingleLine2 come secondo parametro un oggetto che contenga entrambi i valori. Certamente uno spreco di memoria (visto che la descrizione verrebbe memorizzata 2 volte) ma comunque una soluzione al problema.

Umberto
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
Una "furbata" per avere sia ID che descrizione sarebbe quella di passare in AddSingleLine2 come secondo parametro un oggetto che contenga entrambi i valori. Certamente uno spreco di memoria (visto che la descrizione verrebbe memorizzata 2 volte) ma comunque una soluzione al problema.

Umberto

Giustissimo, Umberto, infatti lo avevo scritto e poi cancellato, anche perché non ricordavo se fosse possibile e come recuperare il dato dalla seconda label.
Come mai? Perché (memoria corta a parte) mi piace molto di più e consiglio sempre la CheckList.

Certo, in casi semplici, probabilmente è eccessiva, ma difficilmente avremo casi semplici: la maggior parte delle volte vorremo fare delle modifiche agli item che nelle listview non sarà possibile fare.
 

Touch

Active Member
Licensed User
Longtime User
Grazie a tutti per le soluzioni.
La soluzione di Umberto è ottima per semplici liste anche di pochi Item.
La soluzione di Luca è risolutiva per liste più complesse.
 

Touch

Active Member
Licensed User
Longtime User
Ho trovato una soluzione che posto per chi, come me, avesse un'esigenza analoga:

Ho dichiarato una variabile Map in Globals
B4X:
Private lvAziendeMap As Map

Ho modificato la routine per riempire la ListView
B4X:
Dim sqlStr As String
    sqlStr = "SELECT * FROM Aziende ORDER BY Descrizione"
   
    lvAziendeMap.Initialize
    lvAziende.Clear
   
    Try
        modFunzioni.rs = modFunzioni.db.ExecQuery(sqlStr)

        If modFunzioni.rs.RowCount > 0 Then
            For i = 0 To modFunzioni.rs.RowCount - 1
                DoEvents
                modFunzioni.rs.Position = i
'                lvAziende.AddSingleLine2(modFunzioni.rs.GetString("Descrizione"), modFunzioni.rs.GetString("ID"))
                lvAziende.AddSingleLine(modFunzioni.rs.GetString("Descrizione"))
                lvAziendeMap.Put(modFunzioni.rs.GetString("Descrizione"), modFunzioni.rs.GetString("ID"))
            Next
        End If
        modFunzioni.rs.Close
    Catch
        Msgbox(LastException,"Errore")
    End Try

Ho modificato la sub per catturare il click sulla lista
B4X:
Sub lvAziende_ItemClick (Position As Int, Value As Object)   
    txtAzienda.Text = Value
    txtId.Text = lvAziendeMap.Get(Value)
End Sub

Così facendo ho ottenuto il risultato desiderato.

Grazie a Tutti
 

udg

Expert
Licensed User
Longtime User
Ciao Touch,

lungi da me l'idea di deprimere la tua soddisfazione, ma..

introducendo la variabile di tipo Map sei tornato nella situazione di "spreco di risorse" della furbata precedente; a quel punto tanto vale usare il secondo argomento della lista per contenere un'oggetto che memorizzi sia ID che descrizione e gestire la sola List invece che List+Map.

Approfitto per farti notare che, se non vengono utilizzati altrove, i campi diversi da ID e Descrizione ritornati dalla select sono superflui, quindi qualcosa tipo: "select ID,Descrizione from aziende ordered by decsrizione" diviene più efficiente.
 

LucaMs

Expert
Licensed User
Longtime User
Dato che quella situazione, ovvero avere un campo descrizione e relativo ID, è molto frequente, l'ideale sarebbe creare una funzione specializzata, passandogli un cursore aperto, nel caso in cui i campi siano sempre denominati Descrizione e ID, oppure una query da eseguire all'interno della funzione.

Qualcosa di simile l'ho fatta in ExecuteSpinnerMap
 

Touch

Active Member
Licensed User
Longtime User
Ciao Umberto,
La mia non voleva essere la "panacea di tutti i mali" ma solo una soluzione più elegante da un punto di vista della scrittura del codice.
La mia listview non dovrebbe contenere più di una ventina di item, quindi l'impiego di risorse è anche relativo.
Corretto, invece, il tuo consiglio sulla select. Mi porto dietro da anni reminescenze di programmazione sbagliata.
 

LucaMs

Expert
Licensed User
Longtime User
Infatti sicuramente non voleva sminuire la tua soluzione.

Anche se in questo caso non sprecheresti molte risorse, conviene (lo dico anche a me stesso, eh!) abituarsi a fare le cose nel modo "giusto", così, appunto, diventa una "buona abitudine" (chissà perché è più facile acquisire le "cattive abitudini", i vizi :)).

Se poi utilizzerai la tua soluzione, è inutile eseguire due volte la query (immagina se fosse eseguita in remoto):
B4X:
Dim sqlStr As String
    sqlStr = "SELECT * FROM Aziende ORDER BY Descrizione"
 
    lvAziendeMap.Initialize
    lvAziende.Clear
  
    Private Descrizione As String

    Try
        modFunzioni.rs = modFunzioni.db.ExecQuery(sqlStr)

        If modFunzioni.rs.RowCount > 0 Then
            For i = 0 To modFunzioni.rs.RowCount - 1
                DoEvents
                modFunzioni.rs.Position = i
                Descrizione = modFunzioni.rs.GetString("Descrizione")
                lvAziende.AddSingleLine(Descrizione)
                lvAziendeMap.Put(Descrizione, modFunzioni.rs.GetString("ID"))
            Next
        End If
        modFunzioni.rs.Close
    Catch
        Msgbox(LastException,"Errore")
    End Try
 

udg

Expert
Licensed User
Longtime User
@LucaMs : confermo, avevo solo buone intenzioni :)
@Touch : una ventina di clienti? pagano tutti regolarmente? in tal caso tocca a te pagare da bere al forum eh eh

E visto che finalmente ho avuto il mio caffè e la nebbia dovrebbe abbandonare la mente per un po', vi propongo la "furbata n.2" (squillo di trombe).

Basterebbe sostituire alla Map una semplice List in cui memorizzare gli ID aziendali.
In tal modo non avresti spreco di spazio (lo so, sono fissato, ma proprio non riesco ad adattarmi a macchine che abbiano più di 64-128KB di RAM..) e lo stesso indice ti permetterebbe di prelevare dalla ListView la descrizione e dalla List l'ID.

Non ho nulla contro l'oggetto Map in sè, che anzi adoro, ma come dicevo prima, il solo pensiero di duplicare tutte quelle stringhe.. brrr

Detto questo, su una ventina di elementi, una soluzione vale l'altra e quindi concordo che non vale la pena perdere neanche un solo istante per cambiare qualcosa che evidentemente già funziona secondo le attese.

Bye
 

udg

Expert
Licensed User
Longtime User
Esattamente come hai fatto tu, ma dove usi la Map usa una List.

Esempio:
B4X:
' In Globals dichiara
Private AziendeID() as List

'nel loop che legeg il cursor usa
lvAziende.AddSingleLine(modFunzioni.rs.GetString("Descrizione"))
AziendeID.Add(modFunzioni.rs.GetString("ID"))

'quindi l'evento click diventa
Sub lvAziende_ItemClick (Position AsInt, Value AsObject)
txtAzienda.Text = Value  'questo ritorna la descrizione
txtId.Text = AziendeID.Get(Position)
End Sub

Ma va benissimo anche la map. Non perdere tempo che la tua app ha tutta l'aria di essere impegnativa..

ps: la cosa funziona perchè List.add aggiunge sempre in coda e procedendo in parallelo con gli inserimenti (e non avendo eliminazione di item di cui preoccuparsi) gli indici delle due list sono allineati.
 

Touch

Active Member
Licensed User
Longtime User
Ho provato ad implementare la tua soluzione cambiando
B4X:
txtId.Text = AziendeID.Get(Position)
in
B4X:
txtId.Text = AziendeID().Get(Position)
ma mi torna questo tipo di errore:

Schermata 2014-08-20 alle 15.34.30.png
 

udg

Expert
Licensed User
Longtime User
L'errore dipende proprio dalle parentesi dopo AziendeID. E' come se si attendesse che indicato un elemento della lista (es. AziendeID(5)) poi si voglia trattarlo come una lista a sua volta ed usare la funzione Get per rilevare l'elemento con indice Position.

Riguardando il mio suggerimento..

Dim AziendeID as List ' questo va bene come lista
oppure
Dim AziendeID() as string ' qui invece diventa un array di stringhe

si vede che il caffè non era ancora in circolo.. :rolleyes:

in pratica siamo passati in pochi minuti da squillo di trombe a pernacchie, sigh!
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
Io penso che la farei così (veramente no, la farei diversamente):
B4X:
' Sub Globals
Private lstIDs As List : lstIDs.Initialize

Private ErrMsg As String
ErrMsg = LV_Fill_Descr_ID(lvAziende, modFunzioni.rs, "Descrizione", "ID", lstIDs, True)
If ErrMsg.Length > 0 Then
    Msgbox(ErrMsg, "Caricamento Aziende")
End Sub

Private Sub lvAziende_ItemClick (Position As Int, Value As Object)
    txtAzienda.Text = Value
    txtId.Text = lstIDs.Get(Position)
End Sub


Public Sub LV_Fill_Descr_ID(lvtToFill As ListView, Cur As Cursor, DescFieldName As String, IDFieldName As String, lstIDs As List, Clear As Boolean) As String
    Private ErrMsg As String = ""
  
    If Clear Then
        lvtToFill.Clear
        lstIDs.Clear
    End If

    Try
        For i = 0 To Cur.RowCount - 1
            Cur.Position = i
            lvtToFill.AddSingleLine(Cur.GetString(DescFieldName))
            lstIDs.Add(Cur.GetString(IDFieldName))
        Next
    Catch
        ErrMsg = LastException.Message
    End Try
  
    Return ErrMsg
  
End Sub

La chiusura del cursore non la metterei nella routine.

Nota: non è assolutamente testato.
 

Touch

Active Member
Licensed User
Longtime User
Ci sono alcune parentesi di troppo, AziendeID è una List, non un Array. Basta toglierle nella dichiarazione e nell'evento. Inoltre va inizializzata.

Perfetto! Il codice di Umberto era corretto ma senza parentesi.
Penso di implementare questa soluzione. Più che sufficiente per una listview di pochi item.
Il suggerimento di Luca va comunque inserito nei code snippet.

A proposito, nessuno ha mai pensato di sviluppare un'applicazione mobile che raccoglie i migliori snippet suddivisi per categoria?
Un po' come il Codebook di ogni linguaggio di programmazione!
A disposizione solo all'interno del Forum!
Sarebbe una grande Idea....
 

LucaMs

Expert
Licensed User
Longtime User
Un'app non sarebbe utilissima perché usiamo i PC per sviluppare, non il dispositivo mobile (a meno che qualcuno non usi i tablet, ma penso siano scomodi).

Comunque c'è quest'applicazione, grazie a rwblinn, scritta in B4J, che quindi gira su Windows, Mac, Linux, etc.:

Code Snippet App
 
Top