Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Při zobrazení dlouhého seznamu seřazených dat může být velmi užitečné seskupit související data zavedením řádků oddělovače. V tomto kurzu se dozvíte, jak vytvořit takové uživatelské rozhraní řazení.
Úvod
Při zobrazení dlouhého seznamu seřazených dat, kde je v seřazených sloupcích jenom několik různých hodnot, může koncový uživatel těžko rozpoznat, kde přesně dochází k hranicím rozdílů. V databázi je například 81 produktů, ale pouze devět různých kategorií (osm jedinečných kategorií plus možnost NULL ). Vezměte v úvahu případ uživatele, který má zájem prozkoumat produkty, které spadají do kategorie Mořské plody. Ze stránky se seznamem všech produktů v jediném objektu GridView se uživatel může rozhodnout, že její nejlepší tip je seřadit výsledky podle kategorie, která seskupí všechny produkty mořské plody dohromady. Po seřazení podle kategorie musí uživatel projít seznamem a hledat, kde začínají a končí produkty seskupené mořské plody. Protože jsou výsledky seřazeny abecedně podle názvu kategorie, není obtížné najít produkty mořské plody, ale stále to vyžaduje pečlivé prohlédnutí seznamu položek v mřížce.
Aby bylo možné zvýraznit hranice mezi seřazenými skupinami, mnoho webů využívá uživatelské rozhraní, které mezi tyto skupiny přidává oddělovač. Oddělovače, jako jsou ty zobrazené na obrázku 1, umožňují uživateli rychleji najít konkrétní skupinu a identifikovat její hranice a zjistit, jaké různé skupiny v datech existují.
Obrázek 1: Každá skupina kategorií je jasně identifikována (kliknutím zobrazíte obrázek s plnou velikostí)
V tomto kurzu se dozvíte, jak vytvořit takové uživatelské rozhraní řazení.
Krok 1: Vytvoření standardního řaditelného objektu GridView
Než se podíváme, jak rozšířit GridView tak, aby poskytoval rozšířené rozhraní pro řazení, pojďme nejprve vytvořit standardní, řaditelný GridView, který zobrazuje seznam produktů. Začněte otevřením CustomSortingUI.aspx stránky ve PagingAndSorting složce. Na stránku přidejte GridView, nastavte jeho ID vlastnost na ProductLista vytvořte vazbu na nový ObjectDataSource. Nakonfigurujte ObjectDataSource tak, aby pro výběr záznamů používal metodu ProductsBLL třídy s GetProducts() .
Dále nakonfigurujte Objekt GridView tak, aby obsahoval pouze pole ProductName, CategoryName, SupplierNamea UnitPrice BoundFields a Discontinued CheckBoxField. Nakonec nakonfigurujte GridView tak, aby podporoval řazení zaškrtnutím políčka Povolit řazení v inteligentní značce GridView (nebo nastavením jeho AllowSorting vlastnosti na true). Po přidání těchto doplňků na CustomSortingUI.aspx stránku by deklarativní kód měl vypadat podobně jako následující:
<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>
Chvíli si prohlédněte náš pokrok v prohlížeči. Obrázek 2 znázorňuje seřaditelný objekt GridView, když jsou data seřazená podle kategorie v abecedním pořadí.
Obrázek 2: Řazení dat GridView s je seřazeno podle kategorie (kliknutím zobrazíte obrázek v plné velikosti)
Krok 2: Zkoumání technik pro přidání oddělovacích řádků
Po dokončení obecného, seřazeného GridView zbývá pouze přidat řádky oddělovače do GridView před každou jedinečnou seřazenou skupinu. Jak se ale tyto řádky dají vložit do GridView? V podstatě musíme iterovat řádky GridView s, určit, kde se vyskytují rozdíly mezi hodnotami ve seřazeném sloupci, a pak přidat odpovídající řádek oddělovače. Když uvažujeme o tomto problému, zdá se přirozené, že řešení leží někde v zpracování události GridView RowDataBound. Jak jsme si probrali v kurzu Vlastní formátování na základě dat, tato obslužná rutina události se běžně používá při formátování na úrovni řádků podle dat řádku. Obslužná rutina RowDataBound události zde však není řešením, protože z ní nelze programově přidávat řádky do GridView. Kolekce GridView s Rows je ve skutečnosti pouze pro čtení.
Pokud chcete do Objektu GridView přidat další řádky, máme tři možnosti:
- Přidejte tyto řádky oddělovače metadat do skutečných dat, která jsou svázaná s objektem GridView.
- Po svázání GridView s daty přidejte další
TableRowinstance do kolekce ovládacího prvku GridView. - Vytvoření vlastního ovládacího prvku serveru, který rozšiřuje ovládací prvek GridView a přepíše tyto metody zodpovědné za vytvoření struktury GridView s.
Vytvoření vlastního ovládacího prvku serveru by bylo nejlepší v případě, že by byla tato funkce potřebná na mnoha webových stránkách nebo na několika webech. To by však znamenalo poměrně hodně kódu a důkladné zkoumání hloubky vnitřních prací GridView. Proto tuto možnost pro tento kurz nebereme v úvahu.
Další dvě možnosti, které přidávají řádky oddělovače ke skutečným datům, která jsou svázána s GridView, a manipulují s kolekcí ovládacích prvků GridView po jeho svázání, řeší problém jinak a zaslouží si diskusi.
Přidání řádků do dat vázaných na GridView
Když je Objekt GridView vázán na zdroj dat, vytvoří GridViewRow pro každý záznam vrácený zdrojem dat. Proto můžeme vložit potřebné oddělovače řádků přidáním záznamů sloužících jako oddělovače do zdroje dat před navázáním na GridView. Obrázek 3 znázorňuje tento koncept.
Obrázek 3: Jedna technika zahrnuje přidání řádků oddělovače do zdroje dat
Používám termín „záznamy oddělovače“ v uvozovkách, protože neexistuje žádný speciální záznam sloužící jako oddělovač; musíme nějak označit, že určitý záznam ve zdroji dat slouží jako oddělovací záznam namísto normálního datového řádku. V našich příkladech vytvoříme vazbu ProductsDataTable instance na GridView, který se skládá z ProductRows. Záznam můžeme označit jako oddělovací řádek nastavením jeho CategoryID vlastnosti na -1 (protože taková hodnota normálně nemůže existovat).
Abychom mohli tuto techniku využít, musíme provést následující kroky:
- Programově načíst data pro vytvoření vazby k instanci GridView (
ProductsDataTable). - Seřazení dat podle vlastností
SortExpressionaSortDirectionobjektu GridView - Iterujte v
ProductsRowsčástiProductsDataTablea hledejte, kde jsou rozdíly v seřazeném sloupci. - Na hranici každé skupiny vložte do tabulky DataTable instanci záznamu oddělovače
ProductsRow, která má mít nastavenou hodnotuCategoryIDna-1(nebo na jakékoliv jiné označení, na kterém bylo rozhodnuto k označení záznamu jako oddělovače). - Po vložení řádků oddělovače vytvořte programovou vazbu dat k objektu GridView.
Kromě těchto pěti kroků potřebujeme také poskytnout obslužnou rutinu pro RowDataBound událost GridView. V této části zkontrolujeme každý DataRow řádek a určíme, jestli se jedná o oddělovač řádků, jeden, jehož CategoryID nastavení bylo -1. Pokud ano, pravděpodobně bychom chtěli upravit jeho formátování nebo text zobrazený v buňkách.
Použití této techniky pro vložení hranic skupiny řazení vyžaduje trochu více práce, než bylo výše uvedeno, protože potřebujete také poskytnout obslužnou rutinu pro událost GridView s Sorting a sledovat hodnoty SortExpression a SortDirection.
Manipulace s kolekcí ovládacích prvků GridView po navázání dat.
Místo zasílání zpráv dat před jejich vazbou na GridView můžeme přidat oddělovače řádků po svázání dat s GridView. Proces datových vazeb vytvoří hierarchii ovládacího prvku GridView, která je ve skutečnosti jednoduše Table instance složená z kolekce řádků, z nichž každý se skládá z kolekce buněk. Konkrétně, kolekce ovládacích prvků GridView obsahuje Table objekt ve svém kořenu, GridViewRow (který je odvozen z třídy TableRow) pro každý záznam v DataSource vázaném na GridView a TableCell objekt v každé GridViewRow instanci pro každé datové pole v DataSource.
Pokud chcete přidat oddělovač řádků mezi každou skupinu řazení, můžeme tuto hierarchii ovládacích prvků po vytvoření přímo manipulovat. Můžeme si být jisti, že hierarchie ovládacího prvku GridView s byla vytvořena naposledy v době, kdy se stránka vykresluje. Tento přístup proto přepíše metodu Page třídy Render ve chvíli, kdy je finální struktura řízení GridView aktualizována a zahrnuje potřebné řádky s oddělovači. Obrázek 4 znázorňuje tento proces.
Obrázek 4: Alternativní technika manipuluje s hierarchií ovládacího prvku GridView (kliknutím zobrazíte obrázek v plné velikosti).
V tomto kurzu použijeme tento druhý přístup k přizpůsobení uživatelského prostředí řazení.
Poznámka:
Kód, který prezentuji v tomto tutoriálu, je založen na příkladu poskytnutém v blogové položce Teemu KeiskiHrát si trochu s GridView Sort Grouping.
Krok 3: Přidání řádků oddělovače do hierarchie ovládacího prvku GridView s
Vzhledem k tomu, že chceme do hierarchie ovládacích prvků GridView přidat pouze řádky oddělovače poté, co byla tato hierarchie na dané stránce vytvořena naposledy, chceme toto přidání provést na konci životního cyklu stránky, ale ještě předtím, než se skutečná hierarchie ovládacích prvků GridView vykreslí do HTML. Nejpozdější možný okamžik, kdy toho můžeme dosáhnout, je Page událost třídy Render, kterou můžeme přepsat v naší třídě za kódem pomocí následujícího podpisu metody:
protected override void Render(HtmlTextWriter writer)
{
// Add code to manipulate the GridView control hierarchy
base.Render(writer);
}
Když je vyvolána původní metoda třídy Page, každá z ovládacích prvků na stránce bude vykreslena a vytvoří se značení na základě jejich ovládací hierarchie. Proto je nezbytné, abychom oba volali base.Render(writer), aby se stránka vykreslovala a že před voláním base.Render(writer)manipulujeme s hierarchií ovládacího prvku GridView, aby byly řádky oddělovače přidány do hierarchie ovládacích prvků GridView před vykreslením.
Abychom mohli vložit hlavičky skupiny řazení, musíme nejprve ujistit se, že uživatel požádal o seřazení dat. Ve výchozím nastavení není obsah GridView seřazený, a proto nemusíme zadávat záhlaví řazení skupin.
Poznámka:
Pokud chcete, aby objekt GridView byl při prvním načtení stránky seřazen podle konkrétního sloupce, zavolejte při první návštěvě stránky metodu GridView s Sort (ale ne v následných postbackech). Chcete-li toho dosáhnout, přidejte toto volání do Page_Load obslužné rutiny události v rámci podmínky if (!Page.IsPostBack). Pro více informací o metodě se vraťte ke kurzu Sort.
Za předpokladu, že jsou data seřazená, je naším dalším úkolem určit, podle kterého sloupce se data seřadila, a pak prohledat řádky, které hledají rozdíly v hodnotách daného sloupce. Následující kód zajistí, že se data seřadí a vyhledá sloupec, podle kterého byla data seřazena:
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
}
Pokud objekt GridView ještě není seřazený, vlastnost GridView s SortExpression nebyla nastavena. Proto chceme přidat pouze řádky oddělovače, pokud má tato vlastnost nějakou hodnotu. Pokud ano, musíme dále určit index sloupce, podle kterého byla data seřazena. Toho lze dosáhnout procházením kolekce GridView, hledáním sloupce, jehož Columns vlastnost se rovná vlastnosti SortExpression. Kromě indexu sloupců také získáme vlastnost HeaderText, která se používá pro zobrazení řádků oddělovače.
S indexem sloupce, podle kterého jsou data seřazena, je posledním krokem vytvoření výčtu řádků objektu GridView. U každého řádku musíme určit, jestli se hodnota seřazeného sloupce liší od hodnoty seřazeného sloupce předchozího řádku. Pokud ano, musíme do řídicí hierarchie vložit novou GridViewRow instanci. To se provádí pomocí následujícího kódu:
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);
}
Tento kód začíná programovým odkazem na Table objekt nalezený v kořenovém uzlu hierarchie ovládacích prvků GridView a vytvořením řetězcové proměnné s názvem lastValue.
lastValue se používá k porovnání hodnoty seřazeného sloupce v aktuálním řádku s hodnotou v předchozím řádku. Dále je kolekce GridView Rows enumerována a pro každý řádek je hodnota seřazeného sloupce uložena do proměnné currentValue.
Poznámka:
K určení hodnoty konkrétního řádku ve seřazeném sloupci používám vlastnost buňky Text. To funguje dobře pro BoundFields, ale nebude fungovat podle potřeby pro TemplateFields, CheckBoxFields atd. Brzy se podíváme na to, jak počítat s alternativními poli GridView.
currentValue A lastValue proměnné se pak porovnávají. Pokud se liší, musíme do řídicí hierarchie přidat nový oddělovací řádek. Toho se dosahuje určením indexu GridViewRow v Table kolekci objektů Rows , vytvořením nových GridViewRow a TableCell instancí a následným přidáním TableCell a GridViewRow do hierarchie ovládacích prvků.
Všimněte si, že samotný oddělovač řádku TableCell je formátován tak, aby přesahoval celou šířku GridView, je formátován pomocí třídy CSS SortHeaderRowStyle a má Text vlastnost, která zobrazuje jak název skupiny řazení (například Category), tak i hodnotu skupiny (například Beverages).
lastValue Nakonec se aktualizuje na hodnotu currentValue.
Třída CSS použitá k formátování řádku záhlaví skupiny řazení SortHeaderRowStyle musí být zadána v souboru Styles.css. Neváhejte použít jakékoli nastavení stylu, které se vám líbí; Použil jsem následující:
.SortHeaderRowStyle
{
background-color: #c00;
text-align: left;
font-weight: bold;
color: White;
}
S aktuálním kódem rozhraní pro řazení přidává hlavičky skupin při řazení podle jakéhokoli BoundFieldu (viz Obrázek 5, který znázorňuje snímek obrazovky při řazení podle dodavatele). Při řazení podle jiného typu pole (například CheckBoxField nebo TemplateField) se ale záhlaví skupiny řazení nedají najít (viz obrázek 6).
Obrázek 5: Rozhraní řazení zahrnuje záhlaví skupiny řazení při řazení podle boundFields (Kliknutím zobrazíte obrázek s plnou velikostí)
Obrázek 6: Při řazení zaškrtávacího pole chybí záhlaví skupiny řazení (kliknutím zobrazíte obrázek s plnou velikostí).
Důvodem, proč při řazení podle CheckBoxField chybí záhlaví skupiny řazení, je to, že kód aktuálně používá pouze TableCell vlastnost s Text k určení hodnoty seřazeného sloupce pro každý řádek. U CheckBoxFields TableCell je vlastnost Text prázdný řetězec; místo toho je hodnota dostupná prostřednictvím webového ovládacího prvku CheckBox, který je součástí kolekce TableCellControls.
Abychom mohli zpracovávat jiné typy polí než BoundFields, musíme rozšířit kód, ve currentValue kterém je proměnná přiřazena, aby se zkontrolovala existence CheckBoxu TableCell v kolekci s Controls . Místo použití currentValue = gvr.Cells[sortColumnIndex].Textnahraďte tento kód následujícím kódem:
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;
Tento kód prozkoumá seřazený sloupec TableCell pro aktuální řádek a určí, jestli v kolekci Controls existují nějaké ovládací prvky. Pokud existuje a první ovládací prvek je CheckBox, currentValue proměnná je nastavena na Ano nebo Ne v závislosti na vlastnosti CheckBox s Checked . V opačném případě je hodnota získána z vlastnosti TableCell s Text. Tuto logiku je možné replikovat pro zpracování řazení pro všechna pole šablon, která může existovat v Objektu GridView.
Přidáním výše uvedeného kódu se nyní při řazení podle CheckBoxFieldu "Ukončeno" zobrazují záhlaví skupin řazení (viz obrázek 7).
Obrázek 7: Při řazení pole CheckBoxField jsou nyní k dispozici záhlaví skupiny řazení (kliknutím zobrazíte obrázek s plnou velikostí).
Poznámka:
Pokud máte produkty s hodnotami databáze NULL v polích CategoryID, SupplierID nebo UnitPrice, tyto hodnoty se ve výchozím nastavení zobrazí jako prázdné řetězce v GridView, což znamená, že text oddělovacího řádku pro tyto produkty s hodnotami NULL bude číst jako Kategorie: (to znamená, že není žádný název po Kategorie: například s Kategorie: Nápoje). Pokud chcete, aby se zde zobrazila hodnota, můžete buď nastavit vlastnost BoundFields
Shrnutí
GridView neobsahuje mnoho předdefinovaných možností pro přizpůsobení rozhraní řazení. S trochou kódu nízké úrovně je však možné upravit hierarchii ovládacích prvků GridView a vytvořit tak přizpůsobené rozhraní. V tomto kurzu jsme viděli, jak přidat řádek oddělovače skupin řazení pro řaditelný objekt GridView, který snadněji identifikuje jedinečné skupiny a hranice těchto skupin. Další příklady přizpůsobených rozhraní řazení najdete v blogovém příspěvku Scott Guthrie s Few ASP.NET 2.0 GridView Sorting Tips and Tricks .
Šťastné programování!
O autorovi
Scott Mitchell, autor sedmi knih ASP/ASP.NET a zakladatel 4GuysFromRolla.com, pracuje s webovými technologiemi Microsoftu od roku 1998. Scott pracuje jako nezávislý konzultant, trenér a spisovatel. Jeho nejnovější kniha je Sams: Nauč se ASP.NET 2.0 za 24 hodin. Může být dosažitelný na mitchell@4GuysFromRolla.comadrese .