giovedì 30 luglio 2009

VB.NET : GridView con paginazione selezionata dall'utente

Sul sito della community romana del mondo .NET (www.dotnetromacesta.org) trovate un mio piccolo articolo su come realizzare un controllo ASP.NET, derivato dal controllo GridView, che permette all’utente di selezionare il numero di record da visualizzare.

Per semplicità riporto l’intero articolo rimandandovi al link di cui sopra per scaricare il file con il codice.

La nostra GridView modificherà il pager della GridView originale suddividendolo in due colonne, la prima delle quali conterrà il controllo di paginazione originale (normalmente contenuto in una cella con l’attributo ColumnSpan pari al numero di colonne della griglia), mentre la seconda conterrà una label e una drop down list con i possibili valori della paginazione (recuperati da una nuova proprietà che l’utente può impostare da codice).

Creiamo, dunque una classe che chiameremo DynamicPageSizeGridView e che deriverà dalla GridView contenuta nel namespace System.Web.UI.WebControls.

Definiamo tre proprietà pubbliche:

PageSizeList : elenco dei possibili valori selezionabili dall’utente. Si tratta di una stringa composta da valori numerici separati dal carattere “;” ;

PageSelectorText : testo che comparirà al fianco del controllo di selezione del numero di record;

PageSelectorCssClass : classe di stile della cella contenente la label e la dropdown di selezione.

Tutte e tre le proprietà sfruttano il viewstate per memorizzare i valori in modo da mantenerli tra un post back ed il successivo:

Public Property PageSelectorText() As String
   Get
        Dim _PageSelectorText = Me.ViewState("PageSelectorText")
        Return CStr(_PageSelectorText)
    End Get
    Set(ByVal value As String)
        Me.ViewState("PageSelectorText") = value
    End Set
End Property

Per creare fisicamente i controlli di gestione della paginazione, andiamo a gestire l’evento RowCreated ed in particolare ci occupiamo di agire sulla riga di tipo Pager:

Private Sub DynamicPageSizeGridView_RowCreated( _

        ByVal sender As Object, _
        ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) _
        Handles Me.RowCreated
    If e.Row.RowType = DataControlRowType.Pager Then
        Dim pagerControl = e.Row.Cells(0).Controls(0)
        e.Row.Cells(0).Controls.Clear()
        e.Row.Cells(0).Controls.Add(CreatePagerTable(pagerControl))
    End If
End Sub

In particolare andiamo a salvarci il controllo PagerTable che contiene il controllo di paginazione originale (che si trova nell’unica cella della riga di paginazione) e creiamo una tabella con una riga e due colonne. Nella prima di queste colonne (entrambe che occupano il 50% dello spazio) rimettiamo il paginatore originale, mentre nella seconda costruiamo i due controlli per la selezione del numero di record per pagina.

Function CreatePagerTable(ByVal pagerControl As Control) As Table
    Dim table = New Table()
    Dim row As New TableRow
    Dim pagerCell As New TableCell
    With pagerCell
        .Width = Unit.Percentage(50)
        .Controls.Add(pagerControl)
    End With
    row.Cells.Add(pagerCell)
    Dim sizeCell As New TableCell
    With sizeCell
        .CssClass = Me.PageSelectorCssClass
        .Width = Unit.Percentage(50)
        Dim label = New Label()
        label.Text = Me.PageSelectorText
        .Controls.Add(label)
        .Controls.Add(CreatePageSizeListDropDown())
    End With
    row.Cells.Add(sizeCell)
    table.Rows.Add(row)
    table.Width = Unit.Percentage(100)
    Return table
End Function

Nella funzione CreatePageSizeListDropDown creiamo la dropdown per la selezione della dimensione di paginazione:

Private Function CreatePageSizeListDropDown() As DropDownList
    Dim combo = New DropDownList()
    combo.Items.AddRange((From s In Me.PageSizeList.Split(CChar(";")) _
                      Where IsNumeric(s) _
                      Order By s Ascending _
                      Distinct Select New ListItem(s, s)).ToArray())
    combo.AutoPostBack = True
    combo.SelectedValue = Me.PageSize.ToString()
    AddHandler combo.SelectedIndexChanged, _
    AddressOf Me.ComboPageSizeSelectionChanged
    Return combo
End Function

Da notare l’utilizzo di LINQ per creare la lista dei valori possibili di paginazione contenuti nella proprietà PageSizeList.

Nell’evento di cambio dell’indice della dropdown effettuiamo il cambio di paginazione:

Protected Sub ComboPageSizeSelectionChanged( _
        ByVal sender As Object, _
        ByVal e As EventArgs)
    Dim combo = CType(sender, DropDownList)
    Dim selectedValue = combo.SelectedItem.Value
    Me.PageSize = System.Convert.ToInt32(selectedValue)
    Me.PageIndex = 0
End Sub

Recuperiamo il valore selezionato dall’utente, impostiamo il valore del PageSize e riportiamo il PageIndex a zero. Quest’ultima istruzione serve a garantire che, se ci si trova in una pagina diversa dalla prima e si seleziona una paginazione per cui esiste una sola pagina, non si abbia un errore.

A questo punto tutto sembra funzionare ma se selezioniamo una paginazione con un numero di record maggiore del numero di record totali della sorgente dati, scompare la riga di paginazione non consentendo più di cambiare il valore selezionato.

Per risolvere questo problema ci viene in aiuto un post di Ryan McDonnell(link) in cui si spiega come fare in modo di visualizzare sempre la riga di paginazione anche se il numero di record totali è minore del numero di record della pagina.

In sostanza è sufficiente implementare l’override della proprietà PageCount (utilizzata dal frame work per sapere quante pagine servono per visualizzare tutti i dati):

Public Overrides ReadOnly Property PageCount() As Integer
    Get
        Dim basePageCount = MyBase.PageCount
        If basePageCount = 1 Then
            Dim sf = New System.Diagnostics.StackFrame(1)
            If sf.GetMethod().Name = "CreateChildControls" _
                And sf.GetMethod().ReflectedType.Name = "GridView" Then
                    basePageCount += 1
            End If
        End If
        Return basePageCount
    End Get
End Property

Nel momento in cui ci si accorge che il numero di pagine necessarie per visualizzare i dati è pari ad 1 (cioè non viene visualizzata la riga di paginazione), si incrementa tale valore in modo da “ingannare” il framework e fargli , comunque, disegnare la riga di paginazione (priva del paginatore perché, effettivamente, non ci sono record su cui navigare).

In questo modo la nostra griglia è perfettamente funzionante.

Da notare che il tutto funziona sia con la paginazione lato server che con quella lato client.

Il codice sorgente è disponibile al seguente indirizzo

http://www.dotnetromacesta.org/common/articoli/aspnet/gridview/AspNet_GridView_Paginazione.aspx

Nessun commento: