|

La DataGridView è tra i controlli più vasti e meno intuitivi messi a disposizione da VisualStudio.
E' stata introdotta col Framework 2.0 a sostituzione della DataGrid (che è ancora presente nelle applicazioni ASP).
E' un controllo molto potente ma con diverse limitazioni. Nel Framework 3.5 sono state apportate delle migliorie ma non così significative da far diminuire il divario di competitività con gli altri controlli griglia prodotti da terze parti.
Comunque con un pò di pazienza (e tanto codice) si possono raggiungere notevoli risultati anche con essa.
Per chi fosse interessato ad approfondire la sua struttura consiglio di leggere questo white paper e soprattutto la guida MSDN.
Ci vorrebbe un libro intero per descriverne approfonditamente tutte le potenzialità (una tra tutte la possibilità di personailizzarla mediante l'ereditarietà delle sue cellle). Pertanto questo articolo affronterà solamente le proprietà che influiscono sul suo aspetto grafico.
Lavoreremo con dei dati disconnessi, ossia non collegheremo la griglia ad una sorgente dati bensì creeremo direttamente colonne e righe tramite l'IDE per prendere confidenza con la sua interfaccia.
Solitamente la struttura di ogni tabella è data dalla combinazione di una o più colonne che, ad eccezione dei fogli di calcolo tipo Excel, determinano il tipo di dato presente in tutte le celle in esse contenute.
La DataGridView mette a disposizione 6 tipi di colonne:
- DataGridViewButtomColumn (visualizza una colonna contenente dei pulsanti);
- DataGridViewCheckBoxColumn (visualizza una colonna contenente delle CheckBox);
- DataGridViewComboBoxColumn (visualizza una colonna contenente delle caselle a discesa);
- DataGridViewImageColumn (visualizza una colonna contenente delle immagini);
- DataGridViewLinkColumn (visualizza una colonna contenente dei collegamenti ipertestuali);
- DataGridViewTextBoxColumn (visualizza una colonna contenente dei valori alfanumerici).
Iniziamo il nostro esercizio col trascinare il controllo DataGridView dalla Casella degli Strumenti all'interno di una Form

A questo punto creiamo alcune colonne per visualizzare dei campi di comune utilità tipo:
- testo;
- numerico e monetario;
- collegamento ipertestuale.
Clicchiamo sullo SmartTag della DataGridView (il piccolo triangolo nero posto in alto a destra dei controlli).

Clicchiamo sulla voce di menu 'Aggiungi colonna' ed aggiungiamo sei colonne lasciando impostati i valori di default.

Da notare che il tipo di colonna di preselezionato è DataGridViewTextBoxColumn, ossia il tipo di colonna usato più comunemente per visualizzare dei valori alfanumerici.
Lasceremo le impostazioni delle proprietà di default perchè è utile sapere che si possono modificare in qualunque momento. Ogni proprietà delle DataGridViewColumn può essere modificate in fase di esecuzione tranne la proprietà ColumnType. Questo perchè ogni colonna è un determinato tipo di Classe, ossia un oggetto a se che eredita da un altro oggetto specifico e ben definito. Se si necessita di modificare il tipo di DataGridViewColum a runtime si è costretti a crearne dinamicamente una nuova del tipo desiderato ed a sostituire quella indesiderata.

Per accedere alla finestra delle proprietà delle DataGridViewColumns è possibile cliccare sullo SmartTag della DataGridView e poi selezionare lla voce di menu 'Modifica colonne', oppure è possibile andare nella finestra Proprietà della DataGridView e cliccare sulla voce 'Columns'.
Apparirà la finestra 'Modifica colonne', l'interfaccia fondamentale per gestire velocemente le proprietà delle DataGridViewColumns:

Tramite la sezione 'Progettazione' della PropertyGrid si possono modificare tutte le proprietà di ogni singola colonna.
Cambiamo il testo dell'intestazione delle colonne modificando la loro prorprietà 'HeaderText'.

Impostiamo i seguenti titoli: Id, Testo, Numero, Valuta, Link, Check, Button.
Creiamo una piccola routine per popolare la griglia con dei valori casuali:
Sub FillRandom()
' Aggiunge 10 righe vuote alla griglia.
Me.DataGridView1.Rows.Add(10)
' Riempie la griglia con dei valori casuali.
For rigo As Integer = 0 To 9
For colonna As Integer = 0 To Me.DataGridView1.ColumnCount - 1
With Me.DataGridView1.Rows(rigo).Cells(colonna)
Select Case Me.DataGridView1.Columns(colonna).HeaderText
Case "Id"
.Value = rigo + 1
Case "Testo"
.Value = _
Globalization.CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(rigo + 1)
Case "Numero"
Dim rnd As New Random(rigo)
.Value = rnd.Next(100000)
Case "Valuta"
Dim rnd As New Random(rigo)
.Value = rnd.NextDouble * rnd.Next(10000)
Case "Link"
.Value = String.Format("{0}{1}.html", "http://www.dotnetwork.it/", rigo)
Case "Check"
.Value = rigo Mod (2)
End Select
End With
Next
Next
End Sub
public void FillRandom()
{
// Aggiunge 10 righe vuote alla griglia.
this.DataGridView1.Rows.Add(10);
// Riempie la griglia con dei valori casuali.
for (int rigo = 0; rigo <= 9; rigo++) {
for (int colonna = 0; colonna <= this.DataGridView1.ColumnCount - 1; colonna++) {
{
switch (this.DataGridView1.Columns[colonna].HeaderText) {
case "Id":
this.DataGridView1.Rows[rigo].Cells[colonna].Value = rigo + 1;
break;
case "Testo":
this.DataGridView1.Rows[rigo].Cells[colonna].Value =
Globalization.CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(rigo + 1);
break;
case "Numero":
Random rnd = new Random(rigo);
this.DataGridView1.Rows[rigo].Cells[colonna].Value = rnd.Next(100000);
break;
case "Valuta":
Random rnd = new Random(rigo);
this.DataGridView1.Rows[rigo].Cells[colonna].Value =
rnd.NextDouble * rnd.Next(10000);
break;
case "Link":
this.DataGridView1.Rows(rigo).Cells(colonna).Value =
string.Format("{0}{1}.html", "http://www.dotnetwork.it/", rigo);
break;
case "Check":
this.DataGridView1.Rows[rigo].Cells[colonna].Value = rigo % (2);
break;
}
}
}
}
}

E' importante sapere che le impostazioni visuali di default della DataGridView vengono applicate in modo omogeneo e non selettivo. L'insieme delle proprietà che determinano queste impostazioni è chiamato Style (stile).
La logica è quella di applicare gli stili ad un insieme generale di celle (tutta la griglia) e successivamente, se desiderato, applicarli ad oggetti più specifici come ad esempio intere colonne o intestazioni, fino ad arrivare alla specificità della singola cella.
Riporto un piccolo schema preso dal white paper citato all'inizio dell'articolo.

Proviamo a cambiare il carattere e l'aspetto di tutta la griglia cliccando sul pulsante presente nella proprietà DefaultCellStyle

si aprirà la finestra 'Generatore CellStyle':

Tramite questa finestra è' possibile intervenire su ogni singola proprietà dello stile di default.
Divertiamoci a modificare il font, il suo colore, il colore di sfondo di tutte le celle ed il colore della cella selezionata:

Reimpostiamo i valori di default della griglia (perchè al momento è inguardabile) e semplifichiamo la lettura dei dati colorando alternativamente le righe visualizzate. Per farlo basta specificare il colore desiderato nella proprietà BackColor all'interno dello stile 'AlternatingRowsDefaultCell'.


Evidenziamo le intestazioni di colonna modificando lo stile 'ColumnHeadersDefaultCell'.

Apriamo ancora una volta il 'Generatore di CellStyle' tramite il quale modificheremo il font e l'allineamento del testo.
I titoli delle colonne appaiono adesso in grassetto ed allineati centralmente:

Possiamo constatare che tutti i procedimenti per modificare gli stili della tabella sono molto simili tra loro. E' giunto quindi il momento di spendere due parole sulla finestra 'Generatore di CellStyle'. Abbiamo capito che essa genera semplicemente degli stili che altro non sono che Classi adibite alla formattazione degli elementi che compongono una DataGridView. Aprendo il file .Designer della nostra Form possiamo osservare come viene generato il suo codice:
Dim DataGridViewCellStyle1 As System.Windows.Forms.DataGridViewCellStyle = _
New System.Windows.Forms.DataGridViewCellStyle
Dim DataGridViewCellStyle2 As System.Windows.Forms.DataGridViewCellStyle = _
New System.Windows.Forms.DataGridViewCellStyle
...
DataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(CType(CType(192, Byte), Integer), _
CType(CType(255, Byte), Integer), CType(CType(192, Byte), Integer))
Me.DataGridView1.AlternatingRowsDefaultCellStyle = DataGridViewCellStyle1
...
DataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter
DataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Control
DataGridViewCellStyle2.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, _
System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
DataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.WindowText
DataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight
DataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText
DataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.[True]
Dando l'incombenza a VisualStudio di generare il codice degli stili, dobbiamo essere consapevoli che (teoricamente) non saremo in grado di determinare quale DataGridViewCellStyle sarà applicato ai vari componenti della nostra griglia. Per avere il massimo controllo su queste impostazioni bisognerà quindi crearli direttamente da codice.
Perfezionaiamo ancora di più la nostra griglia. Se non ci interessa visualizzare la colonna col campo 'Id' possiamo nasconderla. Per farlo bisogna accedere alla finestra 'Modifica Colonne' cliccando sullo SmartTag in alto a destra della DataGridView (come già visto in precedenza) e settare la proprietà 'Visible' su 'False'.

E' utile sapere che nel caso si utilizzasse una griglia collegata ad una sorgente dati, le colonne visualizzate non sarebbero strettamente correlate alla struttura della colonne della DataTable sottostante. Questo vuol dire che se non si volesse visualizzare una determinata DataGridViewColumn, potremmo eliminarla direttamente senza avere conseguenze sulla DataColumn corrispondente. Attenzione però, le righe sono invece strettamente correlate alla DataTable sottostante, quindi l'eliminazione di una DataGridViewRow dalla griglia comporterebbe l'eliminazione della riga bindata alla DataTable (per l'esattezza lo stato della DataRow sottostante verrebbe contrassegnato come Deleted).
Adesso formattiamo correttamente tutti i valori numerici presenti nella colonna titolata 'Numero'. Apriamo quindi la solita finestra 'Modifica colonne', nel riquadro 'Colonne selezionate' selezioniamo la colonna 'Numero' e nella sezione 'Proprietà' selezioniamo la voce 'DefaultCellStyle'. Si aprirà l'ormai familiare finestra 'Generatore di CellStyle' nella quale impostiamo d'apprima l'allineamento del testo tramite la proprietà 'Alignment' e successivamente impostiamo la proprietà 'Format'. Si aprirà la maschera 'Finestra di dialogo Stringa di formato', simile a molte altre finestre già viste in altri programmi Microsoft, primo tra tutti Excel:


NB: Se la DataGridView non è connessa ad un'origine dati, i nuovi valori immessi e quelli modificati non potranno essere formattati automaticamente. Questo succede perchè i valori delle colonne disconnesse vengono riconosciuti come valori stringa. La formattazione andrà quindi eseguita manualmente tramite codice (vedi evento .CellFormatting in fondo all'articolo).
Seguendo i passaggi appena descritti saremo in grado di formattare correttamente anche la colonna 'Valuta'.
Adesso cambieremo il tipo di DataGridViewColumn delle ultime tre colonne (Link, Check e Button).
Ricordiamoci che questo tipo di operazione può essere fatta solamente tramite IDE se non si desidera sostituire la colonna.
All'inizio dell'articolo abbiamo visto che la DataGridView offre la possibilità di utilizzare sei tipi diversi di colonne per visualizzare correttamente determinati tipi di dati. Apriamo la finestra 'Modifica colonne', selezioniamo la colonna 'Link', ed impostiamo la proprità 'ColumnType' come DataGridViewLinkColumn.
Per attivare il link visualizzato nella colonna bisogna gestire da codice l'evento CellContentClick:
Private Sub DataGridView1_CellContentClick(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
If Me.DataGridView1.Columns(e.ColumnIndex).HeaderText = "Link" Then
Process.Start(Me.DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value.ToString)
End If
End Sub
private void DataGridView1_CellContentClick(object sender,
System.Windows.Forms.DataGridViewCellEventArgs e)
{
if (this.DataGridView1.Columns[e.ColumnIndex].HeaderText == "Link") {
Process.Start(this.DataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString);
}
}
Visualizziamo adesso delle CheckBox nella colonna 'Check' seguendo la procedura appena descritta impostando la proprità 'ColumnType' come DataGridViewCheckBoxColumn.
Attenzione: le DataGridViewCheckBoxColumn devono contenere solamente valori booleani (True/False oppure 0/1...) altrimenti, durante il redraw della griglia, verranno sollevate delle eccezioni che ne impediranno l'utilizzo.
Per finire, inseriamo dei pulsanti nella colonna 'Button' impostando la sua proprietà ColumnType come DataGridViewButtonColum. L'evento generato dalla pressione dei pulsanti viene gestito sempre tramite l'evento CellContentClick della DataGridView nello stesso modo in cui è stato gestito il collegamento ipertestuale.
Private Sub DataGridView1_CellContentClick(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
If Me.DataGridView1.Columns(e.ColumnIndex).HeaderText = "Link" Then
Process.Start(Me.DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value.ToString)
ElseIf Me.DataGridView1.Columns(e.ColumnIndex).HeaderText = "Button" Then
MessageBox.Show("E' stato premuto il tasto della riga " & e.RowIndex + 1)
End If
End Sub
private void DataGridView1_CellContentClick(object sender,
System.Windows.Forms.DataGridViewCellEventArgs e)
{
if (this.DataGridView1.Columns(e.ColumnIndex).HeaderText == "Link") {
Process.Start(this.DataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString);
}
else if (this.DataGridView1.Columns[e.ColumnIndex].HeaderText == "Button") {
MessageBox.Show("E' stato premuto il tasto della riga " + e.RowIndex + 1);
}
}

Fino ad ora abbiamo applicato delle modifiche "in blocco", ossia ad un insieme di elementi. Siamo partiti col formattare l'intera griglia, poi siamo stati più specifici nel formattare le singole colonne e l'insieme delle intestazioni. Adesso opereremo in modo estremamente selettivo andando a formattare una singola cella. Questa operazione è possibile farla esclusivamente tramite codice.
Evidenziamo il numero maggiore presente nella colonna titolata 'Numeri':
' Determina il valore maggiore nella colonna 'Numeri'.
Dim max As Double
Dim rw As DataGridViewRow = Nothing
For Each row As DataGridViewRow In Me.DataGridView1.Rows
If CInt(row.Cells("Column3").Value) > max Then
max = CInt(row.Cells("Column3").Value)
rw = row
End If
Next
' Crea un nuovo stile da applicare alla cella desiderata.
Dim myStyle As New DataGridViewCellStyle
myStyle.Font = New System.Drawing.Font("Arial", 10, _
System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
myStyle.ForeColor = Color.White
' Applica lo stile alla cella col valore maggiore.
rw.Cells("Column3").Style = myStyle
' Modifica il colore di sfondo della cella.
rw.Cells("Column3").Style.BackColor = Color.Green
{
// Determina il valore maggiore nella colonna 'Numeri'.
double max;
DataGridViewRow rw = null;
foreach (DataGridViewRow row in this.DataGridView1.Rows) {
if ((int)row.Cells("Column3").Value > max) {
max = (int)row.Cells("Column3").Value;
rw = row;
}
}
// Crea un nuovo stile da applicare alla cella desiderata.
DataGridViewCellStyle myStyle = new DataGridViewCellStyle();
myStyle.Font = new System.Drawing.Font("Arial", 10,
System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, (byte)0);
myStyle.ForeColor = Color.White;
// Applica lo stile alla cella col valore maggiore.
rw.Cells("Column3").Style = myStyle;
// Modifica il colore di sfondo della cella.
rw.Cells("Column3").Style.BackColor = Color.Green;
}
Gestendo l'evento .CellFormatting della DataGridView possiamo formattare una cella in base al proprio valore contenuto (formattazione condizionale):
Private Sub DataGridView1_CellFormatting(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DataGridViewCellFormattingEventArgs) Handles DataGridView1.CellFormatting
If Me.DataGridView1.Columns(e.ColumnIndex).HeaderText = "Valuta" Then
If CDbl(Me.DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value) > 0 Then
Me.DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Style.ForeColor = _
Me.DataGridView1.DefaultCellStyle.ForeColor
Else
Me.DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Style.ForeColor = Color.Red
End If
End If
End Sub
private void DataGridView1_CellFormatting(object sender,
System.Windows.Forms.DataGridViewCellFormattingEventArgs e)
{
if (this.DataGridView1.Columns(e.ColumnIndex).HeaderText == "Valuta") {
if ((double)this.DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value > 0) {
this.DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Style.ForeColor =
this.DataGridView1.DefaultCellStyle.ForeColor;
}
else {
this.DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Style.ForeColor = Color.Red;
}
}
}

Come abbiamo già detto in precedenza, le griglie non connesse ad una sorgente dati non sono in grado di formattare automaticamente i valori digitati a mano (mentre può formattare quelli passati via codice perché sono di tipo ben definito). Bisogna quindi gestire manualmente la formattazione dei dati in caso di utilizzo di colonne disconnesse.
Questa operazione dovrebbe essere effettuata nell'evento .CellValidated della DataGridView dopo aver convalidato i dati immessi dall'utente:
Private Sub DataGridView1_CellValidated(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellValidated
If Me.DataGridView1.Columns(e.ColumnIndex).HeaderText = "Valuta" Then
Me.DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value = _
CDbl(Me.DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value).ToString("c")
End If
End Sub
private void DataGridView1_CellValidated(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
{
if (this.DataGridView1.Columns(e.ColumnIndex).HeaderText == "Valuta") {
this.DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value =
((double)this.DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value).ToString("c");
}
}
La visualizzazione dei dati in una Form è stata notevolmente migliorata in VisualStudio grazie all'introduzione del controllo DataGridView nel Framework 2.0. Purtroppo tale controllo non risulta essere ancora all'altezza dei controlli griglia prodotti da softwarehouse specializzate nealla realizzazione di controlli WinForm.
Rimane comunque un controllo di una potenza notevole, più che sufficiente a soddisfare le esigenze delle più comuni applicazioni.
|