Webové ovládací prvky vnořených dat (VB)
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ě.
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
.
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
.
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.
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 CategoryList
ItemTemplate
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 ItemTemplate
př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é rutinyItemDataBound
udá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í RepeaterItem
instanci . Nakonec odkazujeme na ObjectDataSource v objektu ItemTemplate
a přiřadíme jeho CategoryID
hodnotu parametru CategoryID
k aktuální RepeaterItem
hodnotě .
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.
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é allProducts
na ú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 NULL
napří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.