domenica 15 gennaio 2012

[VB.NET] DataGridView e MultiSelezioni

Descrizione :
Una mia tecnica per gestire la Multi-Selezione utente su DataGridView.

+ Articolo :

Spesso nei Forum Tecnici capita di leggere richieste sull'utilizzo avanzato del Controllo DataGridView, sempre molto utile e adatto alla rappresentazione di dati in svariate situazioni.
In questo Articolo penso di dare una risposta pratica e abbastanza completa sul problema di riuscire a selezionare con il Mouse, ma non solo ( la tecnica seguente tiene conto anche della selezione via tastiera e via codice... ) i dati presenti in un DataGridView connesso ad un DataTable, mantenendo tale selezione attiva.
Non sono quindi necessarie colonne CheckBox aggiuntive nè combinazioni di tasti ( niente CTRL ) per eseguire le selezioni e inoltre le Righe o Celle selezionate vengono automaticamente raccolte in opportune List() di riferimento che rimangono sempre aggiornate e pronte al prelievo dei dati.

Per replicare l'esempio seguente basta predisporre la solita Application Windows Forms con una Form "FormMain".
Su FormMain servono 4 controlli. Nell'ordine da sinistra a destra sono :
--> DataGridView : DGV1.
--> ListBox : lst_dgv1.
--> DataGridView : DGV2.
--> ListBox : lst_dgv2.

- DGV1 rappresenta i dati del proprio DataSource ( DT1 ), e mostra la tecnica sulla selezione delle Rows.
Le Rows selezionate e raccolte nella propria List() "selectedRows", sono mostrate nella lst_dgv1.
- Analogamente, DGV2 rappresenta i dati del proprio DataSource ( DT2 ), e mostra la tecnica sulla selezione delle Cells.
Le Cells selezionate e raccolte nella propria List() "selectedCells", sono mostrate nella lst_dgv2.
Perciò le due ListBox qui hanno scopo di verifica costante e visibile di quanto avviene dietro le quinte.

Non occorre impostare alcuna proprietà dei controlli a design, in quanto tutto viene definito al Load().

La figura seguente mostra un esempio di utilizzo a runtime :


--> Codice FormMain :
Public Class FormMain

    'DataTable per DGV1
    Private DT1 As New DataTable
    'DataTable per DGV1
    Private DT2 As New DataTable
    'SelectedIndices per Rows su DGV1
    Private selectedRows As New List(Of DataGridViewRow)
    'SelectedCells per Cells su DGV2
    Private selectedCells As New List(Of DataGridViewCell)

    Private Sub FormMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        'Impostazioni DGV1 - Dimostrazione con Rows
        With DGV1
            .MultiSelect = True
            .SelectionMode = DataGridViewSelectionMode.FullRowSelect
            .DefaultCellStyle.SelectionBackColor = Color.Red
            .DefaultCellStyle.Font = New Font(System.Drawing.FontFamily.GenericSansSerif, 12)
            .ReadOnly = False
            .AllowUserToAddRows = False
            .AllowUserToDeleteRows = True
            .DataSource = DT1
        End With

        'Impostazioni DGV2 - Dimostrazione con Celle
        With DGV2
            .MultiSelect = True
            .SelectionMode = DataGridViewSelectionMode.CellSelect
            .DefaultCellStyle.SelectionBackColor = Color.Green
            .DefaultCellStyle.Font = New Font(System.Drawing.FontFamily.GenericSansSerif, 12)
            .ReadOnly = False
            .AllowUserToAddRows = False
            .AllowUserToDeleteRows = True
            .DataSource = DT2
        End With

        'DataTable per DGV1
        With DT1
            'Colonna PK
            .Columns.Add("ID")
            .PrimaryKey = New DataColumn() {.Columns("ID")}
            'In VB 2010 può essere anche :
            '.PrimaryKey = {.Columns("ID")}
            'Altre Colonne
            For i As Integer = 1 To 9
                .Columns.Add("Colonna_" & i.ToString.PadLeft(2, "0"))
            Next
            '...
            'Inserimento Rows
            Dim R As DataRow
            For i As Integer = 0 To 9
                R = .NewRow
                R(0) = i
                For j As Integer = 1 To .Columns.Count - 1
                    R(j) = "Cella_" & j
                Next
                .Rows.Add(R)
            Next
        End With

        'DataTable per DGV2
        With DT2
            'Colonna PK
            .Columns.Add("ID")
            .PrimaryKey = New DataColumn() {.Columns("ID")}
            'In VB 2010 può essere anche :
            '.PrimaryKey = {.Columns("ID")}
            'Altre Colonne
            For i As Integer = 1 To 9
                .Columns.Add("Colonna_" & i.ToString.PadLeft(2, "0"))
            Next
            '...
            'Inserimento Rows
            Dim R As DataRow
            For i As Integer = 0 To 9
                R = .NewRow
                R(0) = i
                For j As Integer = 1 To .Columns.Count - 1
                    R(j) = "Cella_" & j
                Next
                .Rows.Add(R)
            Next
        End With

    End Sub

    Private Sub CheckCurrentDgv1()

        If DGV1.CurrentRow Is Nothing Then Exit Sub
        If selectedRows.Contains(DGV1.CurrentRow) Then
            selectedRows.Remove(DGV1.CurrentRow)
        Else
            selectedRows.Add(DGV1.CurrentRow)
        End If
        SelezioneDgv1()

    End Sub

    Private Sub SelezioneDgv1()

        For Each dgvr As DataGridViewRow In DGV1.Rows
            If selectedRows.Contains(dgvr) Then
                dgvr.Selected = True
            Else
                dgvr.Selected = False
            End If
        Next
        lst_dgv1.Items.Clear()
        lst_dgv1.Items.AddRange(selectedRows.ToArray)

    End Sub

    Private Sub DGV1_CurrentCellChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DGV1.CurrentCellChanged
        CheckCurrentDgv1()
    End Sub

    Private Sub DGV1_CellBeginEdit(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles DGV1.CellBeginEdit
        SelezioneDgv1()
    End Sub

    Private Sub DGV1_UserDeletingRow(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewRowCancelEventArgs) Handles DGV1.UserDeletingRow
        If selectedRows.Contains(e.Row) Then selectedRows.Remove(e.Row)
        SelezioneDgv1()
    End Sub

    Private Sub DGV1_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles DGV1.DoubleClick
        CheckCurrentDgv1()
    End Sub

    Private Sub CheckCurrentDgv2()

        If DGV2.CurrentCell Is Nothing Then Exit Sub
        If selectedCells.Contains(DGV2.CurrentCell) Then
            selectedCells.Remove(DGV2.CurrentCell)
        Else
            selectedCells.Add(DGV2.CurrentCell)
        End If
        SelezioneDgv2()

    End Sub

    Private Sub SelezioneDgv2()

        For Each dgvr As DataGridViewRow In DGV2.Rows
            For Each dgvc As DataGridViewCell In dgvr.Cells
                If selectedCells.Contains(dgvc) Then
                    dgvc.Selected = True
                Else
                    dgvc.Selected = False
                End If
            Next
        Next
        lst_dgv2.Items.Clear()
        lst_dgv2.Items.AddRange(selectedCells.ToArray)

    End Sub

    Private Sub DGV2_CurrentCellChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DGV2.CurrentCellChanged
        CheckCurrentDgv2()
    End Sub

    Private Sub DGV2_CellBeginEdit(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles DGV2.CellBeginEdit
        SelezioneDgv2()
    End Sub

    Private Sub DGV2_UserDeletingRow(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewRowCancelEventArgs) Handles DGV2.UserDeletingRow
        For Each dgvc As DataGridViewCell In e.Row.Cells
            If selectedCells.Contains(dgvc) Then selectedCells.Remove(dgvc)
        Next
        SelezioneDgv2()
    End Sub

    Private Sub DGV2_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles DGV2.DoubleClick
        CheckCurrentDgv2()
    End Sub

End Class

Per quanto riguarda la parte Dati il codice è già ben commentato.
Con un paio di semplici Metodi scritti ad hoc e 4 Eventi ( comunque molto compatti ), il gioco è fatto.
Come si può notare, la tecnica tiene conto anche dell'eventuale eliminazione di Righe da parte dell'utente.
Inoltre L'Edit di Cella funziona in ogni caso, con Riga o Cella selezionata e non, e come è possibile notare ...


... non reca disturbi sulle pregresse selezioni.

Ovviamente il passo successivo ( che lascio ai lettori ) sarà quello di crearsi una propria Classe che eredita da DataGridView, e che conterrà questi ed altri Metodi ed Eventi, a seconda delle esigenze.

+ Fine Articolo.

Un Click su "Mi Piace" è il modo migliore per ringraziare l'autore di questo articolo.




2 commenti:

Anonimo ha detto...

come al solito posso solo dire una cosa Bravo, sei sempre
Preciso e costruttivo, complimenti hai colmato una lacuna
e te ne sono grato, almeno da parte mia

Giovanni Lacchei ha detto...

scusa non ho firmato il messaggio

Posta un commento

 
Design by Free WordPress Themes Modificato da MarcoGG