Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
által Scott Mitchell
Ebben az oktatóanyagban bemutatjuk, hogyan használható egy másik repeaterbe ágyazott repeater. A példák bemutatják, hogyan lehet deklaratívan és programozottan feltölteni a belső repeatert.
Bevezetés
A statikus HTML- és adatkötési szintaxis mellett a sablonok webvezérlőket és felhasználói vezérlőket is tartalmazhatnak. Ezek a webvezérlők deklaratív, adatkötési szintaxissal rendelhetők hozzá a tulajdonságaikhoz, vagy programozott módon is elérhetők a megfelelő kiszolgálóoldali eseménykezelőkben.
A vezérlők sablonba való beágyazásával a megjelenés és a felhasználói élmény testre szabható és továbbfejleszthető. A GridView Control oktatóanyag Sablonmezők használata című oktatóanyagában például azt láttuk , hogyan szabhatja testre a GridView-megjelenítést úgy, hogy hozzáad egy Naptár vezérlőelemet egy Sablonmezőhöz, hogy megjelenítse az alkalmazottak munkadátumát; Az Érvényesítési vezérlők hozzáadása a szerkesztő- és beszúrási felületekhez , valamint az Adatmódosítási felület testreszabása oktatóanyagokban láthattuk, hogyan szabhatja testre a szerkesztő- és beszúrási felületeket érvényesítési vezérlők, szövegdobozok, legördülő listák és egyéb webes vezérlők hozzáadásával.
A sablonok más adat webes vezérlőket is tartalmazhatnak. Ez azt is jelentheti, hogy egy DataList egy másik DataListet (vagy Repeatert, GridView-t vagy DetailsView-t stb.) tartalmaz a sablonjaiban. Az ilyen felülettel kapcsolatos kihívás a megfelelő adatok kötése a belső adat webes vezérlőhöz. Az ObjectDataSource használatával elérhető deklaratív lehetőségektől a programozottakig számos különböző megközelítés érhető el.
Ebben az oktatóanyagban bemutatjuk, hogyan használható egy másik repeaterbe ágyazott repeater. A külső Repeater az adatbázis minden kategóriájához tartalmaz egy elemet, amely megjeleníti a kategória nevét és leírását. Az egyes kategóriaelemek belső repeaterei egy listajeles listában jelenítik meg az adott kategóriához tartozó összes termék adatait (lásd az 1. ábrát). Példáink bemutatják, hogyan lehet deklaratívan és programozottan feltölteni a belső repeatert.
Minden kategória, valamint a termékei fel vannak sorolva.
1. ábra: Az egyes kategóriák, valamint a termékek listázva vannak (kattintson ide a teljes méretű kép megtekintéséhez)
1. lépés: A kategórialista létrehozása
Beágyazott adat webes vezérlőket használó lap létrehozásakor hasznosnak találom a legkülső adat webes vezérlő tervezését, létrehozását és tesztelését anélkül, hogy a belső beágyazott vezérlő miatt kellene aggódnia. Kezdjük tehát azokkal a lépésekkel, amelyek szükségesek ahhoz, hogy a laphoz hozzáadjuk a Repeater elemet, amely az egyes kategóriák nevét és leírását sorolja fel.
Először nyissa meg a NestedControls.aspx lapot a DataListRepeaterBasics mappában, és adjon hozzá egy Repeater vezérlőt a laphoz, és állítsa be a tulajdonságát ID a következőre CategoryList: . Az Repeater intelligens címkéje alapján hozzon létre egy új ObjectDataSource nevű CategoriesDataSourceobjektumot.
2. ábra: Nevezze el az Új ObjectDataSource-t CategoriesDataSource (kattintson ide a teljes méretű kép megtekintéséhez)
Konfigurálja az ObjectDataSource-t úgy, hogy az adatokat az CategoriesBLL osztály GetCategories metódusából lekérje.
3. ábra: Az ObjectDataSource konfigurálása az CategoriesBLL osztály GetCategories metódusának használatára (ide kattintva megtekintheti a teljes méretű képet)
Az Ismétlő sablon tartalmának megadásához a Forrás nézetre kell lépnünk, és manuálisan be kell írnunk a deklaratív szintaxist. Adjon hozzá egy olyan nevet ItemTemplate , amely megjeleníti a kategória nevét egy <h4> elemben, és a kategória leírását egy bekezdéselemben (<p>). Továbbá különítsuk el az egyes kategóriákat egy vízszintes szabálysal (<hr>). A módosítások elvégzése után a lapnak deklaratív szintaxist kell tartalmaznia az Repeaterhez és az ObjectDataSource-hoz, amely az alábbihoz hasonló:
<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>
A 4. ábra a böngészőn keresztüli megtekintéskor mutatja be az előrehaladást.
4. ábra: Minden kategória neve és leírása szerepel a listában, vízszintes szabály szerint elválasztva (kattintással megtekintheti a teljes méretű képet)
2. lépés: A beágyazott termékismétlő hozzáadása
A kategórialista befejeztével a következő feladatunk egy repeater hozzáadása az CategoryList s-hez ItemTemplate , amely megjeleníti a megfelelő kategóriába tartozó termékek adatait. Ennek a belső repeaternek több módon is lekérhetjük az adatokat, amelyek közül kettőt hamarosan megvizsgálunk. Egyelőre csak hozzuk létre az Repeater termékeket az CategoryList Repeater s ItemTemplate- ben. Pontosan tegyük fel, hogy a Repeater termék megjeleníti az egyes termékeket egy pontozott listában, ahol minden listaelem tartalmazza a termék nevét és árát.
Ennek a Repeaternek a létrehozásához manuálisan kell beírnunk a belső Repeater deklaratív szintaxisát és sablonjait az CategoryList struktúrájába ItemTemplate. Adja hozzá a következő korrektúrát az CategoryList Repeater s-ben ItemTemplate:
<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>
3. lépés: A Category-Specific termékek kötése a ProductsByCategoryList repeaterhez
Ha ezen a ponton egy böngészőben látogatja meg az oldalt, a képernyő ugyanúgy fog kinézni, mint a 4. ábrán, mivel még nem kötöttünk adatokat az Repeaterhez. Van néhány módja annak, hogy megragadjuk a megfelelő termékrekordokat, és kössük őket az Repeaterhez, néhány hatékonyabb, mint mások. Itt a fő kihívás a megfelelő termékek visszaszerzése a megadott kategóriához.
A belső Repeater-vezérlőhöz kötéshez szükséges adatok deklaratív módon, az Ismétlőben CategoryListtalálható ItemTemplate ObjectDataSource-on keresztül érhetők el, vagy programozott módon, a ASP.NET lap kód mögötti oldaláról. Hasonlóképpen, ezek az adatok deklaratív módon kötődhetnek a belső repeaterhez – a belső Repeater s DataSourceID tulajdonságán keresztül, vagy deklaratív adatkötési szintaxissal vagy programozott módon az Repeater eseménykezelőjében CategoryListItemDataBound a belső repeaterre hivatkozva, a tulajdonsága programozott módon történő beállításával DataSource és a metódus meghívásával DataBind() . Vizsgáljuk meg ezeket a megközelítéseket.
Az adatok deklaratív elérése ObjectDataSource-vezérlővel és azItemDataBoundeseménykezelővel
Mivel ebben az oktatóanyag-sorozatban széles körben használtuk az ObjectDataSource-t, a példában szereplő adatok eléréséhez a legtermtermelműbb választás az ObjectDataSource használata. Az ProductsBLL osztály olyan metódussal GetProductsByCategoryID(categoryID) rendelkezik, amely adatokat ad vissza a megadott categoryIDtermékhez tartozó termékekről. Ezért hozzáadhatunk egy ObjectDataSource-t az CategoryList Repeater-hez ItemTemplate, és konfigurálhatjuk úgy, hogy az adatai az osztály metódusán keresztül érhetők el.
Sajnos az Ismétlő nem teszi lehetővé a sablonok szerkesztését a Tervező nézetben, ezért kézzel kell hozzáadnunk az ObjectDataSource vezérlő deklaratív szintaxisát. Az alábbi szintaxis a(z) CategoryList ObjectDataSource új hozzáadása után az ItemTemplate Repeater ProductsByCategoryDataSource szintaxisát mutatja be:
<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>
Az ObjectDataSource megközelítés használatakor az ProductsByCategoryList Repeater s tulajdonságát DataSourceID az ObjectDataSource (ID) értékre ProductsByCategoryDataSource kell állítani. Figyelje meg azt is, hogy az ObjectDataSource-nak van egy <asp:Parameter> eleme, amely meghatározza a categoryID metódusnak GetProductsByCategoryID(categoryID) átadott értéket. De hogyan határozzuk meg ezt az értéket? Ideális esetben egyszerűen beállíthatjuk az DefaultValue elem tulajdonságát adatkötési <asp:Parameter> szintaxis használatával, például:
<asp:Parameter Name="CategoryID" Type="Int32"
DefaultValue='<%# Eval("CategoryID")' />
Sajnos az adatkötés szintaxisa csak az eseményt DataBinding tartalmazó vezérlőelemekben érvényes. Az Parameter osztály nem rendelkezik ilyen eseménysel, ezért a fenti szintaxis érvénytelen, és futásidejű hibát eredményez.
Az érték beállításához létre kell hoznunk egy eseménykezelőt az CategoryList Repeater s ItemDataBound eseményhez. Ne feledje, hogy az ItemDataBound esemény egyszer aktiválódik az repeaterhez kötött minden egyes elemnél. Ezért minden alkalommal, amikor ez az esemény aktiválódik a külső Repeater esetében, hozzárendelhetjük az aktuális CategoryID értéket az ProductsByCategoryDataSource ObjectDataSource paraméteréhez CategoryID .
Hozzon létre egy eseménykezelőt az CategoryList Repeater s ItemDataBound eseményhez a következő kóddal:
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
Ez az eseménykezelő azzal kezdődik, hogy ellenőrzi, valóban egy adatelemmel foglalkozunk, nem pedig a fejléc, lábléc vagy elválasztó elemmel. Ezután hivatkozunk a tényleges CategoriesRow példányra, amely éppen az aktuális RepeaterItem-hez lett kötve. Végül hivatkozunk az ObjectDataSource-ra, ItemTemplate és hozzárendeljük annak paraméterértékét CategoryID az CategoryID aktuálishoz RepeaterItem.
Ezzel az eseménykezelővel a(z) ProductsByCategoryList kategóriába tartozó termékekhez kapcsolódik a RepeaterItem minden RepeaterItem Ismétlője. Az 5. ábrán az eredményként kapott kimenet képernyőképe látható.
5. ábra: A külső ismétlő felsorolja az egyes kategóriákat; az Inner One listázza az adott kategóriához tartozó termékeket (ide kattintva megtekintheti a teljes méretű képet)
Termékek elérése kategóriaadatok szerint programozott módon
Ahelyett, hogy objectDataSource-t használnánk az aktuális kategória termékeinek lekéréséhez, létrehozhatunk egy metódust a ASP.NET lap mögötti osztályában (vagy a App_Code mappában vagy egy külön Osztálytár projektben), amely a megfelelő termékkészletet adja vissza egy CategoryIDadott osztályban való átadáskor. Tegyük fel, hogy volt egy ilyen metódus az ASP.NET kód mögötti osztályban, és hogy elnevezték GetProductsInCategory(categoryID). Ezzel a módszerrel az aktuális kategória termékeit a belső Repeaterhez köthetjük az alábbi deklaratív szintaxissal:
<asp:Repeater runat="server" ID="ProductsByCategoryList" EnableViewState="False"
DataSource='<%# GetProductsInCategory(CType(Eval("CategoryID"), Integer)) %>'>
...
</asp:Repeater>
Az Repeater s DataSource tulajdonság a databinding szintaxissal jelzi, hogy az adatok a GetProductsInCategory(categoryID) metódusból származnak. Mivel Eval("CategoryID") egy típusértéket Objectad vissza, az objektumot egy Integer elé vetjük, mielőtt átadnánk azt a GetProductsInCategory(categoryID) metódusnak. Vegye figyelembe, hogy itt a CategoryID adatkapcsolat szintaxisán keresztül érhető el a CategoryID Repeater (), amely a CategoryList táblázat rekordjaihoz van kapcsolatban. Ezért tudjuk, hogy CategoryID nem lehet NULL adatbázis-érték, ezért vakon vethetjük be a Eval metódust anélkül, hogy ellenőriznénk, hogy egy DBNull-vel van dolgunk.
Ezzel a megoldással létre kell hoznunk a GetProductsInCategory(categoryID) módszert, és le kell kérni a megfelelő termékkészletet a megadott categoryID alapján. Ezt úgy tehetjük meg, hogy egyszerűen visszaadjuk a ProductsDataTable értéket, amit a ProductsBLL osztály GetProductsByCategoryID(categoryID) metódusa visszaad. Hozzuk létre a GetProductsInCategory(categoryID) metódust az oldal NestedControls.aspx kód mögötti osztályában. Ehhez használja a következő kódot:
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
Ez a metódus egyszerűen létrehozza a ProductsBLL metódus egy példányát, és visszaadja a GetProductsByCategoryID(categoryID) metódus eredményeit. Vegye figyelembe, hogy a metódust meg kell jelölni Public , vagy Protected– ha a metódus meg van jelölve Private– nem lesz elérhető a ASP.NET lap deklaratív korrektúrája alapján.
Miután végrehajtotta ezeket a módosításokat az új technika használatához, szánjon egy kis időt az oldal megtekintésére egy böngészőben. A kimenetnek meg kell egyeznie a kimenettel az ObjectDataSource és ItemDataBound az eseménykezelő megközelítés használatakor (a képernyőfelvétel megtekintéséhez tekintse meg az 5. ábrát).
Megjegyzés:
Úgy tűnhet, mintha felesleges munka lenne létrehozni az ASP.NET oldal kód-behind osztályában a GetProductsInCategory(categoryID) metódust. Végül is ez a metódus egyszerűen létrehozza az ProductsBLL osztály egy példányát, és visszaadja GetProductsByCategoryID(categoryID) a metódus eredményeit. Miért nem hívjuk ezt a metódust közvetlenül a belső repeater adatkötési szintaxisából, például: DataSource='<%# ProductsBLL.GetProductsByCategoryID(CType(Eval("CategoryID"), Integer)) %>'? Bár ez a szintaxis nem fog működni az ProductsBLL osztály jelenlegi implementációjával (mivel a GetProductsByCategoryID(categoryID) metódus egy példánymetódus), módosíthatja ProductsBLL , hogy statikus GetProductsByCategoryID(categoryID) metódust tartalmazzon, vagy hogy az osztály tartalmazzon egy statikus Instance() metódust az ProductsBLL osztály új példányának visszaadásához.
Bár az ilyen módosítások szükségtelenné tennék a GetProductsInCategory(categoryID) metódust a ASP.NET lap mögötti osztályában, a kód mögötti osztály metódusa nagyobb rugalmasságot biztosít a lekért adatokkal való munkában, amint azt hamarosan látni fogjuk.
Az összes termékinformáció beolvasása egyszerre
A két korábbi technika, amelyet megvizsgáltunk, megragadja ezeket a termékeket az aktuális kategóriához az osztály ProductsBLL metódusának GetProductsByCategoryID(categoryID) meghívásával (az első megközelítés egy ObjectDataSource-on keresztül történt, a második pedig a kód mögötti osztály GetProductsInCategory(categoryID) metódusán keresztül). A metódus minden meghívásakor az üzleti logikai réteg lehívja az adatelérési réteget, amely egy SQL-utasítással kéri le az adatbázist, amely a megadott bemeneti paraméternek megfelelő sorokat ad vissza a Products táblából CategoryID .
A rendszerben az N kategóriákat figyelembe véve ez a megközelítés N + 1 hívást indít az adatbázishoz egy adatbázis-lekérdezéssel, hogy lekérje az összes kategóriát, majd N-hívásokkal lekérje az egyes kategóriákhoz tartozó termékeket. Az összes szükséges adatot azonban csak két adatbázisban tudjuk lekérni, egy hívással lekérhetjük az összes kategóriát, egy másikat pedig az összes termék lekéréséhez. Miután minden termékünk megvan, szűrhetjük ezeket a termékeket, hogy csak az aktuálisnak CategoryID megfelelő termékek legyenek az adott kategória belső repeateréhez kötve.
Ennek a funkciónak a biztosításához csak egy kis módosítást kell végezni a GetProductsInCategory(categoryID) metódushoz az ASP.NET lap kód-behind osztályában. Ahelyett, hogy vakon visszaadnánk az ProductsBLL osztály GetProductsByCategoryID(categoryID) metódusának eredményeit, előbb hozzáférhetünk az összes termékhez (ha még nem történt meg a hozzáférés), majd csak a termékek szűrt nézetét adjuk vissza az átadott CategoryID alapján.
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
Jegyezze fel az oldalszintű változó hozzáadását. allProducts Ez az összes termékre vonatkozó információkat tartalmazza, és a metódus első meghívásakor GetProductsInCategory(categoryID) lesz feltöltve. Az objektum létrehozásának és feltöltésének biztosítása allProducts után a metódus szűri a DataTable eredményeit, így csak azok a sorok érhetők el, amelyek CategoryID megegyeznek a megadottakkal CategoryID . Ez a módszer csökkenti az adatbázis N +1-ről kettőre való elérésének számát.
Ez a fejlesztés nem módosítja az oldal renderelt jelölését, és nem eredményez kevesebb rekordot, mint a másik megközelítés. Egyszerűen csökkenti az adatbázisba irányuló hívások számát.
Megjegyzés:
Intuitív indok lehet, hogy az adatbázis-hozzáférések számának csökkentése biztosan javítja a teljesítményt. Előfordulhat azonban, hogy ez nem így van. Ha például sok olyan terméke van, amelynek CategoryIDNULL, akkor a GetProducts metódus hívása olyan termékeket ad vissza, amelyek soha nem jelennek meg. Ezenkívül az összes termék visszaküldése pazarló lehet, ha csak a kategóriák egy részhalmazát jeleníti meg, ami akkor fordulhat elő, ha a lapozást implementálta.
Mint mindig, amikor két technika teljesítményének elemzéséről van szó, az egyetlen surefire-mérték az alkalmazás gyakori esetforgatókönyveihez szabott ellenőrzött tesztek futtatása.
Összefoglalás
Ebben az oktatóanyagban azt láttuk, hogyan ágyazhat be egy webes adatvezérlőt egy másikba, különösen azt vizsgálva, hogyan jeleníthet meg egy külső Repeater egy elemet az egyes kategóriákhoz egy belső Repeaterrel, amely felsorolja az egyes kategóriák termékeit egy felsorolásjelű listában. A beágyazott felhasználói felület kialakításának fő kihívása a megfelelő adatok elérése és kötése a belső adat webes vezérlőhöz. Számos különböző technika érhető el, amelyek közül kettőt ebben az oktatóanyagban vizsgáltunk. Az első vizsgált megközelítés egy ObjectDataSource-t használt a külső adat webes vezérlőkben ItemTemplate , amely a tulajdonságán keresztül a belső adat webes vezérlőhöz DataSourceID volt kötve. A második technika egy metóduson keresztül fért hozzá az adatokhoz a ASP.NET lap kód mögötti osztályában. Ez a módszer ezután adatkötési szintaxissal köthető a belső adat webvezérlő tulajdonságához DataSource .
Bár az oktatóanyagban vizsgált beágyazott felhasználói felület egy repeaterbe ágyazott repeatert használt, ezek a technikák kiterjeszthetők a többi adat webes vezérlőre is. Egy ismétlőt beágyazhat egy GridView-be, vagy egy GridView-t egy DataListen belül, és így tovább.
Boldog programozást!
Tudnivalók a szerzőről
Scott Mitchell, hét ASP/ASP.NET-könyv szerzője és a 4GuysFromRolla.com alapítója, 1998 óta dolgozik a Microsoft webtechnológiáival. Scott független tanácsadóként, edzőként és íróként dolgozik. Legújabb könyve Sams Tanuld meg ASP.NET 2.0 24 óra alatt. Ő itt elérhető mitchell@4GuysFromRolla.com.
Külön köszönet
Ezt az oktatóanyag-sorozatot sok hasznos véleményező áttekintette. Az oktatóanyag vezető véleményezői Zack Jones és Liz Shulok voltak. Szeretné áttekinteni a közelgő MSDN-cikkeimet? Ha igen, írj egy sort a mitchell@4GuysFromRolla.com-ra.