Sdílet prostřednictvím


Vytvoření vlastního uživatelského rozhraní pro řazení (C#)

Scott Mitchell

Stáhnout PDF

Při zobrazení dlouhého seznamu seřazených dat může být velmi užitečné seskupit související data zavedením oddělovače řádků. V tomto kurzu se dozvíte, jak vytvořit takové uživatelské rozhraní pro řazení.

Úvod

Při zobrazení dlouhého seznamu seřazených dat, kde je v seřazených sloupcích jen několik různých hodnot, může být pro koncového uživatele obtížné rozpoznat, kde přesně dochází k rozdílům. V databázi je například 81 produktů, ale pouze devět různých kategorií (osm jedinečných kategorií plus NULL možnost). Vezměte v úvahu případ uživatele, který má zájem o zkoumání produktů, které spadají do kategorie Mořské plody. Na stránce, která obsahuje seznam všech produktů v jednom GridView, se uživatel může rozhodnout, že nejlepší tip je seřadit výsledky podle kategorie, která seskupí všechny produkty z mořských plodů dohromady. Po seřazení podle kategorie musí uživatel projít seznam a hledat, kde začínají a končí produkty seskupené z mořských plodů. Vzhledem k tomu, že výsledky jsou seřazeny abecedně podle názvu kategorie, není nalezení produktů z mořských plodů obtížné, ale stále vyžaduje důkladnou kontrolu seznamu položek v mřížce.

Aby bylo možné zvýraznit hranice mezi seřazenými skupinami, mnoho webů použí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 také zjistit, jaké jedinečné skupiny v datech existují.

Každá skupina kategorií je jasně identifikována.

Obrázek 1: Každá skupina kategorií je jasně identifikována (kliknutím zobrazíte obrázek v plné velikosti)

V tomto kurzu se dozvíte, jak vytvořit takové uživatelské rozhraní pro řazení.

Krok 1: Vytvoření standardního seřaditelného objektu GridView

Než prozkoumáme, jak rozšířit GridView o vylepšené rozhraní pro řazení, pojďme nejprve vytvořit standardní, řaditelný GridView, který obsahuje seznam produktů. Začněte otevřením CustomSortingUI.aspx stránky ve PagingAndSorting složce. Přidejte objekt GridView na stránku, nastavte jeho ID vlastnost na ProductLista vytvořte vazbu s novým objektem 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 ProductNamepouze , CategoryNameSupplierName, a UnitPrice BoundFields a Ukončené pole 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 provedení těchto přidání na CustomSortingUI.aspx stránku by deklarativní kód měl vypadat nějak takto:

<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>

Podívejte se na náš dosavadní pokrok v prohlížeči. Obrázek 2 znázorňuje řaditelný objekt GridView, když jsou jeho data seřazena podle kategorií v abecedním pořadí.

Data řaditelného objektu GridView s jsou seřazená podle kategorie.

Obrázek 2: Data typu GridView s řazením jsou seřazená podle kategorie (kliknutím zobrazíte obrázek v plné velikosti)

Krok 2: Prozkoumání technik pro přidání řádků oddělovače

Po dokončení obecného objektu GridView, který lze seřadit, zbývá jen přidat řádky oddělovače v GridView před každou jedinečnou seřazenou skupinu. Ale jak mohou být takové řádky vloženy do GridView? V podstatě potřebujeme iterovat řádky GridView s, určit, kde dochází k rozdílům mezi hodnotami v seřazených sloupcích, a pak přidat příslušný řádek oddělovače. Při úvahách o tomto problému se zdá přirozené, že řešení leží někde v obslužné rutině události GridView s RowDataBound . Jak jsme probrali v kurzu Vlastní formátování založené na datech , tato obslužná rutina události se běžně používá při použití formátování na úrovni řádků na základě dat řádků. Obslužná rutina RowDataBound události zde však není řešením, protože řádky nelze přidat do objektu GridView programově z této obslužné rutiny události. Kolekce GridView s Rows je ve skutečnosti jen 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 ke skutečným datům, která jsou svázaná s objektem GridView.
  • Po vazbě objektu GridView na data přidejte do kolekce ovládacích prvků GridView s další TableRow instance.
  • Vytvoření vlastního serverového ovládacího prvku, který rozšiřuje ovládací prvek GridView a přepíše metody zodpovědné za vytvoření struktury GridView s

Vytvoření vlastního serverového ovládacího prvku by bylo nejlepší, pokud by tato funkce byla potřebná na mnoha webových stránkách nebo na několika webech. To by však vyžadovalo poměrně málo kódu a důkladné prozkoumání do hloubky vnitřních fungování GridView. Proto tuto možnost v tomto kurzu nebudeme brát v úvahu.

Další dvě možnosti přidání oddělovacích řádků do skutečných dat vázaných na GridView a manipulace s gridView s kolekce ovládacích prvků po jeho vazbě - útok na problém odlišně a zaslouží si diskuzi.

Přidání řádků k datům vázaným 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ělovací řádky přidáním záznamů oddělovače do zdroje dat před jeho vazbou na GridView. Tento koncept znázorňuje obrázek 3.

Jedna technika zahrnuje přidání řádků oddělovače do zdroje dat

Obrázek 3: Jedna technika zahrnuje přidání oddělovačů řádků do zdroje dat

Používám termín oddělovač záznamů v uvozovkách, protože neexistuje žádný zvláštní oddělovač záznamu; místo toho musíme nějak označit příznakem, že konkrétní záznam ve zdroji dat slouží jako oddělovač, nikoli jako normální řádek dat. V našich příkladech svážeme instanci s objektem ProductsDataTable GridView, který se skládá z objektu ProductRows. Záznam můžeme označit jako řádek oddělovače nastavením jeho CategoryID vlastnosti na -1 (protože taková hodnota nemohla normálně existovat).

Abychom mohli tuto techniku využít, museli bychom provést následující kroky:

  1. Programové načtení dat pro vazbu na GridView ( ProductsDataTable instance)
  2. Řazení dat na základě vlastností GridView SortExpressionSortDirection
  3. Iterujte v ProductsDataTablesouboru a vyhledejteProductsRows, kde leží rozdíly v seřazených sloupcích.
  4. Na hranici každé skupiny vložte instanci oddělovače záznamu ProductsRow do tabulky DataTable, která je nastavená CategoryID na -1 (nebo jakékoli označení bylo rozhodnuto označit záznam jako oddělovač záznamu).
  5. Po vložení oddělovačů řádky, programově svázat data s GridView

Kromě těchto pěti kroků bychom také potřebovali poskytnout obslužnou rutinu události pro událost GridView s RowDataBound . Tady každý z nich DataRow zkontrolujeme a určíme, jestli se jedná o řádek oddělovače, 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 skupin řazení vyžaduje trochu více práce, než je uvedeno výše, protože potřebujete také poskytnout obslužnou rutinu události pro událost GridView s Sorting a sledovat SortExpression hodnoty a SortDirection .

Manipulace se kolekcí ovládacích prvků GridView s po příchozím datu

Místo zasílání zpráv o datech před jejich vazbou na GridView, můžeme přidat řádky oddělovače poté , co byla data svázána s GridView. Proces datové vazby vytváří hierarchii ovládacích prvků GridView s, což 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 s obsahuje Table objekt v kořenovém adresáři, GridViewRow objekt (který je odvozen z TableRow třídy) pro každý záznam ve DataSource vazbě na GridView a TableCell objekt v každé GridViewRow instanci pro každé datové pole v objektu DataSource.

Pokud chcete přidat oddělovací řádky mezi každou skupinu řazení, můžeme s touto hierarchií ovládacích prvků po jejím vytvoření přímo manipulovat. Můžeme si být jistí, že hierarchie ovládacích prvků GridView s byla naposledy vytvořena v době vykreslení stránky. Proto tento přístup přepíše metodu Page třídy s Render , v tomto okamžiku se aktualizuje hierarchie ovládacích prvků GridView s tak, aby zahrnovala potřebné oddělovací řádky. Tento proces znázorňuje obrázek 4.

Alternativní technika manipuluje s hierarchií ovládacích prvků GridView s

Obrázek 4: Alternativní technika manipuluje s hierarchií ovládacích prvků 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 kurzu, je založený na příkladu uvedeném v položce blogu Teemu Keiski s Hrou bitů se seskupováním řazení GridView.

Krok 3: Přidání oddělovače řádků do hierarchie ovládacích prvků GridView s

Vzhledem k tomu, že chceme přidat oddělovací řádky do hierarchie ovládacích prvků GridView s až po vytvoření a vytvoření hierarchie ovládacích prvků při poslední návštěvě této stránky, chceme toto přidání provést na konci životního cyklu stránky, ale před vykreslení skutečné hierarchie ovládacích prvků GridView do HTML. Poslední možný bod, ve kterém toho můžeme dosáhnout, je Page událost třídy s Render , kterou můžeme přepsat v naší třídě kódu na pozadí pomocí následujícího podpisu metody:

protected override void Render(HtmlTextWriter writer)
{
    // Add code to manipulate the GridView control hierarchy
    base.Render(writer);
}

Při vyvolání Pagebase.Render(writer) původní Render metody třídy s se vykreslí každý ovládací prvek na stránce a vygeneruje značky na základě jejich hierarchie ovládacích prvků. Proto je nutné, abychom oba volali base.Render(writer), aby stránka byla vykreslena a abychom před voláním base.Render(writer)manipulovali s hierarchií ovládacích prvků GridView s tak, aby oddělovací řádky byly přidány do hierarchie ovládacích prvků GridView s před jeho vykreslení.

Pokud chcete vložit hlavičky skupiny řazení, musíme nejprve zajistit, aby uživatel požádal o seřazení dat. Ve výchozím nastavení není obsah GridView s seřazen, a proto nemusíme zadávat záhlaví řazení skupin.

Poznámka

Pokud chcete, aby byl GridView při prvním načtení stránky seřazen podle konkrétního sloupce, zavolejte metodu GridView s Sort při první návštěvě stránky (ale ne při následných postbackech). Chcete-li toho dosáhnout, přidejte toto volání do obslužné rutiny Page_Load události v rámci podmíněného if (!Page.IsPostBack) . Další Sort informace o metodě najdete v kurzu Stránkování a řazení dat sestavy.

Za předpokladu, že jsou data seřazená, je naším dalším úkolem určit, podle kterého sloupce byla data seřazena, a pak prohledávat řádky a hledat rozdíly v hodnotách daného sloupce. Následující kód zajistí, že jsou data seřazená, a najde 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 GridView ještě není seřazen, GridView s SortExpression vlastnost nebyla nastavena. Proto chceme přidat řádky oddělovače pouze v případě, že tato vlastnost má nějakou hodnotu. Pokud ano, musíme dále určit index sloupce, podle kterého byla data seřazena. Toho lze dosáhnout tak, že procházíme kolekci GridView s Columns a vyhledáte sloupec, jehož SortExpression vlastnost se rovná vlastnosti GridView s SortExpression . Kromě indexu sloupce uchopíme HeaderText také vlastnost, která se používá při zobrazení řádků oddělovače.

S indexem sloupce, podle kterého jsou data seřazena, je posledním krokem výčet řádků GridView. U každého řádku musíme zjistit, 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. K tomu slouží následující kód:

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 adresáři hierarchie ovládacích prvků GridView a vytvoří řetězcovou proměnnou s názvem lastValue. lastValue slouží k porovnání hodnoty seřazeného sloupce aktuálního řádku s hodnotou s předchozím řádkem. Dále je výčet kolekce GridView s Rows a pro každý řádek je hodnota seřazeného sloupce uložena currentValue v proměnné .

Poznámka

K určení hodnoty konkrétního seřazeného sloupce řádku používám vlastnost buňky s Text . To funguje dobře pro BoundFields, ale nebude fungovat podle potřeby pro TemplateFields, CheckBoxFields atd. Za chvíli se podíváme na to, jak zohlednit alternativní pole GridView.

Proměnné currentValue a lastValue se pak porovnávají. Pokud se liší, musíme do hierarchie ovládacích prvků přidat nový řádek oddělovače. Toho se dosahuje určením indexu objektu GridViewRowTable v kolekci objektů Rows , vytvořením nových GridViewRow instancí a TableCell a následným přidáním TableCell a GridViewRow do hierarchie ovládacích prvků.

Všimněte si, že oddělovací řádek TableCell je naformátován tak, aby přesahoval celou šířku GridView, je formátován pomocí SortHeaderRowStyle třídy CSS a má svou Text vlastnost tak, že zobrazuje název skupiny řazení (například Category ) a hodnotu skupiny (například Beverages ). lastValue Nakonec se aktualizuje na hodnotu currentValue.

Třída CSS použitá k formátování řádku SortHeaderRowStyle záhlaví řazení skupiny musí být zadána Styles.css v souboru. Nebojte se použít jakýkoli styl nastavení, které vás osloví; Použil(a) jsem následující:

.SortHeaderRowStyle
{
    background-color: #c00;
    text-align: left;
    font-weight: bold;
    color: White;
}

S aktuálním kódem přidává rozhraní pro řazení hlavičky skupin řazení při řazení podle libovolného BoundField (viz Obrázek 5, který ukazuje snímek obrazovky při řazení podle dodavatele). Při řazení podle jakéhokoli jiného typu pole (například CheckBoxField nebo TemplateField) ale záhlaví skupin řazení nikde nenajdete (viz Obrázek 6).

Rozhraní pro řazení zahrnuje záhlaví skupin řazení při řazení podle boundfields

Obrázek 5: Rozhraní pro řazení zahrnuje při řazení podle boundfields záhlaví skupin (kliknutím zobrazíte obrázek v plné velikosti)

Při řazení CheckBoxField chybí záhlaví skupiny řazení

Obrázek 6: Při řazení CheckBoxField chybí záhlaví skupiny řazení (kliknutím zobrazíte obrázek v plné velikosti)

Důvod, proč při řazení podle CheckBoxField chybí záhlaví skupiny řazení, je, že kód aktuálně používá pouze TableCell vlastnost s Text k určení hodnoty seřazeného sloupce pro každý řádek. Pro CheckBoxFields TableCell je vlastnost s Text prázdný řetězec. Místo toho je hodnota k dispozici prostřednictvím ovládacího prvku CheckBox web, který se nachází v kolekci TableCell s Controls .

Abychom mohli zpracovávat jiné typy polí než BoundFields, potřebujeme rozšířit kód, kterému currentValue je proměnná přiřazena, abychom zkontrolovali existenci CheckBoxu v kolekci TableCell s Controls . Místo příkazu 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 kolekce Controls obsahuje nějaké ovládací prvky. Pokud ano 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ě se hodnota převezme z TableCell vlastnosti s Text . Tuto logiku lze replikovat pro zpracování řazení pro všechny TemplateFields, které mohou existovat v GridView.

S výše uvedeným přidáním kódu jsou teď při řazení podle ukončeného pole CheckBoxField k dispozici záhlaví skupin řazení (viz Obrázek 7).

Při řazení CheckBoxField jsou teď k dispozici záhlaví skupiny řazení

Obrázek 7: Při řazení CheckBoxField jsou teď k dispozici záhlaví skupiny řazení (kliknutím zobrazíte obrázek v plné velikosti)

Poznámka

Pokud máte produkty s databázovými NULL hodnotami pro CategoryIDpole , SupplierIDnebo UnitPrice , tyto hodnoty se ve výchozím nastavení zobrazí jako prázdné řetězce v GridView, což znamená, že text řádku oddělovače pro tyto produkty s NULL hodnotami bude vypadat jako Category: (to znamená, že neexistuje název za Category: like with Category: Beverages ). Pokud chcete, aby se zde zobrazila hodnota, můžete buď nastavit vlastnost BoundFields NullDisplayText na text, který chcete zobrazit, nebo můžete přidat podmíněný příkaz v render metoda při přiřazení currentValue k vlastnosti oddělovače řádkuText.

Souhrn

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 více přizpůsobené rozhraní. V tomto kurzu jsme viděli, jak přidat řádek řazení oddělovače skupin pro seřaditelný Objekt GridView, který snadněji identifikuje jedinečné skupiny a jejich hranice. Další příklady přizpůsobených rozhraní pro řazení najdete na blogu Scott Guthrie s A Few ASP.NET 2.0 GridView Tipy a triky pro řazení .

Všechno nejlepší na programování!

O autorovi

Scott Mitchell, autor sedmi knih o ASP/ASP.NET a zakladatel 4GuysFromRolla.com, pracuje s webovými technologiemi Microsoftu od roku 1998. Scott pracuje jako nezávislý konzultant, školitel a spisovatel. Jeho nejnovější kniha je Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Můžete ho zastihnout na mitchell@4GuysFromRolla.comadrese . nebo prostřednictvím jeho blogu, který najdete na adrese http://ScottOnWriting.NET.