Creazione di un'interfaccia utente per l'ordinamento personalizzato (VB)
Quando si visualizza un lungo elenco di dati ordinati, può essere molto utile raggruppare i dati correlati introducendo righe separatori. In questa esercitazione verrà illustrato come creare un'interfaccia utente di ordinamento di questo tipo.
Introduzione
Quando si visualizza un lungo elenco di dati ordinati in cui sono presenti solo pochi valori diversi nella colonna ordinata, un utente finale potrebbe trovare difficile distinguere dove, esattamente, si verificano i limiti della differenza. Ad esempio, nel database sono presenti 81 prodotti, ma solo nove diverse opzioni di categoria (otto categorie univoche più l'opzione NULL
). Si consideri il caso di un utente interessato a esaminare i prodotti che rientrano nella categoria Pesce. Da una pagina che elenca tutti i prodotti in un singolo controllo GridView, l'utente potrebbe decidere di scegliere di ordinare i risultati in base alla categoria, che raggruppa tutti i prodotti Pesce insieme. Dopo l'ordinamento per categoria, l'utente deve quindi eseguire la ricerca nell'elenco, cercando dove iniziano e terminano i prodotti raggruppati per pesce. Poiché i risultati sono ordinati alfabeticamente in base al nome della categoria che trova i prodotti Seafood non è difficile, ma richiede comunque di analizzare attentamente l'elenco di elementi nella griglia.
Per evidenziare i limiti tra i gruppi ordinati, molti siti Web usano un'interfaccia utente che aggiunge un separatore tra tali gruppi. I separatori come quelli mostrati nella figura 1 consentono a un utente di trovare più rapidamente un determinato gruppo e di identificarne i limiti, nonché di verificare quali gruppi distinti esistono nei dati.
Figura 1: Ogni gruppo di categorie è chiaramente identificato (fare clic per visualizzare l'immagine a dimensione intera)
In questa esercitazione verrà illustrato come creare un'interfaccia utente di ordinamento di questo tipo.
Passaggio 1: Creazione di un controllo GridView standard ordinabile
Prima di esplorare come aumentare GridView per fornire l'interfaccia di ordinamento avanzata, è prima possibile creare un controllo GridView standard ordinabile che elenca i prodotti. Per iniziare, aprire la CustomSortingUI.aspx
pagina nella PagingAndSorting
cartella . Aggiungere un controllo GridView alla pagina, impostarne la ID
proprietà ProductList
su e associarla a un nuovo ObjectDataSource. Configurare ObjectDataSource per l'uso del ProductsBLL
metodo della GetProducts()
classe per la selezione dei record.
Configurare quindi GridView in modo che contenga ProductName
solo , CategoryName
, SupplierName
e UnitPrice
BoundFields e CheckBoxField discontinued. Infine, configurare GridView per supportare l'ordinamento selezionando la casella di controllo Abilita ordinamento nello smart tag gridView (o impostandone la AllowSorting
proprietà su true
). Dopo aver apportato queste aggiunte alla CustomSortingUI.aspx
pagina, il markup dichiarativo dovrebbe essere simile al seguente:
<asp:GridView ID="ProductList" runat="server" AllowSorting="True"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ObjectDataSource1" EnableViewState="False">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:BoundField DataField="SupplierName" HeaderText="Supplier"
ReadOnly="True" SortExpression="SupplierName" />
<asp:BoundField DataField="UnitPrice" DataFormatString="{0:C}"
HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
OldValuesParameterFormatString="original_{0}" SelectMethod="GetProducts"
TypeName="ProductsBLL"></asp:ObjectDataSource>
Prenditi un attimo per visualizzare i nostri progressi finora in un browser. Nella figura 2 viene illustrato il controllo GridView ordinabile quando i dati vengono ordinati in base alla categoria in ordine alfabetico.
Figura 2: I dati ordinabili di GridView sono ordinati per categoria (fare clic per visualizzare l'immagine a dimensione intera)
Passaggio 2: Esplorazione delle tecniche per l'aggiunta delle righe di separatore
Con il completamento di GridView generico ordinabile, tutto ciò che rimane consiste nell'aggiungere le righe separatori in GridView prima di ogni gruppo ordinato univoco. Ma come è possibile inserire tali righe in GridView? Essenzialmente, è necessario scorrere le righe di GridView, determinare dove si verificano le differenze tra i valori nella colonna ordinata e quindi aggiungere la riga separatore appropriata. Quando si pensa a questo problema, sembra naturale che la soluzione si trovi da qualche parte nel gestore eventi di RowDataBound
GridView. Come illustrato nell'esercitazione Sulla formattazione personalizzata basata sui dati , questo gestore eventi viene comunemente usato quando si applica la formattazione a livello di riga in base ai dati della riga. Tuttavia, il RowDataBound
gestore eventi non è la soluzione, perché le righe non possono essere aggiunte a GridView a livello di codice da questo gestore eventi. La raccolta di Rows
GridView, infatti, è di sola lettura.
Per aggiungere altre righe a GridView, sono disponibili tre opzioni:
- Aggiungere queste righe separatori di metadati ai dati effettivi associati a GridView
- Dopo che GridView è stato associato ai dati, aggiungere altre
TableRow
istanze alla raccolta di controlli GridView - Creare un controllo server personalizzato che estende il controllo GridView ed esegue l'override di tali metodi responsabili della costruzione della struttura di GridView
La creazione di un controllo server personalizzato sarebbe l'approccio migliore se questa funzionalità fosse necessaria in molte pagine Web o in diversi siti Web. Tuttavia, comporta piuttosto un po 'di codice e un'esplorazione approfondita delle profondità dei lavori interni di GridView. Di conseguenza, non si considererà questa opzione per questa esercitazione.
Le altre due opzioni aggiungono righe separatori ai dati effettivi associati a GridView e modificano la raccolta di controlli di GridView dopo il limite, attaccano il problema in modo diverso e meritano una discussione.
Aggiunta di righe ai dati associati a GridView
Quando GridView è associato a un'origine dati, crea un GridViewRow
oggetto per ogni record restituito dall'origine dati. È quindi possibile inserire le righe separatori necessarie aggiungendo record separatori all'origine dati prima di associarlo a GridView. La figura 3 illustra questo concetto.
Figura 3: Una tecnica prevede l'aggiunta di righe separatori all'origine dati
Io uso i record separatori termini tra virgolette perché non c'è alcun record separatore speciale; è necessario in qualche modo contrassegnare che un determinato record nell'origine dati funge da separatore anziché come riga di dati normale. Per gli esempi, viene eseguito il binding di un'istanza ProductsDataTable
a GridView, costituita da ProductRows
. È possibile contrassegnare un record come riga separatore impostandone la CategoryID
proprietà su -1
(poiché tale valore non poteva esistere normalmente).
Per usare questa tecnica, è necessario eseguire i passaggi seguenti:
- Recuperare i dati a livello di codice da associare a GridView (un'istanza
ProductsDataTable
) - Ordinare i dati in base alle proprietà e
SortDirection
diSortExpression
GridView - Scorrere in
ProductsRows
,ProductsDataTable
cercando dove si trovano le differenze nella colonna ordinata - In ogni limite di gruppo inserire un'istanza di record
ProductsRow
separatore in DataTable, una su cui èCategoryID
impostata-1
(o su qualsiasi designazione sia stata deciso di contrassegnare un record come record separatore) - Dopo aver inserito le righe separatori, associare i dati a livello di codice a GridView
Oltre a questi cinque passaggi, è necessario fornire anche un gestore eventi per l'evento gridView.RowDataBound
In questo caso, si controllano ognuna DataRow
e si determina se si tratta di una riga separatore, una la cui CategoryID
impostazione era -1
. In tal caso, è probabile che si voglia modificare la formattazione o il testo visualizzato nelle celle.
L'uso di questa tecnica per inserire i limiti del gruppo di ordinamento richiede un po' più lavoro rispetto a quanto descritto in precedenza, perché è necessario fornire anche un gestore eventi per l'evento gridView e Sorting
tenere traccia dei SortExpression
valori e SortDirection
.
Modifica della raccolta di controlli di GridView dopo che è stato databound
Anziché inviare i dati prima di associarlo a GridView, è possibile aggiungere le righe separatori dopo che i dati sono stati associati a GridView. Il processo di data binding crea la gerarchia di controllo di GridView, che in realtà è semplicemente un'istanza Table
composta da una raccolta di righe, ognuna delle quali è costituita da una raccolta di celle. In particolare, l'insieme di controlli gridView contiene un Table
oggetto alla radice, un GridViewRow
oggetto (derivato dalla TableRow
classe ) per ogni record nell'oggetto DataSource
associato a GridView e un TableCell
oggetto in ogni GridViewRow
istanza per ogni campo dati in DataSource
.
Per aggiungere righe separatori tra ogni gruppo di ordinamento, è possibile modificare direttamente questa gerarchia di controlli dopo la creazione. È possibile assicurarsi che la gerarchia di controllo di GridView sia stata creata per l'ultima volta dal momento in cui viene eseguito il rendering della pagina. Pertanto, questo approccio esegue l'override del Page
metodo della Render
classe, a quel punto viene aggiornata la gerarchia di controllo finale di GridView per includere le righe separatori necessarie. La figura 4 illustra questo processo.
Figura 4: Una tecnica alternativa modifica la gerarchia di controllo di GridView (fare clic per visualizzare l'immagine a dimensione intera)
Per questa esercitazione si userà questo secondo approccio per personalizzare l'esperienza utente di ordinamento.
Nota
Il codice che sto presentando in questa esercitazione si basa sull'esempio fornito nel post di blog di Teemu Keiski , Playing a Bit with GridView Sort Grouping ( Riproduzione di un bit con gridView Sort Grouping).
Passaggio 3: Aggiunta delle righe separatori alla gerarchia di controllo di GridView
Poiché si vogliono aggiungere solo le righe separatori alla gerarchia di controllo di GridView dopo la creazione e la creazione della gerarchia di controllo per l'ultima volta in tale pagina, si vuole eseguire questa aggiunta alla fine del ciclo di vita della pagina, ma prima del rendering della gerarchia di controlli GridView effettiva in HTML. L'ultimo punto possibile in cui è possibile eseguire questa operazione è l'evento Page
della Render
classe , che è possibile eseguire l'override nella classe code-behind usando la firma del metodo seguente:
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
' Add code to manipulate the GridView control hierarchy
MyBase.Render(writer)
End Sub
Quando viene richiamato base.Render(writer)
il Page
metodo originale Render
della classe, verrà eseguito il rendering di ognuno dei controlli nella pagina, generando il markup in base alla gerarchia dei controlli. Pertanto, è fondamentale chiamare base.Render(writer)
entrambi , in modo che venga eseguito il rendering della pagina e che venga modificata la gerarchia di controllo di GridView prima di chiamare base.Render(writer)
, in modo che le righe separatori siano state aggiunte alla gerarchia di controllo di GridView prima che venga eseguito il rendering.
Per inserire le intestazioni del gruppo di ordinamento, è prima necessario assicurarsi che l'utente abbia richiesto l'ordinamento dei dati. Per impostazione predefinita, il contenuto di GridView non è ordinato e pertanto non è necessario immettere intestazioni di ordinamento di gruppo.
Nota
Se si desidera che GridView venga ordinato in base a una determinata colonna quando la pagina viene caricata per la prima volta, chiamare il metodo gridView nella Sort
prima pagina visita (ma non per i postback successivi). A tale scopo, aggiungere questa chiamata nel Page_Load
gestore eventi all'interno di un'istruzione if (!Page.IsPostBack)
condizionale. Per altre informazioni sul metodo, vedere l'esercitazione Sul paging e l'ordinamento dei dati delSort
report.
Supponendo che i dati siano stati ordinati, l'attività successiva consiste nel determinare la colonna in cui sono stati ordinati i dati e quindi analizzare le righe cercando differenze nei valori di tale colonna. Il codice seguente garantisce che i dati siano stati ordinati e trovino la colonna in base alla quale sono stati ordinati i dati:
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
' Only add the sorting UI if the GridView is sorted
If Not String.IsNullOrEmpty(ProductList.SortExpression) Then
' Determine the index and HeaderText of the column that
'the data is sorted by
Dim sortColumnIndex As Integer = -1
Dim sortColumnHeaderText As String = String.Empty
For i As Integer = 0 To ProductList.Columns.Count - 1
If ProductList.Columns(i).SortExpression.CompareTo( _
ProductList.SortExpression) = 0 Then
sortColumnIndex = i
sortColumnHeaderText = ProductList.Columns(i).HeaderText
Exit For
End If
Next
' TODO: Scan the rows for differences in the sorted column�s values
End Sub
Se GridView non è ancora stato ordinato, la proprietà gridView non SortExpression
sarà stata impostata. Pertanto, si desidera aggiungere le righe separatori solo se questa proprietà ha un valore. In caso affermativo, è necessario determinare l'indice della colonna in base al quale sono stati ordinati i dati. Questa operazione viene eseguita eseguendo un ciclo nell'insieme gridViewColumns
, cercando la colonna la cui SortExpression
proprietà è uguale alla proprietà di GridView.SortExpression
Oltre all'indice della colonna, si afferra anche la HeaderText
proprietà , usata per visualizzare le righe separatori.
Con l'indice della colonna in base al quale vengono ordinati i dati, il passaggio finale consiste nell'enumerare le righe di GridView. Per ogni riga è necessario determinare se il valore della colonna ordinata è diverso dal valore della colonna ordinata della riga precedente. In tal caso, è necessario inserire una nuova GridViewRow
istanza nella gerarchia dei controlli. Questa operazione viene eseguita con il codice seguente:
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
' Only add the sorting UI if the GridView is sorted
If Not String.IsNullOrEmpty(ProductList.SortExpression) Then
' ... Code for finding the sorted column index removed for brevity ...
' Reference the Table the GridView has been rendered into
Dim gridTable As Table = CType(ProductList.Controls(0), Table)
' Enumerate each TableRow, adding a sorting UI header if
' the sorted value has changed
Dim lastValue As String = String.Empty
For Each gvr As GridViewRow In ProductList.Rows
Dim currentValue As String = gvr.Cells(sortColumnIndex).Text
If lastValue.CompareTo(currentValue) <> 0 Then
' there's been a change in value in the sorted column
Dim rowIndex As Integer = gridTable.Rows.GetRowIndex(gvr)
' Add a new sort header row
Dim sortRow As New GridViewRow(rowIndex, rowIndex, _
DataControlRowType.DataRow, DataControlRowState.Normal)
Dim sortCell As New TableCell()
sortCell.ColumnSpan = ProductList.Columns.Count
sortCell.Text = String.Format("{0}: {1}", _
sortColumnHeaderText, currentValue)
sortCell.CssClass = "SortHeaderRowStyle"
' Add sortCell to sortRow, and sortRow to gridTable
sortRow.Cells.Add(sortCell)
gridTable.Controls.AddAt(rowIndex, sortRow)
' Update lastValue
lastValue = currentValue
End If
Next
End If
MyBase.Render(writer)
End Sub
Questo codice inizia facendo riferimento a livello di codice all'oggetto Table
trovato nella radice della gerarchia di controllo di GridView e creando una variabile stringa denominata lastValue
. lastValue
viene utilizzato per confrontare il valore della colonna ordinata della riga corrente con il valore della riga precedente. La raccolta di GridView viene Rows
quindi enumerata e per ogni riga il valore della colonna ordinata viene archiviato nella currentValue
variabile .
Nota
Per determinare il valore della colonna ordinata della riga specifica, utilizzare la proprietà della cella.Text
Questo funziona bene per BoundFields, ma non funzionerà come desiderato per TemplateFields, CheckBoxFields e così via. A breve si esaminerà come tenere conto dei campi GridView alternativi.
Le currentValue
variabili e lastValue
vengono quindi confrontate. Se differiscono, è necessario aggiungere una nuova riga separatore alla gerarchia dei controlli. Questa operazione viene eseguita determinando l'indice di nell'insieme dell'oggetto , creando nuove GridViewRow
istanze e TableCell
e quindi aggiungendo e GridViewRow
TableCell
alla gerarchia dei controlli.GridViewRow
Table
Rows
Si noti che la riga separatore s solitaria TableCell
è formattata in modo che si estende sull'intera larghezza di GridView, venga formattata usando la classe CSS e abbia la SortHeaderRowStyle
relativa Text
proprietà in modo che mostri sia il nome del gruppo di ordinamento (ad esempio Category ) che il valore del gruppo (ad esempio Beverages). Infine, lastValue
viene aggiornato al valore di currentValue
.
La classe CSS utilizzata per formattare la riga SortHeaderRowStyle
di intestazione del gruppo di ordinamento deve essere specificata nel Styles.css
file. È possibile usare qualsiasi impostazione di stile vi piace; Ho usato quanto segue:
.SortHeaderRowStyle
{
background-color: #c00;
text-align: left;
font-weight: bold;
color: White;
}
Con il codice corrente, l'interfaccia di ordinamento aggiunge intestazioni di gruppo di ordinamento durante l'ordinamento in base a qualsiasi BoundField (vedere la figura 5, che mostra uno screenshot durante l'ordinamento in base al fornitore). Tuttavia, quando si esegue l'ordinamento in base a qualsiasi altro tipo di campo (ad esempio CheckBoxField o TemplateField), le intestazioni del gruppo di ordinamento non sono disponibili (vedere la figura 6).
Figura 5: L'interfaccia di ordinamento include intestazioni di gruppo di ordinamento durante l'ordinamento in base a BoundFields (fare clic per visualizzare l'immagine a dimensione intera)
Figura 6: Le intestazioni del gruppo di ordinamento non sono presenti durante l'ordinamento di un campo CheckBox (fare clic per visualizzare l'immagine a dimensione intera)
Il motivo per cui le intestazioni del gruppo di ordinamento non sono presenti durante l'ordinamento in base a un controllo CheckBoxField è dovuto al fatto che il codice utilizza solo la TableCell
proprietà s Text
per determinare il valore della colonna ordinata per ogni riga. Per CheckBoxFields, la TableCell
proprietà s Text
è una stringa vuota, ma il valore è disponibile tramite un controllo Web CheckBox che si trova all'interno dell'insiemeTableCell
.Controls
Per gestire i tipi di campo diversi da BoundFields, è necessario aumentare il codice in cui è assegnata la variabile per verificare l'esistenza currentValue
di un controllo CheckBox nella TableCell
raccolta di .Controls
Anziché usare currentValue = gvr.Cells(sortColumnIndex).Text
, sostituire questo codice con quanto segue:
Dim currentValue As String = String.Empty
If gvr.Cells(sortColumnIndex).Controls.Count > 0 Then
If TypeOf gvr.Cells(sortColumnIndex).Controls(0) Is CheckBox Then
If CType(gvr.Cells(sortColumnIndex).Controls(0), CheckBox).Checked Then
currentValue = "Yes"
Else
currentValue = "No"
End If
' ... Add other checks here if using columns with other
' Web controls in them (Calendars, DropDownLists, etc.) ...
End If
Else
currentValue = gvr.Cells(sortColumnIndex).Text
End If
Questo codice esamina la colonna TableCell
ordinata per la riga corrente per determinare se sono presenti controlli nella Controls
raccolta. In caso affermativo, e il primo controllo è un controllo CheckBox, la currentValue
variabile è impostata su Sì o No, a seconda della proprietà CheckBox.Checked
In caso contrario, il valore viene ricavato dalla TableCell
proprietà s Text
. Questa logica può essere replicata per gestire l'ordinamento per tutti i campi template che possono esistere in GridView.
Con l'aggiunta del codice precedente, le intestazioni del gruppo di ordinamento sono ora presenti durante l'ordinamento in base al campo CheckBox interrotto (vedere la figura 7).
Figura 7: Le intestazioni del gruppo di ordinamento sono ora presenti durante l'ordinamento di un campo CheckBox (fare clic per visualizzare l'immagine a dimensione intera)
Nota
Se sono presenti prodotti con NULL
valori di database per i CategoryID
campi , SupplierID
o UnitPrice
, tali valori verranno visualizzati come stringhe vuote in GridView per impostazione predefinita, ovvero il testo della riga separatore per tali prodotti con NULL
valori verrà letto come Category: (ovvero, non vi è alcun nome dopo Category: like with Category: Beverages). Se si desidera visualizzare un valore qui, è possibile impostare la proprietà BoundFields NullDisplayText
sul testo che si desidera visualizzare oppure aggiungere un'istruzione condizionale nel metodo Render quando si assegna alla currentValue
proprietà della Text
riga separatore.
Riepilogo
GridView non include molte opzioni predefinite per personalizzare l'interfaccia di ordinamento. Tuttavia, con un po' di codice di basso livello, è possibile modificare la gerarchia di controllo di GridView per creare un'interfaccia più personalizzata. In questa esercitazione è stato illustrato come aggiungere una riga separatore di gruppi di ordinamento per un controllo GridView ordinabile, che identifica più facilmente i gruppi distinti e i limiti di tali gruppi. Per altri esempi di interfacce di ordinamento personalizzate, vedere La voce di blog Suggerimenti e consigli per l'ordinamento di Scott Guthrie s A Few ASP.NET 2.0 GridView .
Buon programmatori!
Informazioni sull'autore
Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, lavora con le tecnologie Web Microsoft dal 1998. Scott lavora come consulente indipendente, formatore e scrittore. Il suo ultimo libro è Sams Teach Yourself ASP.NET 2.0 in 24 ore. Può essere raggiunto all'indirizzo mitchell@4GuysFromRolla.com. o tramite il suo blog, disponibile all'indirizzo http://ScottOnWriting.NET.