Sdílet prostřednictvím


Webové ovládací prvky vnořených dat (VB)

Scott Mitchell

Stáhnout PDF

V tomto kurzu prozkoumáme, jak použít opakovač vnořený v jiném opakovači. Příklady ukazují, jak naplnit vnitřní repeater deklarativně i programově.

Úvod

Šablony můžou kromě statické syntaxe HTML a vazby dat obsahovat také webové ovládací prvky a uživatelské ovládací prvky. Tyto webové ovládací prvky můžou mít přiřazené své vlastnosti prostřednictvím deklarativní syntaxe vazby dat nebo mohou být přístupné programově v příslušných obslužných rutinách událostí na straně serveru.

Vložením ovládacích prvků do šablony je možné přizpůsobit a vylepšit vzhled a uživatelské prostředí. Například v kurzu Použití templateFields v ovládacím prvku GridView jsme viděli, jak přizpůsobit zobrazení GridView s přidáním ovládacího prvku Calendar v TemplateField zobrazit datum přijetí zaměstnance; v kurzech Přidání ověřovacích ovládacích prvků do úprav a vkládání rozhraní a Přizpůsobení rozhraní pro úpravu dat jsme viděli, jak upravit a vložit rozhraní přidáním ověřovacích ovládacích prvků, textových polí, rozevíracích seznamů a dalších webových ovládacích prvků.

Šablony můžou také obsahovat další webové ovládací prvky dat. To znamená, že můžeme mít DataList, který obsahuje jiný seznam DataList (nebo Repeater, GridView nebo DetailsView atd.) v jeho šablonách. Problémem s takovým rozhraním je vytvoření vazby příslušných dat na vnitřní datový webový ovládací prvek. K dispozici je několik různých přístupů, od deklarativních možností pomocí ObjectDataSource až po programové.

V tomto kurzu prozkoumáme, jak použít opakovač vnořený v jiném opakovači. Vnější repeater bude obsahovat položku pro každou kategorii v databázi, která zobrazí název a popis kategorie. Vnitřní repeater každé položky kategorie zobrazí informace o každém produktu, který patří do dané kategorie (viz obrázek 1) v seznamu s odrážkami. Naše příklady ukazují, jak naplnit vnitřní repeater deklarativně i programově.

Každá kategorie, spolu se svými produkty, jsou uvedeny

Obrázek 1: Každá kategorie spolu s produkty jsou uvedené (kliknutím zobrazíte obrázek v plné velikosti)

Krok 1: Vytvoření výpisu kategorií

Při vytváření stránky, která používá webové ovládací prvky s vnořenými daty, je užitečné nejprve navrhnout, vytvořit a otestovat nejkrajnější datový webový ovládací prvek, aniž by se museli starat o vnitřní vnořený ovládací prvek. Začněme tedy kroky potřebnými k přidání repeateru na stránku, která obsahuje název a popis jednotlivých kategorií.

Začněte tím, že NestedControls.aspx otevřete stránku ve DataListRepeaterBasics složce a přidáte na stránku ovládací prvek Repeater a nastavíte jeho ID vlastnost na CategoryList. Z inteligentní značky Repeater s zvolte vytvořit nový ObjectDataSource s názvem CategoriesDataSource.

Pojmenujte Nový objektDataSource KategorieDataSource.

Obrázek 2: Pojmenujte Nový objektDataSource CategoriesDataSource (kliknutím zobrazíte obrázek v plné velikosti)

Nakonfigurujte ObjectDataSource tak, aby načítá data z CategoriesBLL metody třídy s GetCategories .

Nakonfigurujte ObjectDataSource tak, aby používal metodu CategoriesBLL Class s GetCategories

Obrázek 3: Konfigurace objektu ObjectDataSource pro použití CategoriesBLL metody Třídy s GetCategories (kliknutím zobrazíte obrázek v plné velikosti)

Pokud chcete zadat obsah šablony Repeater s, musíme přejít do zobrazení Zdroj a ručně zadat deklarativní syntaxi. Přidejte objekt ItemTemplate , který zobrazí název kategorie v elementu <h4> a popis kategorie v elementu odstavce (<p>). Kromě toho oddělme každou kategorii vodorovným pravidlem (<hr>). Po provedení těchto změn by vaše stránka měla obsahovat deklarativní syntaxi pro Repeater a ObjectDataSource, která je podobná následující:

<asp:Repeater ID="CategoryList" DataSourceID="CategoriesDataSource"
    EnableViewState="False" runat="server">
    <ItemTemplate>
        <h4><%# Eval("CategoryName") %></h4>
        <p><%# Eval("Description") %></p>
    </ItemTemplate>
    <SeparatorTemplate>
        <hr />
    </SeparatorTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

Obrázek 4 znázorňuje náš průběh při prohlížení v prohlížeči.

Název a popis každé kategorie je uvedený a je oddělený vodorovným pravidlem.

Obrázek 4: Název a popis jednotlivých kategorií jsou uvedené, oddělené vodorovným pravidlem (kliknutím zobrazíte obrázek v plné velikosti)

Krok 2: Přidání vnořeného opakovače produktu

Po dokončení výpisu kategorií je naším dalším úkolem přidat do CategoryListItemTemplate seznamu opakovač, který zobrazí informace o produktech, které patří do příslušné kategorie. Existuje několik způsobů, jak můžeme načíst data pro tento vnitřní repeater. Dva z těchto dvou z těchto možností prozkoumáme zanedlouho. Prozatím stačí vytvořit produkty Repeater v rámci CategoryList repeater s ItemTemplate. Konkrétně nechte produkt Repeater zobrazit každý produkt v seznamu s odrážkami s každou položkou seznamu včetně názvu a ceny produktu.

K vytvoření tohoto repeateru musíme ručně zadat vnitřní syntaxi repeateru s deklarativní syntaxi a šablony do CategoryList s ItemTemplate. Do rutiny CategoryList Repeater s ItemTemplatepřidejte následující kód:

<asp:Repeater ID="ProductsByCategoryList" EnableViewState="False"
    runat="server">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><strong><%# Eval("ProductName") %></strong>
            (<%# Eval("UnitPrice", "{0:C}") %>)</li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>

Krok 3: Vytvoření vazby Category-Specific Products k opakovači ProductsByCategoryList

Pokud tuto stránku navštívíte v tomto okamžiku přes prohlížeč, bude obrazovka vypadat stejně jako na obrázku 4, protože jsme ještě nesvázali žádná data s repeaterem. Existuje několik způsobů, jak můžeme získat příslušné záznamy produktů a svázat je s repeaterem, některé efektivnější než jiné. Hlavní výzvou je získat zpět vhodné produkty pro zadanou kategorii.

Data, která se mají svázat s vnitřním ovládacím prvkem Repeater, lze přistupovat buď deklarativně prostřednictvím ObjectDataSource v CategoryList repeateru s ItemTemplate, nebo programově ze stránky ASP.NET stránky s kódem na pozadí. Podobně mohou být tato data vázána na vnitřní repeater buď deklarativně – prostřednictvím vnitřní vlastnosti Repeater s DataSourceID nebo prostřednictvím deklarativní syntaxe vazby dat nebo programově odkazováním na vnitřní repeater v CategoryList obslužné rutině události Repeater s ItemDataBound , programovým nastavením jeho DataSource vlastnosti a voláním jeho DataBind() metody. Pojďme se podívat na každý z těchto přístupů.

Deklarativní přístup k datům pomocí ovládacího prvku ObjectDataSource a obslužné rutinyItemDataBoundudálosti

Vzhledem k tomu, že jsme ObjectDataSource v této sérii kurzů používali ve velké míře, je nejpřirozenější volbou pro přístup k datům v tomto příkladu zůstat u ObjectDataSource. Třída ProductsBLL má metodu GetProductsByCategoryID(categoryID) , která vrací informace o produktech, které patří do zadané categoryID. Proto můžeme přidat ObjectDataSource do CategoryList Repeater s ItemTemplate a nakonfigurovat jej pro přístup k jeho datům z této třídy s metody.

Repeater bohužel neumožňuje úpravu šablon prostřednictvím návrhového zobrazení, takže musíme přidat deklarativní syntaxi pro tento ovládací prvek ObjectDataSource ručně. Následující syntaxe ukazuje CategoryList repeatery ItemTemplate po přidání tohoto nového ObjectDataSource (ProductsByCategoryDataSource):

<h4><%# Eval("CategoryName") %></h4>
<p><%# Eval("Description") %></p>
<asp:Repeater ID="ProductsByCategoryList" EnableViewState="False"
        DataSourceID="ProductsByCategoryDataSource" runat="server">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><strong><%# Eval("ProductName") %></strong> -
                sold as <%# Eval("QuantityPerUnit") %> at
                <%# Eval("UnitPrice", "{0:C}") %></li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="ProductsByCategoryDataSource" runat="server"
           SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
   <SelectParameters>
        <asp:Parameter Name="CategoryID" Type="Int32" />
   </SelectParameters>
</asp:ObjectDataSource>

Při použití přístupu ObjectDataSource musíme nastavit ProductsByCategoryList vlastnost Repeater s DataSourceID na ID vlastnost ObjectDataSource (ProductsByCategoryDataSource). Všimněte si také, že náš ObjectDataSource má <asp:Parameter> prvek, který určuje categoryID hodnotu, která bude předána do GetProductsByCategoryID(categoryID) metody. Ale jak tuto hodnotu určíme? V ideálním případě bychom mohli jednoduše nastavit DefaultValue vlastnost elementu <asp:Parameter> pomocí syntaxe vazby dat, například takto:

<asp:Parameter Name="CategoryID" Type="Int32"
    DefaultValue='<%# Eval("CategoryID")' />

Syntaxe vazby dat je bohužel platná pouze v ovládacích prvcích, které mají DataBinding událost. Třída Parameter takovou událost postrádá, a proto je výše uvedená syntaxe neplatná a výsledkem bude chyba za běhu.

Abychom mohli nastavit tuto hodnotu, musíme vytvořit obslužnou rutinu CategoryList události pro událost Repeater s ItemDataBound . Vzpomeňte si, že událost se ItemDataBound aktivuje jednou pro každou položku vázanou na repeater. Proto pokaždé, když se tato událost aktivuje pro vnější repeater, můžeme přiřadit aktuální CategoryID hodnotu parametru ProductsByCategoryDataSource ObjectDataSource s CategoryID .

Vytvořte obslužnou rutinu CategoryList události pro událost Repeater s ItemDataBound pomocí následujícího kódu:

Protected Sub CategoryList_ItemDataBound(sender As Object, e As RepeaterItemEventArgs) _
    Handles CategoryList.ItemDataBound
    If e.Item.ItemType = ListItemType.AlternatingItem _
        OrElse e.Item.ItemType = ListItemType.Item Then
        ' Reference the CategoriesRow object being bound to this RepeaterItem
        Dim category As Northwind.CategoriesRow = _
            CType(CType(e.Item.DataItem, System.Data.DataRowView).Row, _
                Northwind.CategoriesRow)
        ' Reference the ProductsByCategoryDataSource ObjectDataSource
        Dim ProductsByCategoryDataSource As ObjectDataSource = _
            CType(e.Item.FindControl("ProductsByCategoryDataSource"), _
                ObjectDataSource)
        ' Set the CategoryID Parameter value
        ProductsByCategoryDataSource.SelectParameters("CategoryID").DefaultValue = _
            category.CategoryID.ToString()
    End If
End Sub

Tato obslužná rutina události začíná tím, že zajistíme, že pracujeme s datovou položkou, nikoli s položkou záhlaví, zápatí nebo oddělovače. Dále odkazujeme na skutečnou CategoriesRow instanci, která byla právě vázána na aktuální RepeaterIteminstanci . Nakonec odkazujeme na ObjectDataSource v objektu ItemTemplate a přiřadíme jeho CategoryID hodnotu parametru CategoryID k aktuální RepeaterItemhodnotě .

S touto obslužnou rutinou ProductsByCategoryList události je repeater v každém RepeaterItem vázán na tyto produkty v RepeaterItem kategorii s. Obrázek 5 ukazuje snímek obrazovky s výsledným výstupem.

Vnější opakovač Seznamy každou kategorii; vnitřní Seznamy produkty pro danou kategorii.

Obrázek 5: Vnější opakovač Seznamy každou kategorii; vnitřní Seznamy produkty pro danou kategorii (kliknutím zobrazíte obrázek v plné velikosti)

Programový přístup k datům o produktech podle kategorií

Místo použití ObjectDataSource k načtení produktů pro aktuální kategorii bychom mohli vytvořit metodu v naší třídě ASP.NET stránky s kódem na pozadí (nebo ve App_Code složce nebo v samostatném projektu knihovny tříd), která vrátí příslušnou sadu produktů při předání v CategoryID. Představte si, že jsme takovou metodu měli v naší třídě kódu stránky ASP.NET a že měla název GetProductsInCategory(categoryID). S touto metodou bychom mohli svázat produkty pro aktuální kategorii s vnitřním repeaterem pomocí následující deklarativní syntaxe:

<asp:Repeater runat="server" ID="ProductsByCategoryList" EnableViewState="False"
      DataSource='<%# GetProductsInCategory(CType(Eval("CategoryID"), Integer)) %>'>
  ...
</asp:Repeater>

Vlastnost Repeater s DataSource používá syntaxi vazby dat k označení, že její data pocházejí z GetProductsInCategory(categoryID) metody. Vzhledem k tomu Eval("CategoryID") , že vrátí hodnotu typu Object, přetypujeme objekt na objekt Integer před jeho předáním do GetProductsInCategory(categoryID) metody. Všimněte si CategoryID , že sem přistupuje prostřednictvím syntaxe CategoryID vazby dat ve vnějším repeateru (CategoryList), který je vázán na záznamy v Categories tabulce. Proto víme, že CategoryID to nemůže být hodnota databáze NULL , což je důvod, proč můžeme metodu Eval slepě přetypovat bez kontroly, zda pracujeme s DBNull.

S tímto přístupem potřebujeme vytvořit metodu GetProductsInCategory(categoryID) a nechat ji načíst odpovídající sadu produktů zadanou categoryID. Můžeme to udělat tak, že jednoduše vrátíme ProductsDataTable hodnotu vrácenou metodou ProductsBLL třídy s GetProductsByCategoryID(categoryID) . Pojďme vytvořit metodu GetProductsInCategory(categoryID) ve třídě kódu na pozadí pro naši NestedControls.aspx stránku. Proveďte to pomocí následujícího kódu:

Protected Function GetProductsInCategory(ByVal categoryID As Integer) _
    As Northwind.ProductsDataTable
    ' Create an instance of the ProductsBLL class
    Dim productAPI As ProductsBLL = New ProductsBLL()
    ' Return the products in the category
    Return productAPI.GetProductsByCategoryID(categoryID)
End Function

Tato metoda jednoduše vytvoří instanci ProductsBLL metody a vrátí výsledky GetProductsByCategoryID(categoryID) metody. Všimněte si, že metoda musí být označena Public nebo Protected; pokud je metoda označena Private, nebude přístupná z deklarativního kódu ASP.NET stránky.

Po provedení těchto změn pro použití této nové techniky si chvíli prohlédněte stránku v prohlížeči. Výstup by měl být shodný s výstupem při použití přístupu ObjectDataSource a ItemDataBound obslužné rutiny události (podívejte se zpět na obrázek 5 a podívejte se na snímek obrazovky).

Poznámka

Může se zdát, že vytvoření GetProductsInCategory(categoryID) metody ve třídě kódu na pozadí ASP.NET stránky je zaneprázdněné. Koneckonců, tato metoda jednoduše vytvoří instanci ProductsBLL třídy a vrátí výsledky její GetProductsByCategoryID(categoryID) metody. Proč nevolat tuto metodu přímo ze syntaxe vazby dat ve vnitřním repeateru, například: DataSource='<%# ProductsBLL.GetProductsByCategoryID(CType(Eval("CategoryID"), Integer)) %>'? I když tato syntaxe nebude fungovat s naší aktuální implementací ProductsBLL třídy (protože GetProductsByCategoryID(categoryID) metoda je metoda instance), můžete ji upravit ProductsBLL tak, aby zahrnovala statickou GetProductsByCategoryID(categoryID) metodu nebo aby třída obsahovala statickou Instance() metodu pro vrácení nové instance ProductsBLL třídy.

I když by takové úpravy eliminovaly potřebu GetProductsInCategory(categoryID) metody ve třídě kódu na pozadí ASP.NET stránky, metoda třídy s kódem na pozadí nám dává větší flexibilitu při práci s načtenými daty, jak uvidíme za chvíli.

Načítání všech informací o produktu najednou

Dvě pervious techniky, které jsme prozkoumali, přebíjejí tyto produkty pro aktuální kategorii voláním ProductsBLL metody třídy s GetProductsByCategoryID(categoryID) (první přístup to udělal prostřednictvím ObjectDataSource, druhý prostřednictvím GetProductsInCategory(categoryID) metody ve třídě kódu na pozadí). Při každém vyvolání této metody volá vrstva obchodní logiky vrstvu přístupu k datům, která dotazuje databázi pomocí příkazu SQL, který vrací řádky z Products tabulky, jejíž CategoryID pole odpovídá zadanému vstupnímu parametru.

Vzhledem k N kategoriím v systému tento přístup prosí N + 1 volání do databáze jeden databázový dotaz pro získání všech kategorií a pak N volání pro získání produktů specifických pro každou kategorii. Můžeme ale načíst všechna potřebná data v pouhých dvou voláních databáze, abychom získali všechny kategorie a dalším voláním získali všechny produkty. Jakmile budeme mít všechny produkty, můžeme je filtrovat tak, aby se na vnitřní repeater dané kategorie vázaly pouze produkty odpovídající aktuálnímu CategoryID .

Abychom mohli tuto funkci poskytnout, stačí provést jen malou změnu GetProductsInCategory(categoryID) metody v naší třídě ASP.NET stránky s kódem na pozadí. Místo toho, abychom slepě vraceli výsledky ProductsBLL metody třídy GetProductsByCategoryID(categoryID) , můžeme nejprve získat přístup ke všem produktům (pokud k nim ještě nedošlo) a pak vrátit pouze filtrované zobrazení produktů na základě předaného CategoryID.

Private allProducts As Northwind.ProductsDataTable = Nothing
Protected Function GetProductsInCategory(ByVal categoryID As Integer) _
    As Northwind.ProductsDataTable
    ' First, see if we've yet to have accessed all of the product information
    If allProducts Is Nothing Then
        Dim productAPI As ProductsBLL = New ProductsBLL()
        allProducts = productAPI.GetProducts()
    End If
    ' Return the filtered view
    allProducts.DefaultView.RowFilter = "CategoryID = " & categoryID
    Return allProducts
End Function

Všimněte si přidání proměnné allProductsna úrovni stránky . Obsahuje informace o všech produktech a naplní se při GetProductsInCategory(categoryID) prvním vyvolání metody. Po ověření, že allProducts objekt byl vytvořen a naplněn, metoda filtruje výsledky DataTable tak, aby byly přístupné pouze řádky, které CategoryID odpovídají zadanému CategoryID . Tento přístup snižuje počet přístupů k databázi z N + 1 na dva.

Toto vylepšení nezavádí žádnou změnu vykresleného kódu stránky ani nepřináší méně záznamů než jiný přístup. Jednoduše tím snížíte počet volání databáze.

Poznámka

Intuitivně by se mohlo zdát, že snížení počtu přístupů k databázi by určitě zvýšilo výkon. To ale nemusí být tento případ. Pokud máte velký počet produktů, jejichž CategoryID je NULLnapříklad , pak volání GetProducts metody vrátí počet produktů, které se nikdy nezobrazí. Vrácení všech produktů navíc může být plýtvání, pokud zobrazujete pouze podmnožinu kategorií, což může být případ, kdy jste implementovali stránkování.

Jako vždy platí, že pokud jde o analýzu výkonu dvou technik, jediným jistým měřítkemfire je spouštění kontrolovaných testů přizpůsobených pro běžné scénáře případů vaší aplikace.

Souhrn

V tomto kurzu jsme viděli, jak vnořit jeden datový webový ovládací prvek do jiného, konkrétně jsme zkoumali, jak nechat vnější repeater zobrazit položku pro každou kategorii s vnitřním repeaterem, který obsahuje seznam produktů pro každou kategorii v seznamu s odrážkami. Hlavní výzva při vytváření vnořeného uživatelského rozhraní spočívá v přístupu ke správným datům a jejich vazbě na webový ovládací prvek vnitřních dat. K dispozici jsou různé techniky, z nichž dvě jsme prozkoumali v tomto kurzu. První zkoumaný přístup používal ObjectDataSource ve vnějším datovém webovém ovládacím prvku ItemTemplate , který byl svázán s ovládacím prvku web vnitřních dat prostřednictvím jeho DataSourceID vlastnosti. Druhá technika přistupovala k datům prostřednictvím metody ve třídě kódu ASP.NET stránky. Tato metoda pak může být vázána na vnitřní data webové ovládací prvek DataSource vlastnost prostřednictvím syntaxe vazby dat.

Zatímco vnořené uživatelské rozhraní prozkoumané v tomto kurzu používalo opakovač vnořený v repeateru, tyto techniky lze rozšířit na další datové webové ovládací prvky. Repeater můžete vnořit do objektu GridView nebo GridView v rámci objektu DataList atd.

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.

Zvláštní poděkování

Tato série kurzů byla zkontrolována mnoha užitečnými recenzenty. Hlavními recenzenty pro tento kurz byli Zack Jones a Liz Shulok. Chtěli byste si projít své nadcházející články na webu MSDN? Pokud ano, dejte mi řádek na mitchell@4GuysFromRolla.com.