Newsgroup: it.comp.lang.visual-basic
Titolo: [VB.NET] Datagridview e datatable - spostare righe

Ciao,
ho una form con una datagridview che viene popolata dall'utente pescando 
dei dati da una listview.
La datagridview ha come datasource una datatable.
Adesso devo permettere all'utente di modificare l'ordine delle righe 
tramite due bottoni "sposta su" e "sposta giu", ma non riesco a farlo.
Avevo giï¿œ fatto una cosa simile funzionante ma con la datagridview non 
collegata a un datasource, poi seguendo i consigli di Wodka40[google] 
che mi consigliava di usare sempre un datasource ho risolto molti 
problemi ma mi ï¿œ spuntato questo che non riesco a risolvere :-(
Questo il codice (suppongo di voler spostare la riga 1)
' recupero la datatable usata come datasource per la datagridview
Dim dt As DataTable
dt = dg.DataSource
' faccio una copia della riga da spostare
Dim r As DataRow
r = dt.Rows(1)
' cancello la riga da spostare dalla datatable
dt.Rows.Remove(r)
dt.Rows.InsertAt(r, 1 + 1)
Il problema ï¿œ che dopo l'istruzione dt.Rows.Remove(r) la riga r "non 
esiste piï¿œ", infatti il compilatore mi dice "Questa riga ï¿œ stata rimossa 
da una tabella e non contiene alcun dato."
Cosa sto sbagliando?
ciao
Mauro

 Cosa sto sbagliando?
In generale direi che non ha senso quello che stai cercando di fare, su 
una tabella (DataTable o di database che sia) non ha senso il concetto 
di ordine posizionale così come siamo abituati in una lista, perchè non 
ha senso il concetto di indice by design, su una tabella ha senso il 
concetto di ordine sulla base di un valore presente nella tabella, ergo 
o hai una colonna "Sort" sulla base della quale poui ordinare i dati o 
non c'è verso di fare quello che vuoi fare tu.
Quindi giusto sarebbe:
- avere una colonna che definisce l'ordinamento;
- mettere in binding la gurglia non direttamente con la tabella ma con 
una DataView (vista sulla tabella) in cui espliciti le regole 
sull'ordinamento;
- per cambiare ordinamento impostare i valori corretti nella colonna 
Sort;
- eseguire il "refresh" della DataView;
.m
.m
-- 
Mauro Servienti
{C67C0157-5D98-4733-A75E-93CAEE4BADC8}
Microsoft MVP - Visual C# / MCTS
http://mvp.support.microsoft.com
blog @ 
http://milestone.topics.it
whynot [ at ] topics [ dot ] it

 non c'è verso di fare quello che vuoi fare tu.
Ho capito cosa vuoi dire, ma nel mi applicativo la DatagridView 
inizialmente è vuota, poi l'utente tramite interfaccia ci importa dentro 
"righe" oppure "operazioni" (che sono semplicemente dei gruppi di righe).
A questo punto se la guarda per bene, decide di spostare su o giù 
qualche riga, ne cancella alcune, ne aggiunge altre e così via.
Quando ritiene di aver finito preme salva e va tutto sul db, a quel 
punto io valorizzo una colonna "numero riga" che mi serve per l'ordinamento.
Io non vorrei scrivere sul db ogni volta che l'utente inserisce una riga 
o un gruppo di righe, possono essere anche centinaia le righe che 
compongono una operazione ... preferisco che lavori "in ram" fino a 
quando non è soddisfatto del risultato e solo allora salvo tutto sul db.
Il tuo suggerimento di utilizzare la catena
DataGridView -> DataView -> Db
è applicabile nel mio caso in cui la prima volta che arrivo nella 
datagrid i dati non esistono ancora nel db?
Se vado in modifica di un ciclo esistente ok, ci posso provare, ma in 
fase di creazione del ciclo come dovrei procedere?
grazie per l'aiuto, ciao
Mauro

 Mauro
.m


 Qui ti conviene approfondire i concetti di reference e value type.
Mi sono documentato per bene sui tipi reference e sui tipi value, la 
differenza mi ï¿œ molto chiara.
Quello che non riesco a capire allora ï¿œ come mai questo codice funziona:
Private Sub btnUp_Click(ByVal sender As System.Object, ByVal e As 
System.EventArgs) Handles btnUp.Click
   'controllo di non essere alla prima riga della datagridview
   If RigaSelezionata > 0 Then
       'creo una variabile di appoggio di tipo RIGA DI DATAGRIDVIEW
       Dim R As DataGridViewRow
       'copio per intero la riga da spostare
       R = DataGridView1.Rows(RigaSelezionata)
       'tolgo la riga vecchia
       DataGridView1.Rows.Remove(R)
       'inserisco la copia della riga SOPRA quella che devo togliere
       DataGridView1.Rows.Insert(RigaSelezionata - 1, R)
       'mantengo la selezione sulla riga spostata
       DataGridView1.Rows(RigaSelezionata - 1).Selected = True
       'ed aggiorno di conseguenza la variabile che indica la selezione
       RigaSelezionata -= 1
       'ridisegno la datagridview
       DataGridView1.Refresh()
   End If
End Sub
mentre il mio no. Anche qua R ï¿œ la copia di un puntatore alla riga 
selezionata che subito doppo rimuovo ...
Il codice completo dell'esempio che ti ho fatto lo trovi qua:
http://www.dotnethell.it/forum/ShowCode.aspx?MessageID=103565&Frag=0
grazie, ciao
Mauro

 selezionata che subito doppo rimuovo ...
forse perchï¿œ sono completamente diversi?
Il tuo codice ragiona su una DataTable, l'esempio che riporti ragiona 
su una DataGridView.


 una DataGridView.
Si, ma non si tratta in entrambi i casi di oggetti "riga" e quindi non 
sono tutti e due tipi reference? E' questo che non riesco a capire.
ciao e grazie
Mauro

 tutti e due tipi reference? E' questo che non riesco a capire.
si certo, la differenza sta nel funzionamento del metodo "Remove".
Facciamo una prima considerazione, in entrambi i casi dire che si sta 
facendo "una copia per intero della riga" ï¿œ un grave errore perchï¿œ si 
sta creando semplicemente un nuovo puntatore (reference) che punta alla 
posizione in memoria dove si trovano le informazioni relative alla riga 
in oggetto.
La DataGridView quando chiami remove semplicemente rimuove dalla sua 
collection di righe la riche in oggetto e siccome tu hai in mano una 
reference a quella riga la puoi mettere in un altro posto e il tutto 
funziona, la DataTable quando chiami remove non rimuove un bel nulla 
semplicemente marca la riga come "deleted", ï¿œ il funzionamento della 
DataTable, quindi quando cerchi di aggiungere una riga deleted in un 
posto diverso giustamente lei si incazza.


 posto diverso giustamente lei si incazza.
Ah, ok, adesso ho capito, grazie per l'ottima spiegazione !!!
Adesso proseguo con i miei tentativi per implementare il funzionamento 
tramite la DataView ... finora non ci sono riuscito, mi si incasina 
tutto quando la currentRow non ï¿œ piï¿œ nell'insieme delle SelectedCells o 
qualcosa del genere, non ho ancora capito.
ciao
Mauro

 myGrid.DataSource = view;
Ciao,
chiedo ancora un aiuto. Non riesco proprio a capire come faccio a 
spostare su un gruppo di celle.
Sto provando in questo modo:
0. ho aggiunto alla datagrid e alla datatable la colonna peso e assegno 
un peso crescente alle righe, ho creato la vista e ho ordinato la datagrid
1. leggo l'elenco delle SelectedRows della datagrid e metto gli indici 
di riga in una lista
2. per ogni elemento della lista diminuisco il suo peso e aumento il 
peso della riga con indice precedente
3. riordino la vista in base al peso
Ma non mi funziona, non riesco nemmeno a capire il perchï¿œ :-(
Se seleziono un unica riga funziona, poi modifico la selezione per 
spostare un'altra riga e mi legge il peso sbagliato, ovvero il peso che 
aveva la riga prima di fare il primo spostamento.
E' come se una volta che ho modificato i pesi questi non venissero 
memorizzati nella datatable ...
For Each i In lista
   dt.Rows(i).Item("peso") = (dt.Rows(i).Item("peso") - 10)
   dt.Rows(i - 1).Item("peso") = (dt.Rows(i - 1).Item("peso") + 10)
   v.Sort = "peso"
   'dg.DataSource = dt
Next
Ho fatto mille prove, ma non riesco a capire cosa sbaglio, qualcuno ï¿œ in 
grado di indirizzarmi ?
ciao, grazie
Mauro

 grado di indirizzarmi ?
Direi che non ï¿œ cosï¿œ facile :-), hai bisogno di piï¿œ informazioni e stai 
dando per scontate un paio di cose che scontate non sono affatto.
Per spostare degli elementi in una lista hai bisogno di:
- direzione dello spostamento;
- reference (o indice) degli elementi da spostare;
Se gli elementi che devi spostare sono da spostare in "blocco", quindi 
devi supportare la selezione multipla la cosa si complica perchï¿œ devi 
anche capire se gli elementi sono contigui e trattare quelli contigui 
com e un blocco unico.
Hai poi la casistica di selezione mista: seleziono i primi 3 insieme 
poi con il "ctrl" aggiungo alla selezione anche il 5, e voglio spostare 
verso il basso... i primi 3 andranno in blocco dopo il 4 mentre il 5 
andrï¿œ dopo il sesto... sempre che un sesto ci sia, perchï¿œ se il quinto 
fosse l'ultimo non devi spostarlo...
Insomma quel ciclo foreach non puï¿œ proprio funzionare.
Ma non ï¿œ finita, stai partendo dal pressupposto che gli indici nella 
griglia siano gli stessi nella DataTable, ma la girglia rispecchia 
l'ordine della DataView quindi ï¿œ pressochï¿œ impossibile questa cosa se 
non forse al primo DataBinding, devi quindi risalire alla "righe" 
selezionate nella DataTable partendo si da quelle selezionate nella 
griglia ma andando a cercare gli item in binding e non passando per 
gl'indici.
Mi rendo conto che ti ho complicato la vita, ma ï¿œ il bello del nostro 
lavoro ed ï¿œ l'unico motivo per cui lo faccio ancora :-P


 gl'indici.
questo non mi ï¿œ molto chiaro .... ma penso che tu abbia centrato il 
punto, infatti alla prima iterazione mi funziona, se poi ripremo il 
pulsante "sposta su" senza aver riselezionato gli elementi esplode tutto :-)
Per il problema dell'ordinamento ho provato a risolvere aggiungendo una 
colonna "peso" che viene valorizzata quando popolo la datagridview un 
elemento alla volta oppure quando leggo i dati dal db man mano che li 
leggo assegno un peso crescente.
Poi pensavo che quando l'utente fa "sposta su" modifico il valore della 
colonna peso di quella riga e di quella superiore/inferiore e riordino 
il tutto con view.sort ma anche in questo modo mi sa che mi perdo 
qualche pezzo per strada tra datagridview - datasource - datatable - 
selezione ecc ecc
ciao
Mauro

 Il 27/10/2010 05:41, Mauro Servienti [MVP] ha scritto:
zacchete
Stamani mi avanzava tempo....e poi sono in preparazione mistica, a
base di camomilla dell'Himalaya e Joga Uzbeko, in vista delle 5 ore di
corso "base" sull'utilizzo di stasera! OMMMMMMMMM!
Quindi mi andava di programmare qualcosa
NON ho utilizzato i tasti su e giu...ma il mouse!...va perfezionato e
limato il codice ed infarcito di try catch...ma l'idea funziona!
Accade questo:
L'utente clicca con il tasto destro sulla riga  che vuole
spostare ....e tenendo premuto muove il mouse....appare una icona
(drag & drop)....avendolo spostato può anche rilasciare il mouse la
griglia ha capito che vuoi spostare la riga....adesso trovi la
destinazione (usando scroll o quello che vuoi) e CONFERMI usando il
tasto sinistro (faccio più fatica a spiegarlo che a farlo!)
Fatto...i valori vengono swappati....siccome mi ricordavo che il
riferimento ordine NON VUOI swapparlo....quello rimane li!
Funziona!
Allora VS2010 un form una datagridview...sopra una label (per far
vedere al tuo utente cosa sta facendo) e un button per mostrare a
Innuendo che la tabella è davvero aggiornata!
AVVERTENZE:
la form si chiama gridStart...
la label lblStatus
il button btnTest
LA GRIGLIA HA NELLE PROPRIETA': ALLOWDROP  TRUE
Saluti!
Codice:
Public Class GridStart
    Dim Tabella As New System.Data.DataTable
    Dim DumRiga As DataRow
    Private FLAG_SPOSTA As Boolean = False
    Dim riga1 As DataGridViewRow
    Dim riga2 As DataGridViewRow
    Private Sub GridStart_Load(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles MyBase.Load
        myGrid.Anchor = AnchorStyles.Top Or AnchorStyles.Left Or
AnchorStyles.Right
        CaricaDati()
    End Sub
    Private Sub CaricaDati()
        Tabella.Columns.Add("RifOrdine",
Type.GetType("System.String"))
        Tabella.Columns.Add("Descrizione",
Type.GetType("System.String"))
        Tabella.Columns.Add("Qta", Type.GetType("System.Int32"))
        Tabella.Columns.Add("Prezzo", Type.GetType("System.Decimal"))
        'Dati
        For n As Integer = 0 To 20
            Dim record As DataRow = Tabella.NewRow()
            record(0) = n
            record(1) = ("Prodotto prova cod." + n.ToString)
            record(2) = Int((30 - 10 + 1) * Rnd() + 10) 'numero fra 10
e 30
            record(3) = CDec(((12.5 - 3.0 + 1) * Rnd() + 3.0)) 'numero
fra 3.0 e 12.5
            'aggiungi
            Tabella.Rows.Add(record)
        Next
        ' visualizza nella griglia
        myGrid.AutoGenerateColumns = True
        myGrid.DataSource = Tabella
        myGrid.Columns(1).Width = 328
    End Sub
    Private Sub myGrid_DragDrop(ByVal sender As Object, ByVal e As
System.Windows.Forms.DragEventArgs) Handles myGrid.DragDrop
        Try
            Dim t As DataGridView.HitTestInfo
            t = myGrid.HitTest(e.X, e.Y)
            e.Effect = DragDropEffects.None
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub
    Private Sub myGrid_DragEnter(ByVal sender As Object, ByVal e As
System.Windows.Forms.DragEventArgs) Handles myGrid.DragEnter
        'qui ha capito che voglio modificare
        FLAG_SPOSTA = True
        lblStatus.Text = "SPOSTAMENTO"
    End Sub
    Private Sub myGrid_DragLeave(ByVal sender As Object, ByVal e As
System.EventArgs) Handles myGrid.DragLeave
        ' MsgBox("Hai lasciato")
        Dim t As New Point
        t.X = MousePosition.X
        t.Y = MousePosition.Y
        Dim trisp As DataGridView.HitTestInfo
        trisp = myGrid.HitTest(t.X, t.Y)
        lblStatus.Text = "lasciato"
    End Sub
    Private Sub myGrid_DragOver(ByVal sender As Object, ByVal e As
System.Windows.Forms.DragEventArgs) Handles myGrid.DragOver
        e.Effect = DragDropEffects.Move
    End Sub
    Private Sub myGrid_MouseDown(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles myGrid.MouseDown
        Dim Index As Integer
        If e.Button = MouseButtons.Right Then
            Index = myGrid.HitTest(e.X, e.Y).RowIndex
            If Index > -1 Then            'Pass the Index as "Data"
argument of the DoDragDrop Function
                myGrid.DoDragDrop(Index, DragDropEffects.Move)
                'memorizza la partenza
                riga1 = myGrid.Rows(Index)
            End If
        End If
    End Sub
    Private Sub myGrid_MouseUp(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles myGrid.MouseUp
        If FLAG_SPOSTA = True Then
            FLAG_SPOSTA = False
            Dim index As Integer
            index = myGrid.HitTest(e.X, e.Y).RowIndex
            'eseguo lo swap
            'casomai "mirato"
            riga2 = myGrid.Rows(index)
            '******************************
            'ho bisogno di una terza riga clone per lo swap
            Dim rigaswap As DataGridViewRow
            rigaswap = riga2.Clone
            'NON FUNZIONA: troppo bello...non fà un deep copy....i
valori non li ricopia!
            ' a manina cari miei...o derivando
            ' sono pochi campi...lo faccio a manina!
            rigaswap.Cells(1).Value = riga2.Cells(1).Value
            rigaswap.Cells(2).Value = riga2.Cells(2).Value
            rigaswap.Cells(3).Value = riga2.Cells(3).Value
            '....faticaccia finita!
            '******************************
            'il contenuto di riga1 sovrascrive riga 2 ...solo nei
campi 1 2 3
            riga2.Cells(1).Value = riga1.Cells(1).Value
            riga2.Cells(2).Value = riga1.Cells(2).Value
            riga2.Cells(3).Value = riga1.Cells(3).Value
            'adesso tocca a riga 1
            riga1.Cells(1).Value = rigaswap.Cells(1).Value
            riga1.Cells(2).Value = rigaswap.Cells(2).Value
            riga1.Cells(3).Value = rigaswap.Cells(3).Value
            ' butta via cella swap
            rigaswap.Dispose()
            lblStatus.Text = "Normale"
        End If
    End Sub
    Private Sub btnTest_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnTest.Click
        For Each Triga As DataRow In Tabella.Rows
            Console.WriteLine("Rif Ordine:{0} Prodotto:{1} Qta:{2}
Prezzo:{3}", Triga.Item(0).ToString, Triga.Item(1).ToString,
Triga.Item(2).ToString, Triga.Item(3).ToString)
        Next
        Stop ' per guardare cosa è accaduto nella tabella!
    End Sub
End Class

 corso "base" sull'utilizzo di stasera! OMMMMMMMMM!
zac
Non mi riesce nemmeno scriverlo......
utilizzo del pc!
non di qualche strana sostanza....magari!(almeno non mi facevo 2 palle
così con gente che clicca dappertutto a vanvera!)

              ' sono pochi campi...lo faccio a manina!
In sostanza copi i valori contenuti nelle celle della riga di partenza 
nelle celle della riga di destinazione usando una riga temporanea .... 
mi sembrava un po' una brutalit�, ma visto che di copiare la riga intera 
in blocco proprio non c'e' verso ... TI SEGUO :-) !!!
Provo ad adattare il tuo codice al mio progetto, intanto grazie per 
l'ennesima dritta !
ciao
Mauro

 mi sembrava un po' una brutalità, ma visto che di copiare la riga intera
zac
....senza fare il filosofo: la vita E' brutalità!...infatti ci siamo
inventati il Paradiso e la Religione!

 inventati il Paradiso e la Religione!
:-)
Sono riuscito a fare tutto quello che volevo in un progettino ad hoc, ma 
quando lo riporto nel progetto dove mi serve effettivamente ottengo un 
eccezzione in datagridview che mi dice:
"la colonna id_cdl � in sola lettura"
ma io non ho mai messo quella colonna in sola lettura !!! :-( :-( :-(
Perch� mi fa questo? E sopratutto, come posso evitarlo?
Ho provato a fare
dgCicloLavorazione.Columns("id_cdl").ReadOnly = False
ma non gli va bene lo stesso, ottengo l'errore "Per una colonna 
DataGridView associata a un campo in sola lettura ReadOnly deve essere 
impostato su True."
ciao e grazie
Mauro

 Il 28/10/2010 10:33, Wodka40[Google] ha scritto:
IMHO!
Connetto il db
Popolo la datatable
STACCO TUTTO
lavoro....
Connetto di nuovo il db
Aggiorno!
Per me...ma la palla di cristallo l'ho rotta...hai qualche relazione
che impedisce l'aggiornamento! Sai le query son brutte bestie!
Tu se vuoi fare lo spippolamento che mi dicevi
DEVI...DEVI...
usare la griglia non come griglia ma come array di celle...tu vuoi
ROMPERE l'unità del record ...e quindi devi essere libero da vincoli!
Cosa che probabilmente non hai fatto nella tua applicazione!
Vuoi fare il "sommo spippolamento carpiato come se fosee antani alla
blinda supercazzola": cambiare gli ID che sempre si blindano con una
primary key!...sempre se sei al cambio ordine di qualche post fà!
...io sono sempre perplesso è son sempre dell'idea che alla fine
l'uTonto che userà il tuo programma prima o poi sarà  proprio
Tonto....e spedirai un set di finissimi sottobicchieri ricamati della
ValTellina a chi ha comprato la bambola gonfiabile "gina la
porcellina" ...ma tant'è!!!
:)

 Aggiorno!
Ma io faccio esattamente cos�.
' Creo la Datasource di appoggio
Dim dt As New DataTable
dt.Columns.Add("id_cdl", Type.GetType("System.Int32"))
dt.Columns.Add("id_rev_ciclo", Type.GetType("System.Int32"))
dt.Columns.Add("id_operazione", Type.GetType("System.Int32"))
dt.Columns.Add("numero_riga", Type.GetType("System.Int32"))
dt.Columns.Add("nome", Type.GetType("System.String"))
dt.Columns.Add("fase", Type.GetType("System.String"))
dt.Columns.Add("sottofase", Type.GetType("System.String"))
dt.Columns.Add("tipo", Type.GetType("System.String"))
' La collego alla datagridview
dgCicloLavorazione.DataSource = dt
Poi se serve popolo la datagridview leggendo i dati dal database usando 
la seguente funzione:
Public Sub query_to_datagrid(ByRef dg As DataGridView, ByVal sql As 
String, Optional ByVal echo As Boolean = False)
' Svuoto la datagrid
dg.DataSource = Nothing
dg.Rows.Clear()
dg.Columns.Clear()
' Faccio la query
Using conn As New OdbcConnection(Me.dsn)
   Dim command As New OdbcCommand(sql, conn)
   Try
     conn.Open()
     Using tmpTable As New DataTable
       Dim dr As OdbcDataReader = command.ExecuteReader
       tmpTable.Load(dr)
       dr.Close()
       ' Aggiungo le righe alla datatable
       dg.AutoGenerateColumns = True
       dg.DataSource = tmpTable
     End Using
   Catch ex As Exception
     MsgBox("Impossibile eseguire la query" & vbCrLf & ex.Message)
   End Try
End Using
End Sub
e se faccio la controprova con:
MsgBox("Prima: " & dgCicloLavorazione.Columns("id_cdl").ReadOnly)
db.query_to_datagrid(dgCicloLavorazione, "select * from v_cdl where 
id_rev_ciclo=" & id_rev_ciclo)
MsgBox("Dopo: " & dgCicloLavorazione.Columns("id_cdl").ReadOnly)
In pratica prima di chiamare la funzione query_to_datagrid la colonna 
NON � in readonly, mentre dopo lo �.
Io penso che il problema sia nel "cambio" di datasource ma non capisco 
perche'
ciao
Mauro
p.s.
come'e' andata ieri sera il corso?

 come'e' andata ieri sera il corso?
Una tragedia....quasi 5 ore con gente che clicca dappertutto (anche
sui muri)...tranne che sull'icona "start"!!!
p.s.
Domani c'ho quasi la mattina libera....guardo se ti butto giu un
esempio (con Access) dell'aggancio tabella!

 serve? A buttar via cicli macchina?
Uhm ... che domanda difficile :-) ... ho fatto cos� seguendo il tuo 
esempio, prima se ricordi facevo tutto a manina creando una colonna e 
una riga alla volta man mano che scorrevo il datareader.
Tu mi avevi consigliato di usare una cosa del tipo:
Dim miaTab As New DataTable(NomeTable)
Using DR As Npgsql.NpgsqlDataReader = 
comando.ExecuteReader(CommandBehavior.KeyInfo)
   miaTab.Load(DR)
   DR.Close()
End Using
non � la stessa cosa che ho fatto io?
Io per domani provo a preparare i due esempi di modo che chi in futuro 
seguir� questo thread avr� una risposta definitiva.
Un miniprogetto per spostare in alto e in basso gruppi di righe in una 
datagrid l'ho quasi pronto.
Adesso vedo di fare anche questo del datasource
ciao e grazie per l'aiuto
Mauro