Webové ovládací prvky vnořených dat (C#)
V tomto kurzu prozkoumáme, jak používat opakovač vnořený do jiného repeateru. Příklady ukazují, jak naplnit vnitřní repeater deklarativně i programově.
Úvod
Šablony mohou kromě statického HTML a syntaxe datových vazeb obsahovat také webové ovládací prvky a uživatelské ovládací prvky. Tyto webové ovládací prvky mohou mít své vlastnosti přiřazené prostřednictvím deklarativní syntaxe datové vazby nebo je možné k němu přistupovat 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 použití TemplateFields v GridView ovládacího prvku kurzu jsme viděli, jak přizpůsobit zobrazení GridView s přidáním ovládacího prvku Calendar v TemplateField zobrazit datum náboru 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 mohou také obsahovat další webové ovládací prvky dat. To znamená, že můžeme mít dataList, který obsahuje další dataList (nebo Repeater, GridView nebo DetailsView atd.) v rámci svých šablon. Výzvou s takovým rozhraním je vazba příslušných dat na vnitřní data webového ovládacího prvku. 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žívat opakovač vnořený do jiného repeateru. 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 se svými produkty je uvedena (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 vnořených dat, je užitečné 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. Proto pojďme začít tím, že si projdeme kroky potřebné k přidání repeateru na stránku, která obsahuje název a popis každé kategorie.
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 zvolte vytvoření nového objektu 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 Class s GetCategories
(kliknutím zobrazíte obrázek v plné velikosti)
Pokud chcete určit obsah šablony Repeater s, musíme přejít do zobrazení Zdroj a ručně zadat deklarativní syntaxi. Přidejte objekt ItemTemplate
, který zobrazuje 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 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áš postup při prohlížení prostřednictvím prohlížeče.
Obrázek 4: Název a popis každé kategorie jsou uvedené a jsou 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ý zobrazuje informace o produktech patřících 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 způsobů prozkoumáme za chvíli. Prozatím pojďme 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 produktu a ceny.
K vytvoření tohoto repeateru musíme ručně zadat vnitřní syntaxi a šablony repeateru CategoryList
do s ItemTemplate
. Do rutiny Repeater přidejte ItemTemplate
následující kódCategoryList
:
<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 prohlížeči, bude obrazovka vypadat stejně jako na obrázku 4, protože jsme zatím žádná data nesváželi 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 pro vytvoření vazby s vnitřním ovládacím prvkem Repeater lze přistupovat buď deklarativně prostřednictvím ObjectDataSource v CategoryList
repeateru ItemTemplate
, nebo programově z ASP.NET stránky s kódem na pozadí. Podobně lze tato data svázat s vnitřním repeaterem buď deklarativně – prostřednictvím vnitřní vlastnosti Repeater s DataSourceID
nebo prostřednictvím deklarativní syntaxe datové vazby nebo programově odkazováním na vnitřní repeater v CategoryList
obslužné rutině ItemDataBound
události Repeater, programovým nastavením jeho DataSource
vlastnosti a voláním jeho DataBind()
metody. Pojďme prozkoumat 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 v této sérii kurzů často používali ObjectDataSource, nejpřirozenější volbou pro přístup k datům v tomto příkladu je zůstat u objektu ObjectDataSource. Třída ProductsBLL
má metodu GetProductsByCategoryID(categoryID)
, která vrací informace o produktech, které patří do zadaného categoryID
objektu . 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 úpravy svých š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 zobrazuje CategoryList
hodnoty Repeater ItemTemplate
po přidání tohoto nového objektu 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>
element, který určuje categoryID
hodnotu, která bude předána GetProductsByCategoryID(categoryID)
do metody . Ale jak tuto hodnotu určíme? V ideálním případě bychom byli schopni jednoduše nastavit DefaultValue
vlastnost elementu <asp:Parameter>
pomocí syntaxe datové vazby, například takto:
<asp:Parameter Name="CategoryID" Type="Int32"
DefaultValue='<%# Eval("CategoryID")' />
Syntaxe vazby dat je bohužel platná jenom v ovládacích prvcích, které mají DataBinding
událost. Třída Parameter
takovou událost nemá, a proto je výše uvedená syntaxe neplatná a bude mít za následek chybu 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 k 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 void CategoryList_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.AlternatingItem ||
e.Item.ItemType == ListItemType.Item)
{
// Reference the CategoriesRow object being bound to this RepeaterItem
Northwind.CategoriesRow category =
(Northwind.CategoriesRow)((System.Data.DataRowView)e.Item.DataItem).Row;
// Reference the ProductsByCategoryDataSource ObjectDataSource
ObjectDataSource ProductsByCategoryDataSource =
(ObjectDataSource)e.Item.FindControl("ProductsByCategoryDataSource");
// Set the CategoryID Parameter value
ProductsByCategoryDataSource.SelectParameters["CategoryID"].DefaultValue =
category.CategoryID.ToString();
}
}
Tato obslužná rutina události začíná tím, že zajišťujeme, že pracujeme s datovou položkou, a ne 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 k aktuálnímu RepeaterItem
objektu . Nakonec odkazujeme na ObjectDataSource v ItemTemplate
a přiřadíme jeho CategoryID
hodnotu parametru k aktuální RepeaterItem
hodnotě CategoryID
.
S touto obslužnou rutinou ProductsByCategoryList
události je repeater v každém RepeaterItem
vázán na 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)
Přístup k datům produktů podle kategorií prostřednictvím kódu programu
Místo použití ObjectDataSource k načtení produktů pro aktuální kategorii bychom mohli vytvořit metodu v naší třídě ASP.NET kódu stránky s (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 takovou metodu máme v naší třídě kódu stránky ASP.NET a že má 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((int)(Eval("CategoryID"))) %>'>
...
</asp:Repeater>
Vlastnost Repeater s DataSource
používá syntaxi datové vazby k označení, že data pocházejí z GetProductsInCategory(categoryID)
metody . Vzhledem k tomu, že Eval("CategoryID")
vrátí hodnotu typu Object
, přetypujeme objekt na před Integer
předáním GetProductsInCategory(categoryID)
do metody . Všimněte siCategoryID
, že zde prostřednictvím syntaxe vazby dat je ve vnějším repeateru (CategoryList
), který je CategoryID
vázaný na záznamy v Categories
tabulce. Proto víme, že CategoryID
to nemůže být hodnota databáze NULL
, a proto můžeme metodu Eval
slepě přetypovat bez kontroly, jestli se jedná o DBNull
.
Při tomto přístupu potřebujeme vytvořit metodu GetProductsInCategory(categoryID)
a nechat ji načíst odpovídající sadu produktů vzhledem k dodanému categoryID
. Můžeme to udělat jednoduchým vrácením ProductsDataTable
hodnoty vrácené 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. Udělejte to pomocí následujícího kódu:
protected Northwind.ProductsDataTable GetProductsInCategory(int categoryID)
{
// Create an instance of the ProductsBLL class
ProductsBLL productAPI = new ProductsBLL();
// Return the products in the category
return productAPI.GetProductsByCategoryID(categoryID);
}
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čená Public
nebo Protected
; pokud je metoda označená 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 se chvíli podívejte na 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 (pokud chcete vidět snímek obrazovky, podívejte se zpět na Obrázek 5).
Poznámka
Vytvoření metody ve třídě ASP.NET stránky s kódem na pozadí může vypadat jako zaneprázdnění GetProductsInCategory(categoryID)
. 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((int)(Eval("CategoryID"))) %>'
? I když tato syntaxe nebude fungovat s naší aktuální implementací ProductsBLL
třídy (protože GetProductsByCategoryID(categoryID)
metoda je instanční metodou), můžete úpravou ProductsBLL
zahrnout statickou GetProductsByCategoryID(categoryID)
metodu nebo zahrnout statickou Instance()
metodu, která vrátí novou instanci 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 kódu na pozadí nám poskytuje 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, zachytí 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ý metodou GetProductsInCategory(categoryID)
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 vytěsní N + 1 volání do databáze jeden databázový dotaz, aby se získaly všechny kategorie, a pak volání 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 databázových voláních jedním voláním, abychom získali všechny kategorie a dalším, abychom získali všechny produkty. Jakmile budeme mít všechny produkty, můžeme tyto produkty filtrovat tak, aby pouze produkty, které odpovídají aktuálnímu CategoryID
, byly vázány na vnitřní repeater dané kategorie.
Abychom mohli tuto funkci poskytnout, stačí provést pouze mírnou úpravu GetProductsInCategory(categoryID)
metody v naší třídě kódu 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 místo toho nejprve získat přístup ke všem produktům (pokud k nim ještě nedošlo) a pak vrátit jenom filtrované zobrazení produktů na základě předávaného CategoryID
objektu .
private Northwind.ProductsDataTable allProducts = null;
protected Northwind.ProductsDataTable GetProductsInCategory(int categoryID)
{
// First, see if we've yet to have accessed all of the product information
if (allProducts == null)
{
ProductsBLL productAPI = new ProductsBLL();
allProducts = productAPI.GetProducts();
}
// Return the filtered view
allProducts.DefaultView.RowFilter = "CategoryID = " + categoryID;
return allProducts;
}
Všimněte si přidání proměnné na úrovni stránky . allProducts
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 ty řádky, které CategoryID
odpovídají zadanému CategoryID
. Tento přístup snižuje počet přístupů k databázi z N + 1 dolů na dva.
Toto vylepšení nezavádí žádnou změnu vykreslených značek stránky ani nepřináší méně záznamů než jiný přístup. Jednoduše se tím sníží počet volání do databáze.
Poznámka
Můžete intuitivně zdůvodnit, ž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í. Kromě toho může být vrácení všech produktů zbytečné, pokud zobrazujete pouze podmnožinu kategorií, což může být případ, pokud jste implementovali stránkování.
Jako vždy platí, že pokud jde o analýzu výkonu dvou technik, jediným jistým měřítkem je spouštění kontrolovaných testů přizpůsobených běžným scénářům vaší aplikace.
Souhrn
V tomto kurzu jsme viděli, jak vnořit jeden datový webový ovládací prvek do jiného, konkrétně jsme prozkoumali, jak nechat vnější repeater zobrazit položku pro každou kategorii s vnitřním Repeaterem, který obsahuje produkty pro každou kategorii v seznamu s odrážkami. Hlavní výzvou při vytváření vnořeného uživatelského rozhraní je přístup ke správným datům a jejich vazba na vnitřní ovládací prvek webu. K dispozici jsou různé techniky, z nichž dvě jsme prozkoumali v tomto kurzu. První zkoumaný přístup použil ObjectDataSource v ovládacím prvku ItemTemplate
vnější data web, který byl vázán na vnitřní data Web ovládací prvek prostřednictvím jeho DataSourceID
vlastnosti. Druhá technika přistupovala k datům prostřednictvím metody ve třídě kódu stránky ASP.NET. Tuto metodu pak lze vázat na vlastnost ovládacího prvku DataSource
web vnitřních dat prostřednictvím syntaxe vazby dat.
Zatímco vnořené uživatelské rozhraní zkoumané 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 seznamu DataList atd.
Šťastné 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 najít na mitchell@4GuysFromRolla.comadrese . nebo prostřednictvím jeho blogu, který najdete na http://ScottOnWriting.NETadrese .
Zvláštní poděkování
Tato série kurzů byla zkontrolována mnoha užitečnými recenzenty. Hlavními recenzenty tohoto kurzu byli Zack Jones a Liz Shulok. Chcete si projít moje nadcházející články na WEBU MSDN? Pokud ano, dejte mi čáru na mitchell@4GuysFromRolla.comadresu .