In questo articolo vedremo alcuni aspetti dell'utilizzo della DataGrid. La DataGrid è un Web Server Control introdotto con Asp.Net che ci permette di visualizzare i dati in forma tabellare. Faremo una breve premessa sulla DataView. Utilizzeremo la DataView assieme alla DataTable per visualizzare i dati in una DataGrid di esempio. Vedremo poi l'ordinamento, la paginazione dei records e l'applicazione di filtri della DataGrid. Infine, vedremo come sia possibile associare agli elementi delle datagrid del codice JavaScript utilizzando l'evento ItemDataBound. Faremo in modo che al passaggio del mouse cambi il colore delle righe. Come ambiente di riferimento usiamo Visual Studio 2003 in italiano. Il linguaggio è VB.Net. Una demo dell'applicazione è disponibile qui.
Premessa: la classe DataView
Una DataView è un oggetto che contiene una DataTable. Perché dovremmo voler mettere dentro una DataView una DataTable? Il motivo è che la DataView mette a disposizione dello sviluppatore una serie di funzionalità molto utili. In particolare, in questo breve accenno alla DataView, vedremo più da vicino le funzioni di filtro sui records e di ordinamento.
Come detto, la DataView è un contenitore per la DataTable. Nel seguente esempio creiamo una istanza della DataView mettendoci dentro una DataTable esistente:
Dim m_clientiView As New DataView(m_clientiTable)
Ora che abbiamo creato un oggetto DataView m_clientiView mettendoci dentro una DataTable m_clientiTable possiamo applicare un filtro ai dati. Questa caratteristica è offerta dalla funzione RowFilter della DataView. Nel seguente esempio, premesso che nella DataTable ci sono una serie di records di ipotetici clienti con una colonna Città, applicheremo un filtro per creare una vista sui dati della DataTable con solo i clienti che rispettano la condizione Citta = Roma:
m_clientiView.RowFilter = "Citta = 'Roma'"
La sintassi utilizzabile è simile a quella dell'Sql. Si possono concatenare più condizioni utilizzando l'operatore AND e si può usare l'operatore LIKE. Nel seguente esempio otteniamo tutti i records che iniziano per "a" utilizzando una ipotetica colonna chiamata Nome:
m_clientiView.RowFilter = "Nome LIKE 'a%'"
Un'altra utilità della DataView è offerta dalla funzione Sort che ci permette di ordinare i records specificando una o più colonne. Nel seguente esempio applichiamo un ordinamento dei records per Regione:
m_clientiView.Sort = "Regione"
Per le finalità di questo articolo sulla DataGrid ci fermiamo qui, facendo però presente che la DataView ha anche altre funzionalità molto utili come quella offerta dalla proprietà RowStateFilter che dà la possibilità di visualizzare solo i records per esempio il cui stato è Deleted, Added o ModifiedCurrent. Per questa e altre funzionalità consigliamo di approfondire l'argomento DataView sulla guida in linea di Visual Studio o su MSDN.
Colleghiamo la DataGrid alla fonte dati
Come con altri Web Server Controls, anche per la Datagrid è possibile specificare nella sua proprietà DataSource il nome di una fonte dati che può essere rappresentata da una DataTable, una DataView, un DataReader (o un nostro oggetto custom che implementi l'interfaccia IEnumerable).
Per questo esempio, abbiamo deciso di utilizzare il database Access "NorthWind.mdb" e la tabella "Categorie " in esso contenuta. Solitamente questo database viene installato quando si installa Access e si può trovare nella directory di installazione del programma es. "C:ProgrammiMicrosoft OfficeOfficeSamples".
L'obiettivo che ci poniamo è quello di creare una DataGrid che visualizzi all'utente il contenuto della tabella Categorie del database NorthWind e che offra funzioni di ordinamento delle colonne, di paginazione e di filtro sui dati. Cercheremo quindi di limitare al minimo indispensabile le richieste al database. Per raggiungere questo scopo, valorizzeremo inizialmente una DataTable con il contenuto della tabella Categorie e metteremo la DataTable in una variabile di sessione. Successivamente ogni qualvolta avremo bisogno di leggere i dati, faremo riferimento al contenuto di questa variabile di sessione presente in memoria. Questa tecnica, non è consigliabile nel caso di grandi quantità di dati e comunque può essere opportunamente modificata per essere adattata alle proprie esigenze utilizzando altri metodi per valorizzare e memorizzare la DataTable (es. mettendola in una variabile cache se non ci sono modifiche dei dati tra un utente e l'altro).
Creiamo un nuovo progetto web. Trasciniamo sulla web form (es. WebForm1.aspx) una DataGrid dalla casella degli strumenti. Rinominiamo la Datagrid con il nome grid. Alla proprietà DataSource della DataGrid assegneremo una DataView restituita da una semplice funzione che leggerà i dati presenti nella variabile di sessione. Per fare ciò, dopo aver selezionato la DataGrid nella finestra delle proprietà individuiamo la proprietà DataSource e scriviamo il nome che abbiamo dato alla funzione: GridSource.
In questo modo, ogni volta che vorremo ricaricare la DataGrid dai dati contenuti nella variabile di sessione, basterà chiamare il metodo "DataBind" per eseguire la seguente funzione di nome "GridSource":
Public Function GridSource(Optional ByVal _reload As Boolean = False) As DataView
Dim dv As DataView
If Session("dtCategorie") Is Nothing Or _reload = True Then
dv = New DataView(CaricaDati)
Else
dv = New DataView(CType(Session("dtCategorie"), DataTable))
End If
Return dv
End Function
Come si può vedere, la funzione "GridSource" restituisce una DataView. Questa funzione non si collega direttamente con il database, ma si limita a verificare che ci sia una DataTable in una variabile di sessione di nome "dtCategorie" e in caso negativo chiama in causa l'altra funzione "caricaDati" che vedremo fra poco. Il parametro opzionale "_reload" ci serve nel caso in cui vogliamo forzare per qualche motivo il refresh dei dati in memoria con quelli presenti nel database.
Per leggere i dati dal database, utilizziamo quindi un'altra funzione che si occupa di riempire una DataTable, mettere la DataTable in una variabile di sessione, restituire la DataTable stessa. Abbiamo chiamato questa funzione "caricaDati". Eccone di seguito l'esempio di codice:
Private Function caricaDati() As DataTable
Dim m_dap As New OleDb.OleDbDataAdapter("SELECT * FROM Categorie", ConfigurationSettings.AppSettings("ConnectionString"))
Dim dtCategorie As New DataTable("Categorie")
m_dap.Fill(dtCategorie)
Session("dtCategorie") = dtCategorie
Return dtCategorie
End Function
Prima di continuare nell'articolo, provvediamo a modificare la DataGrid in modo da visualizzare all'utente solo le colonne "NomeCategoria" e "Descrizione". Per farlo, in visualizzazione Progettazione della web form clicchiamo il tasto destro sulla DataGrid e selezioniamo la voce Generatore di proprietà:
Nella finestra delle proprietà selezioniamo la voce Colonne nel menu a sinistra e aggiungiamo due Colonne associate (bound column). Sempre nella stessa finestra modifichiamo i dati come segue:
Per la colonna NomeCategoria...
Testo intestazione: Nome categoria
Campo dati: NomeCategoria
Per la colonna Descrizione...
Testo intestazione = Descrizione
Campo dati: Descrizione
Impostiamo come campo dati il nome del campo presente nella tabella del database mentre come Testo intestazione la scritta che vogliamo appaia come intestazione della colonna nella DataGrid.
Non dimentichiamoci di aggiungere nel page_load che è la routine che viene eseguita ad ogni richiesta della pagina web il seguente codice che ci permette di effettuare il caricamento iniziale dei dati e la loro visualizzazione nella DataGrid:
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Inserire qui il codice utente necessario per inizializzare la pagina
If Not Page.IsPostBack Then
Page.DataBind()
End If
End Sub
Per questa prima parte è tutto. Vedremo ulteriori funzionalità: l'ordinamento delle righe, la paginazione, l'applicazione di filtri ai dati.
Se vogliamo dare la possibilità all'utente di cliccare sulla intestazione della colonna ed ordinare alfabeticamente tutte le righe presenti nella DataGrid, dobbiamo attivare la funzione di ordinamento. Per farlo abbiamo bisogno di:
- Attivare una proprietà Consenti ordinamento della DataGrid.
- Assegnare una Espressione di ordinamento alle colonne per le quali vogliamo attivare l'ordinamento.
- Scrivere una procedura che gestisca la colonna scelta dall'utente e il verso di ordinamento ascendente (dalla A alla Z) o discendente (dalla Z alla A).
Con il tasto destro sulla DataGrid selezioniamo la voce Generatore di proprietà. Nella sezione Generale, attiviamo l'ordinamento mettendo il check sulla proprietà Consenti ordinamento.
Una volta effettuata questa operazione se guardiamo nella visualizzazione html della web form troveremo AllowSorting="True" nel codice della DataGrid insieme alle altre dichiarazioni.
Il prossimo passo è quello di impostare una valore chiamato Espressione di Ordinamento per ogni colonna che vogliamo rendere ordinabile. Questa impostazione si può inserire sempre nella finestra Generatore proprietà della DataGrid nella voce Colonne. Il valore da inserire sotto la voce Espressione di Ordinamento corrisponde al nome della colonna nel database e quindi nel nostro esempio "NomeCategoria" e "Descrizione".
A questo punto non ci resta che scrivere una procedura che gestisca il click dell'utente sull'intestazione della colonna per ordinare tutte le righe della DataGrid. L'evento che viene scatenato in questi casi è "OnSortCommand". La procedura che vedremo fa uso del ViewState per memorizzare l'ordinamento e il verso (ascendente o discendente) prescelto dall'utente:
Public Sub grid_Sort(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs)
' Memorizzo in due variabili le impostazioni di ordinamento correnti
Dim strSortBy As String = ViewState("grid_SortExpression")
Dim strSortAscending As String = ViewState("grid_SortAscending")
' Imposto la nuova espressione di ordinamento selezionata
ViewState("grid_SortExpression") = e.SortExpression
' Faccio in modo che dopo aver cliccato sull'header mi inverte l'ordinamento
If (e.SortExpression = strSortBy) Then
If strSortAscending = "yes" Then
ViewState("grid_SortAscending") = "no"
ElseIf strSortAscending = "no" Then
ViewState("grid_SortAscending") = "yes"
End If
Else
' Imposto l'ordinamento di default
ViewState("grid_SortAscending") = "yes"
End If
' Chiamo il DataBind per visualizzare nella DataGrid
' le modifiche di ordinamento nella DataView appena effettuate
grid.DataBind()
End Sub
Il ViewState è un nuovo oggetto introdotto da ASP.Net che si presta bene per memorizzare quando necessario, oltre allo stato dei controlli, anche piccole quantità di dati come in questo caso.
Paginazione dei dati
Quando le righe da visualizzare nella DataGrid non sono poche, potremmo sentire la necessità di paginare i risultati, cioè suddividere il numero totale di righe in più pagine che l'utente può navigare selezionando i pulsanti < > oppure i numeri di pagina solitamente posti in fondo alla DataGrid oppure in alto.
La DataGrid di Asp.Net ci offre un sistema semi-automatico per la paginazione. Quel "semi" sta ad indicare che comunque c'è bisogno di un minimo di codice da parte dello sviluppatore per far funzionare il sistema.
I passaggi da fare sono:
- Attivare la paginazione nelle proprietà della DataGrid
- Scrivere una procedura per gestire il cambio della pagina
Per attivare la paginazione, sulla DataGrid con il tasto destro su Generatore proprietà nella voce Paging è necessario attivare l'opzione Consenti paging in modo da aggiungere alla griglia una barra che contenga i numeri di pagina o del testo (es. Avanti e Indietro) necessari allo spostamento delle pagine. Una volta attivata questa opzione, le altre impostazioni sono:
La procedura che abbiamo chiamato CambioPagina che è necessario scrivere per gestire l'evento OnPageIndexChanged è la seguente:
Public Sub CambioPagina(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs)
grid.EditItemIndex = -1
grid.CurrentPageIndex = e.NewPageIndex
grid.DataBind()
End Sub
impostiamo quindi nella visualizzazione HTML della web form insieme alla altre dichiarazioni della DataGrid il seguente codice che ci permette di collegare all'evento la nostra procedura
OnPageIndexChanged=CambioPagina
Applichiamo un filtro sui dati
Ora vediamo come dare la possibilità all'utente di fare una ricerca e visualizzare i risultati. In termine di linee di codice necessarie, il lavoro è piuttosto facile visto che lavoriamo con una DataView che grazie al metodo RowFilter si presta bene a creare filtri sui dati e in più grazie alla scelta che l'utente fa nel momento in cui ordina i dati, possiamo usare questa scelta per dargli la possibilità di fare ricerca su ogni campo ordinabile e non solo su quelli che decidiamo noi.
Trasciniamo sulla web form un panel, una label, una textbox, un pulsante e diamogli rispettivamente i nomi panelFiltro, lblFiltro, txtFiltro e btnFiltro.
Mettete dentro il Panel la label, la textbox e il pulsante. Ora possiamo fare in modo che il panel e il suo contenuto vengano visualizzati solo se l'utente ha deciso di ordinare la DataGrid principale su qualche valore. La colonna scelta dall'utente per l'ordinamento dei records sarà anche il campo su cui applicheremo il filtro quindi fintantoché non viene scelto nessun ordinamento non faremo ricerche. Naturalmente è possibile impostare un campo di ricerca predefinito ma per adesso accontentiamoci di questo. Scriviamo quindi la seguente procedura che ci permette di visualizzare o meno i controlli a seconda che ci sia un campo su cui fare le ricerche:
Public Function Visualizza() As Boolean
If Not ViewState("grid_SortExpression") Is Nothing Then
Return True
End If
Return False
End Function
Se nella variabile ViewState("grid_SortExpression") è presente un valore allora la funzione ritorna true altrimenti è false. Possiamo quindi usarla per visualizzare il Panel di Ricerca solo quando è possibile effettuare una ricerca. Scriviamo quindi in visualizzazione HTML della web form nella dichiarazione del Panel il seguente codice
Visible="<%# Visualizza %>"
In modo che la dichiarazione completa del Panel sia la seguente
<asp:Panel id="panelFiltro" runat="server" Visible="<%# Visualizza %>">
Ora facciamo un doppio click sul pulsante btnFiltro e scriviamo la seguente procedura:
Private Sub btnFiltro_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnFiltro.Click
grid.DataBind()
End Sub
Cosi, quando l'utente preme il pulsante, chiama in causa la procedura che rinfresca la DataGrid con la fonte dati e che abbiamo chiamato GridSource. Ora non ci rimane che modificare questa procedura per fare in modo che se l'utente ha scritto qualcosa nella casella di testo allora quel qualcosa dovrà essere impiegato per fare un filtro sui dati restituiti dalla dataview. Modifichiamo quindi la procedura GridSource con il seguente codice:
Public Function GridSource(Optional ByVal _reload As Boolean = False) As DataView
' Restituisco una DataView filtrata secondo quanto contenuto
' nella variabile ViewState("dgXxx_SortExpression")
Dim dv As DataView
If Session("dtCategorie") Is Nothing Or _reload = True Then
dv = New DataView(caricaDati)
Else
dv = New DataView(CType(Session("dtCategorie"), DataTable))
End If
dv.Sort = ViewState("grid_SortExpression")
If (ViewState("grid_SortAscending") = "no") Then
dv.Sort += " DESC"
End If
If txtFiltro.Text.Length > 0 Then
dv.RowFilter = String.Format("{0} like '{1}%'", ViewState("grid_SortExpression"), txtFiltro.Text)
Else
dv.RowFilter = ""
End If
Return dv
End Function
Ricordiamoci infine di modificare la label "lblFiltro" in modo che visualizzi il nome del campo su cui si può effettuare la ricerca. Per fare questo, nella visualizzazione HTML della web form aggiungiamo alla dichiarazione della label il seguente codice:
text='<%# ViewState("grid_SortExpression") %>'
in modo che la dichiarazione completa sia come la seguente:
<asp:Label id="lblFiltro" runat="server" text='<%# ViewState("grid_SortExpression") %>'>
Coloriamo le righe al passaggio del mouse
Grazie a javascript possiamo aggiungere alla nostra DataGrid un simpatico effetto e cioè quello che al passaggio del mouse le righe vengano evidenziate per una migliore leggibilità. Questo esempio ci renderà più chiaro se ce ne fosse bisogno la possibilità di associare ad ogni elemento della DataGrid un codice Javascript e in più ci permette di operare su un evento importante che è OnItemDataBound. Questo evento, si scatena quando le righe vengono associate alla DataGrid e prima che avvenga il definitivo rendering della pagina. Rappresenta quindi l'ultima possibilità che abbiamo per modificare l'aspetto delle righe prima che queste vengano visualizzate all'utente. Gestendo questo evento, è ad esempio possibile colorare le righe a seconda del contenuto di esse. Ma torniamo al nostro esempio.
Dobbiamo innanzitutto definire nel codice della nostra DataGrid quale è il nome della procedura che si occupa di gestire l'evento OnItemDataBound. Per farlo è necessario in visualizzazione HTML della nostra web form inserire nella dichiarazione della DataGrid il seguente codice:
OnItemDataBound="grid_ItemDataBound"
a questo punto scriviamo la seguente procedura:
Public Sub grid_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs)
If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType = ListItemType.AlternatingItem Then
'---------------------------------------------------
' Aggiunge gli script onmouseover e onmouseout alle righe della DataGrid
'---------------------------------------------------
e.Item.Attributes.Add("onmouseover", "this.style.backgroundColor='#DDEEFF'")
e.Item.Attributes.Add("onmouseout", "this.style.backgroundColor='white'")
End If
End Sub
Come possiamo notare, nella procedura viene fatto un controllo per essere sicuri di effettuare le successive modifiche solo su Item e AlternatingItem della DataGrid. In questa maniera vengono ad esempio escluse le righe di header, footer e pager. Una volta trovata una riga che interessa, attraverso la collection Attributes della classe item possiamo aggiungere uno o più attributi che altro non sono che codice script lato client iniettato nella pagina.
Conclusione
Concludiamo questo articolo sperando di aver offerto spunti interessanti da utilizzare nei propri progetti. L'applicazione di esempio che accompagna l'articolo contiene tutto quanto detto e in più anche le procedure di update e delete delle righe oltre ad una procedura per il salvataggio nel database di tutte le modifiche apportate dall'utente.