Creazione di un'interfaccia utente per l'ordinamento personalizzato (C#)
Quando si visualizza un elenco lungo 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.
Introduzione
Quando si visualizza un elenco lungo di dati ordinati in cui sono presenti solo una manciata di valori diversi nella colonna ordinata, un utente finale potrebbe trovare difficile distinguere dove, esattamente, si verificano i limiti delle differenze. Ad esempio, nel database sono presenti 81 prodotti, ma solo nove diverse opzioni di categoria (otto categorie univoche e l'opzione NULL
). Si consideri il caso di un utente interessato ad esaminare i prodotti che rientrano nella categoria Pesce. Da una pagina che elenca tutti i prodotti in un singolo GridView, l'utente potrebbe decidere che la sua migliore scommessa consiste nell'ordinare i risultati in base alla categoria, che raggruppa tutti i prodotti Pesce insieme. Dopo l'ordinamento in base alla categoria, l'utente deve quindi cercare l'elenco, cercando dove iniziano e terminano i prodotti a raggruppamento di pesce. Poiché i risultati sono ordinati in ordine alfabetico in base al nome della categoria che individuano i prodotti Pesce non è difficile, ma richiede ancora 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 visualizzati nella figura 1 consentono a un utente di trovare più rapidamente un determinato gruppo e identificare i relativi limiti, nonché verificare quali gruppi distinti esistono nei dati.
Figura 1: Ogni gruppo di categorie è chiaramente identificato (fare clic per visualizzare l'immagine full-size)
In questa esercitazione verrà illustrato come creare un'interfaccia utente di ordinamento.
Passaggio 1: Creazione di un controllo GridView standard, ordinabile
Prima di esplorare come aumentare GridView per fornire l'interfaccia di ordinamento avanzata, consente di creare prima una griglia standard e ordinabile che elenca i prodotti. Iniziare aprendo la CustomSortingUI.aspx
pagina nella PagingAndSorting
cartella. Aggiungere gridView alla pagina, impostarne la ID
proprietà su ProductList
e associarla a un nuovo OggettoDataSource. Configurare ObjectDataSource per usare il ProductsBLL
metodo della GetProducts()
classe per la selezione dei record.
Configurare quindi GridView in modo che contenga solo gli ProductName
elementi , , CategoryName
SupplierName
e UnitPrice
BoundFields e Il controllo CheckBoxField interrotto. Infine, configurare GridView per supportare l'ordinamento selezionando la casella di controllo Abilita ordinamento nella smart tag di GridView (o impostandone la AllowSorting
proprietà su true
). Dopo aver effettuato 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>
Prendere un momento per visualizzare il nostro progresso finora in un browser. La figura 2 mostra 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 dimensioni complete)
Passaggio 2: Esplorazione delle tecniche per l'aggiunta delle righe separatori
Con il completamento di GridView generico ordinabile, tutto ciò che rimane deve essere in grado di aggiungere le righe separatori in GridView prima di ogni gruppo univoco ordinato. Ma come è possibile inserire tali righe in GridView? In sostanza, è 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 GridView.RowDataBound
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 qui, poiché le righe non possono essere aggiunte a livello di codice al gestore eventi GridView. L'insieme GridView, Rows
in realtà, è 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à era necessaria in molte pagine Web o in diversi siti Web. Tuttavia, comporta un po ' di codice e un'esplorazione approfondita nelle profondità dei lavori interni di GridView. Pertanto, 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 GridView dopo il relativo limite: attaccare il problema in modo diverso e merita una discussione.
Aggiunta di righe all'associazione dati 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
Uso i record separatori termini nelle virgolette perché non esiste un record separatore speciale; invece, è necessario contrassegnare in qualche modo che un determinato record nell'origine dati funzioni come separatore anziché una normale riga di dati. Per gli esempi, si associa un'istanza ProductsDataTable
a GridView, composta da ProductRows
. È possibile contrassegnare un record come riga separatore impostandone la CategoryID
proprietà su -1
(poiché tale valore non esiste 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 - Eseguire l'iterazione nell'oggetto
ProductsRows
ProductsDataTable
, cercando dove le differenze nella colonna ordinata si trovano - A ogni limite del gruppo, inserire un'istanza di record
ProductsRow
separatore in DataTable, una a cuiCategoryID
è impostata-1
(o a qualsiasi designazione è 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, è anche necessario fornire un gestore eventi per l'evento GridView.RowDataBound
In questo caso, si verificherà ogni DataRow
oggetto e si determina se fosse una riga separatore, una la cui CategoryID
impostazione era -1
. In tal caso, probabilmente si vuole 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' di lavoro maggiore rispetto a quanto descritto in precedenza, poiché è necessario fornire anche un gestore eventi per l'evento GridView s Sorting
e tenere traccia dei SortExpression
valori e SortDirection
.
Modifica della raccolta di controlli gridView dopo che è stato databound
Anziché inviare messaggi ai 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 è composta da una raccolta di celle. In particolare, l'insieme di controlli GridView contiene un Table
oggetto nella relativa 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 nell'oggetto DataSource
.
Per aggiungere righe separatori tra ogni gruppo di ordinamento, è possibile modificare direttamente questa gerarchia di controllo dopo la creazione. È possibile assicurarsi che la gerarchia di controllo di GridView sia stata creata per l'ultima volta al rendering della pagina. Pertanto, questo approccio esegue l'override del metodo della Render
classe, a quel punto viene aggiornata la Page
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 dimensioni complete)
Per questa esercitazione si userà questo approccio per personalizzare l'esperienza utente di ordinamento.
Nota
Il codice presentato in questa esercitazione si basa sull'esempio fornito nella voce di blog di Teemu Keiski , Riproduzione di un bit con GridView Sort Grouping.
Passaggio 3: Aggiunta delle righe separatori alla gerarchia di controllo gridView s
Poiché si desidera aggiungere solo le righe separatori alla gerarchia di controllo gridView dopo la creazione della gerarchia di controllo e la creazione 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 controllo GridView effettiva in HTML. Il punto più recente possibile a cui è possibile eseguire questa operazione è l'evento della Render
classe, che è possibile eseguire l'override Page
nella classe code-behind usando la firma del metodo seguente:
protected override void Render(HtmlTextWriter writer)
{
// Add code to manipulate the GridView control hierarchy
base.Render(writer);
}
Quando viene richiamato base.Render(writer)
il metodo originale Render
della Page
classe ogni controllo nella pagina verrà eseguito il rendering, generando il markup in base alla gerarchia di controllo. È pertanto imperativo chiamare base.Render(writer)
, 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 del 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 dei gruppi.
Nota
Se si vuole che GridView venga ordinato in base a una colonna specifica quando la pagina viene caricata per la prima volta, chiamare il metodo GridView Sort
nella prima pagina visita (ma non nei postback successivi). A questo scopo, aggiungere questa chiamata nel Page_Load
gestore eventi all'interno di un if (!Page.IsPostBack)
condizionale. Per altre informazioni sul metodo, vedere l'esercitazione Relativa al paging e all'ordinamento dei dati del Sort
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 per analizzare le righe che cercano 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 override void Render(HtmlTextWriter writer)
{
// Only add the sorting UI if the GridView is sorted
if (!string.IsNullOrEmpty(ProductList.SortExpression))
{
// Determine the index and HeaderText of the column that
//the data is sorted by
int sortColumnIndex = -1;
string sortColumnHeaderText = string.Empty;
for (int i = 0; i < ProductList.Columns.Count; i++)
{
if (ProductList.Columns[i].SortExpression.CompareTo(ProductList.SortExpression)
== 0)
{
sortColumnIndex = i;
sortColumnHeaderText = ProductList.Columns[i].HeaderText;
break;
}
}
// TODO: Scan the rows for differences in the sorted column�s values
}
Se GridView non è ancora stato ordinato, la proprietà GridView s SortExpression
non sarà stata impostata. Pertanto, si desidera aggiungere solo le righe separatori se questa proprietà ha un valore. In caso contrario, è necessario determinare l'indice della colonna in base al quale sono stati ordinati i dati. Questa operazione viene eseguita eseguendo il ciclo tramite l'insieme GridView s Columns
, cercando la colonna la cui SortExpression
proprietà equivale alla proprietà GridView s SortExpression
. Oltre all'indice della colonna, si afferra anche la proprietà, usata durante la HeaderText
visualizzazione delle 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 override void Render(HtmlTextWriter writer)
{
// Only add the sorting UI if the GridView is sorted
if (!string.IsNullOrEmpty(ProductList.SortExpression))
{
// ... Code for finding the sorted column index removed for brevity ...
// Reference the Table the GridView has been rendered into
Table gridTable = (Table)ProductList.Controls[0];
// Enumerate each TableRow, adding a sorting UI header if
// the sorted value has changed
string lastValue = string.Empty;
foreach (GridViewRow gvr in ProductList.Rows)
{
string currentValue = gvr.Cells[sortColumnIndex].Text;
if (lastValue.CompareTo(currentValue) != 0)
{
// there's been a change in value in the sorted column
int rowIndex = gridTable.Rows.GetRowIndex(gvr);
// Add a new sort header row
GridViewRow sortRow = new GridViewRow(rowIndex, rowIndex,
DataControlRowType.DataRow, DataControlRowState.Normal);
TableCell sortCell = 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;
}
}
}
base.Render(writer);
}
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:
string currentValue = string.Empty;
if (gvr.Cells[sortColumnIndex].Controls.Count > 0)
{
if (gvr.Cells[sortColumnIndex].Controls[0] is CheckBox)
{
if (((CheckBox)gvr.Cells[sortColumnIndex].Controls[0]).Checked)
currentValue = "Yes";
else
currentValue = "No";
}
// ... Add other checks here if using columns with other
// Web controls in them (Calendars, DropDownLists, etc.) ...
}
else
currentValue = gvr.Cells[sortColumnIndex].Text;
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.