Zagnieżdżone kontrolki internetowe danych (VB)
W tym samouczku dowiesz się, jak używać repeatera zagnieżdżonego wewnątrz innego repeatera. Przykłady ilustrują sposób wypełniania wewnętrznego repeatera zarówno deklaratywnie, jak i programowo.
Wprowadzenie
Oprócz statycznej składni HTML i składni powiązania danych szablony mogą również zawierać kontrolki sieci Web i kontrolki użytkownika. Te kontrolki sieci Web mogą mieć przypisane ich właściwości za pośrednictwem składni deklaratywnej, składni powiązania danych lub można uzyskać do nich dostęp programowo w odpowiednich programowych programowych programach obsługi zdarzeń po stronie serwera.
Osadzając kontrolki w szablonie, wygląd i środowisko użytkownika można dostosować i ulepszyć. Na przykład w samouczku Using TemplateFields in the GridView Control (Używanie pól szablonów w kontrolce GridView ) pokazano, jak dostosować wyświetlanie kontrolki GridView przez dodanie kontrolki Kalendarz w elemecie TemplateField w celu wyświetlenia daty zatrudnienia pracownika; w samouczkach Dodawanie kontrolek walidacji do interfejsów edycji i wstawiania oraz dostosowywanie interfejsu modyfikacji danych widzieliśmy, jak dostosować interfejsy edycji i wstawiania, dodając kontrolki walidacji, TextBoxes, DropDownLists i inne kontrolki sieci Web.
Szablony mogą również zawierać inne kontrolki sieci Web danych. Oznacza to, że możemy mieć listę danych zawierającą inną listę danych (lub repeater lub GridView lub DetailsView itd.) w swoich szablonach. Wyzwanie związane z takim interfejsem polega na powiązaniu odpowiednich danych z wewnętrzną kontrolą sieci Web danych. Dostępnych jest kilka różnych metod, od opcji deklaratywnych przy użyciu obiektu ObjectDataSource po metody programowe.
W tym samouczku dowiesz się, jak używać repeatera zagnieżdżonego wewnątrz innego repeatera. Zewnętrzny repeater będzie zawierać element dla każdej kategorii w bazie danych, wyświetlając nazwę i opis kategorii. Każdy wewnętrzny repeater elementu kategorii będzie wyświetlać informacje o każdym produkcie należącym do tej kategorii (zobacz Rysunek 1) na liście punktowanej. Nasze przykłady ilustrują sposób wypełniania wewnętrznego repeatera zarówno deklaratywnie, jak i programowo.
Rysunek 1. Każda kategoria wraz z produktami znajduje się na liście (kliknij, aby wyświetlić obraz pełnowymiarowy)
Krok 1. Tworzenie listy kategorii
Podczas tworzenia strony korzystającej z zagnieżdżonych kontrolek sieci Web uważam, że warto zaprojektować, utworzyć i przetestować najbardziej zewnętrzną kontrolkę sieci Web danych, nie martwiąc się nawet o wewnętrzną zagnieżdżonym kontrolce. Dlatego zacznijmy od przejścia przez kroki niezbędne do dodania repeatera do strony zawierającej nazwę i opis każdej kategorii.
Zacznij od otwarcia strony w DataListRepeaterBasics
folderze i dodania kontrolki Repeater do strony, ustawiając jej ID
właściwość na CategoryList
.NestedControls.aspx
Z inteligentnego tagu Repeater wybierz opcję utworzenia nowego obiektu ObjectDataSource o nazwie CategoriesDataSource
.
Rysunek 2. Nadaj nazwę nowemu obiektowiDataSource CategoriesDataSource
(kliknij, aby wyświetlić obraz o pełnym rozmiarze)
Skonfiguruj obiekt ObjectDataSource, aby pobierał dane z CategoriesBLL
metody klasy s GetCategories
.
Rysunek 3. Konfigurowanie obiektu ObjectDataSource do używania CategoriesBLL
metody Klasy GetCategories
(kliknij, aby wyświetlić obraz o pełnym rozmiarze)
Aby określić zawartość szablonu Repeater, musimy przejść do widoku Źródło i ręcznie wprowadzić składnię deklaratywną. Dodaj element ItemTemplate
wyświetlający nazwę kategorii w <h4>
elemecie i opis kategorii w elemecie akapitu (<p>
). Ponadto należy oddzielić każdą kategorię za pomocą reguły poziomej (<hr>
). Po wprowadzeniu tych zmian strona powinna zawierać składnię deklaratywną dla elementu Repeater i ObjectDataSource, który jest podobny do następującego:
<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>
Rysunek 4 przedstawia postęp podczas wyświetlania za pośrednictwem przeglądarki.
Rysunek 4. Każda nazwa i opis kategorii jest wymieniona, oddzielona przez regułę poziomą (kliknij, aby wyświetlić obraz pełnowymiarowy)
Krok 2. Dodawanie zagnieżdżonego modułu powtarzającego produkt
Po zakończeniu listy kategorii następnym zadaniem jest dodanie repeatera do CategoryList
s ItemTemplate
, który wyświetla informacje o tych produktach należących do odpowiedniej kategorii. Istnieje wiele sposobów, na które możemy pobrać dane dla tego wewnętrznego repeatera, z których dwa omówimy wkrótce. Na razie po prostu utwórzmy produkty Repeater w elemencie CategoryList
Repeater s ItemTemplate
. W szczególności załóżmy, że produkt Repeater wyświetla każdy produkt na liście punktowanej z każdym elementem listy, w tym nazwą produktu i ceną.
Aby utworzyć ten repeater, musimy ręcznie wprowadzić wewnętrzną składnię deklaratywną repeatera i szablony do CategoryList
elementu s ItemTemplate
. Dodaj następujące znaczniki w elemencie CategoryList
Repeater s 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>
Krok 3. Powiązanie produktów Category-Specific z elementem ProductsByCategoryList Repeater
Jeśli odwiedzisz stronę za pośrednictwem przeglądarki w tym momencie, ekran będzie wyglądać tak samo jak na rysunku 4, ponieważ nie powiązaliśmy jeszcze żadnych danych z repeaterem. Istnieje kilka sposobów, na które możemy pobrać odpowiednie rekordy produktu i powiązać je z repeater, niektóre bardziej wydajne niż inne. Głównym wyzwaniem jest powrót odpowiednich produktów dla określonej kategorii.
Dane powiązane z wewnętrzną kontrolką repeatera mogą być dostępne deklaratywne za pośrednictwem obiektu ObjectDataSource w CategoryList
elemencie Repeater s ItemTemplate
lub programowo ze strony ASP.NET strony kodowej. Podobnie te dane mogą być powiązane z wewnętrznym repeaterem deklaratywnie — za pośrednictwem właściwości wewnętrznego repeatera DataSourceID
lub deklaratywnej składnibinowania danych lub programowo, odwołując się do wewnętrznego repeatera w CategoryList
procedurze obsługi zdarzeń Repeatera ItemDataBound
, programowo ustawiając jej właściwość i wywołując jej DataSource
DataBind()
metodę. Przyjrzyjmy się każdemu z tych podejść.
Uzyskiwanie dostępu do danych deklaratywnie za pomocą kontrolki ObjectDataSource i programu obsługi zdarzeńItemDataBound
Ponieważ w tej serii samouczków korzystaliśmy z obiektu ObjectDataSource, najbardziej naturalnym wyborem do uzyskiwania dostępu do danych w tym przykładzie jest trzymanie się obiektu ObjectDataSource. Klasa ProductsBLL
ma metodę zwracającą GetProductsByCategoryID(categoryID)
informacje o tych produktach należących do określonego categoryID
elementu . W związku z tym możemy dodać obiekt ObjectDataSource do CategoryList
elementu Repeater s ItemTemplate
i skonfigurować go w celu uzyskania dostępu do danych z tej metody klasy.
Niestety, repeater nie zezwala na edytowanie szablonów za pomocą widoku projektu, dlatego musimy ręcznie dodać składnię deklaratywną dla tej kontrolki ObjectDataSource. Poniższa składnia przedstawia CategoryList
funkcję Repeater s ItemTemplate
po dodaniu nowego obiektu 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>
W przypadku korzystania z podejścia ObjectDataSource należy ustawić ProductsByCategoryList
właściwość Repeater na DataSourceID
ID
właściwość ObjectDataSource (ProductsByCategoryDataSource
). Zwróć również uwagę, że nasza baza danych ObjectDataSource zawiera <asp:Parameter>
element określający wartość, categoryID
która zostanie przekazana do GetProductsByCategoryID(categoryID)
metody. Ale jak określić tę wartość? W idealnym przypadku można ustawić DefaultValue
właściwość <asp:Parameter>
elementu przy użyciu składni powiązania danych, w następujący sposób:
<asp:Parameter Name="CategoryID" Type="Int32"
DefaultValue='<%# Eval("CategoryID")' />
Niestety składnia powiązania danych jest prawidłowa tylko w kontrolkach, które mają DataBinding
zdarzenie. Klasa Parameter
nie ma takiego zdarzenia, dlatego powyższej składni jest niedozwolona i spowoduje błąd środowiska uruchomieniowego.
Aby ustawić tę wartość, musimy utworzyć procedurę obsługi zdarzeń dla CategoryList
zdarzenia repeatera ItemDataBound
. Pamiętaj, że ItemDataBound
zdarzenie jest uruchamiane raz dla każdego elementu powiązanego z repeater. W związku z tym za każdym razem, gdy to zdarzenie jest uruchamiane dla zewnętrznego repeatera, możemy przypisać bieżącą CategoryID
wartość do parametru ProductsByCategoryDataSource
ObjectDataSource CategoryID
.
Utwórz procedurę obsługi zdarzeń dla CategoryList
zdarzenia Repeater za ItemDataBound
pomocą następującego kodu:
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
Ta procedura obsługi zdarzeń rozpoczyna się od upewnienia się, że mamy do czynienia z elementem danych, a nie nagłówkiem, stopką lub elementem separatora. Następnie odwołujemy się do rzeczywistego CategoriesRow
wystąpienia, które właśnie zostało powiązane z bieżącym RepeaterItem
. Na koniec odwołujemy się do obiektu ObjectDataSource w obiekcie ItemTemplate
i przypisujemy jej CategoryID
wartość parametru do CategoryID
bieżącej RepeaterItem
wartości .
W przypadku tej procedury obsługi zdarzeń repeater ProductsByCategoryList
w każdym RepeaterItem
z nich jest powiązany z tymi produktami w RepeaterItem
kategorii s. Rysunek 5 przedstawia zrzut ekranu wynikowych danych wyjściowych.
Rysunek 5. Zewnętrzny powtarzacz Listy każdej kategorii; wewnętrzna Listy produkty dla tej kategorii (kliknij, aby wyświetlić obraz pełnowymiarowy)
Programowe uzyskiwanie dostępu do produktów według danych kategorii
Zamiast używać obiektu ObjectDataSource do pobierania produktów dla bieżącej kategorii, możemy utworzyć metodę w naszej klasie ASP.NET stronicowania kodu (lub w App_Code
folderze lub w osobnym projekcie biblioteki klas), który zwraca odpowiedni zestaw produktów po przekazaniu w CategoryID
obiekcie . Załóżmy, że mieliśmy taką metodę w naszej klasie ASP.NET page-behind i że została ona nazwana GetProductsInCategory(categoryID)
. Za pomocą tej metody możemy powiązać produkty dla bieżącej kategorii z wewnętrznym repeaterem przy użyciu następującej składni deklaratywnej:
<asp:Repeater runat="server" ID="ProductsByCategoryList" EnableViewState="False"
DataSource='<%# GetProductsInCategory(CType(Eval("CategoryID"), Integer)) %>'>
...
</asp:Repeater>
Właściwość Repeater DataSource
używa składni powiązania danych, aby wskazać, że jej dane pochodzą z GetProductsInCategory(categoryID)
metody . Ponieważ Eval("CategoryID")
zwraca wartość typu Object
, rzutujemy obiekt na obiekt przed przekazaniem GetProductsInCategory(categoryID)
go do Integer
metody . Należy pamiętać, że dostęp CategoryID
do tego miejsca za pośrednictwem składni powiązania danych jest CategoryID
elementem w zewnętrznym repeaterze (CategoryList
), tym, który jest powiązany z rekordami w Categories
tabeli. W związku z tym wiemy, że CategoryID
nie może być wartością bazy danych NULL
, dlatego możemy ślepo rzutować Eval
metodę bez sprawdzania, czy mamy do czynienia z wartością DBNull
.
Dzięki temu podejściu musimy utworzyć metodę GetProductsInCategory(categoryID)
i pobrać odpowiedni zestaw produktów, biorąc pod uwagę podany element categoryID
. Możemy to zrobić, po prostu zwracając zwrócony ProductsDataTable
przez metodę ProductsBLL
klasy s GetProductsByCategoryID(categoryID)
. Utwórzmy metodę GetProductsInCategory(categoryID)
w klasie code-behind dla naszej NestedControls.aspx
strony. Wykonaj to przy użyciu następującego kodu:
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
Ta metoda po prostu tworzy wystąpienie ProductsBLL
metody i zwraca wyniki GetProductsByCategoryID(categoryID)
metody. Należy pamiętać, że metoda musi być oznaczona Public
lub Protected
; jeśli metoda jest oznaczona Private
, nie będzie dostępna z ASP.NET strony deklaratywnej znaczników.
Po wprowadzeniu tych zmian, aby użyć tej nowej techniki, poświęć chwilę, aby wyświetlić stronę za pośrednictwem przeglądarki. Dane wyjściowe powinny być identyczne z danymi wyjściowymi podczas korzystania z metody ObjectDataSource i ItemDataBound
procedury obsługi zdarzeń (zapoznaj się z rysunkiem 5, aby zobaczyć zrzut ekranu).
Uwaga
Może się wydawać, że zajęta praca nad utworzeniem GetProductsInCategory(categoryID)
metody w klasie ASP.NET stronicowania kodu. W końcu ta metoda po prostu tworzy wystąpienie ProductsBLL
klasy i zwraca wyniki jej GetProductsByCategoryID(categoryID)
metody. Dlaczego nie tylko wywołaj tę metodę bezpośrednio ze składni powiązania danych w wewnętrznym repeaterze, na przykład: DataSource='<%# ProductsBLL.GetProductsByCategoryID(CType(Eval("CategoryID"), Integer)) %>'
? Chociaż ta składnia nie będzie działać z naszą bieżącą implementacją ProductsBLL
klasy (ponieważ GetProductsByCategoryID(categoryID)
metoda jest metodą wystąpienia), można zmodyfikować ProductsBLL
tak, aby zawierała metodę statyczną lub klasa zawiera metodę statyczną GetProductsByCategoryID(categoryID)
Instance()
, aby zwrócić nowe wystąpienie ProductsBLL
klasy.
Chociaż takie modyfikacje wyeliminowałyby konieczność GetProductsInCategory(categoryID)
korzystania z metody w klasie kodu za stroną ASP.NET, metoda klasy za pomocą kodu zapewnia nam większą elastyczność pracy z pobranymi danymi, jak wkrótce zobaczymy.
Pobieranie wszystkich informacji o produkcie jednocześnie
Dwie przekonujące techniki, które zbadaliśmy, pobierają te produkty dla bieżącej kategorii, wywołując metodę ProductsBLL
klasy GetProductsByCategoryID(categoryID)
(pierwsze podejście zrobiło to za pośrednictwem obiektu ObjectDataSource, drugiego za pomocą GetProductsInCategory(categoryID)
metody w klasie code-behind). Za każdym razem, gdy ta metoda jest wywoływana, warstwa logiki biznesowej wywołuje warstwę dostępu do danych, która wysyła zapytanie do bazy danych za pomocą instrukcji SQL zwracającej wiersze z tabeli, których CategoryID
pole jest zgodne z Products
podanym parametrem wejściowym.
Biorąc pod uwagę N kategorii w systemie, to podejście nets N + 1 wywołuje do bazy danych jedno zapytanie bazy danych w celu pobrania wszystkich kategorii, a następnie N wywołań w celu uzyskania produktów specyficznych dla każdej kategorii. Możemy jednak pobrać wszystkie potrzebne dane w zaledwie dwóch bazach danych wywołuje jedno wywołanie, aby pobrać wszystkie kategorie i inne, aby pobrać wszystkie produkty. Gdy mamy wszystkie produkty, możemy filtrować te produkty, aby tylko produkty pasujące do bieżącej CategoryID
były powiązane z wewnętrzną powtarzaną kategorią.
Aby zapewnić tę funkcję, musimy wprowadzić niewielką modyfikację GetProductsInCategory(categoryID)
metody w naszej klasie ASP.NET stronicowej klasy. Zamiast ślepo zwracać wyniki ProductsBLL
metody klasy GetProductsByCategoryID(categoryID)
, zamiast tego możemy najpierw uzyskać dostęp do wszystkich produktów (jeśli nie zostały one jeszcze pobrane), a następnie zwrócić tylko filtrowany widok produktów na podstawie przekazanego elementu 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
Zwróć uwagę na dodanie zmiennej na poziomie strony. allProducts
Zawiera on informacje o wszystkich produktach i jest wypełniany przy pierwszym GetProductsInCategory(categoryID)
wywołaniu metody. Po upewnieniu allProducts
się, że obiekt został utworzony i wypełniony, metoda filtruje wyniki tabeli Danych, tak aby były dostępne tylko te wiersze, których CategoryID
dopasowania CategoryID
są dostępne. Takie podejście zmniejsza liczbę prób uzyskania dostępu do bazy danych z N + 1 do dwóch.
To ulepszenie nie wprowadza żadnych zmian w renderowanej adiustacji strony ani nie przywraca mniejszej liczby rekordów niż inne podejście. Po prostu zmniejsza liczbę wywołań do bazy danych.
Uwaga
Można intuicyjnie z tego powodu, że zmniejszenie liczby dostępu do bazy danych z pewnością poprawi wydajność. Jednak może to nie być przypadek. Jeśli masz dużą liczbę produktów, których CategoryID
na przykład jest NULL
, wywołanie GetProducts
metody zwraca liczbę produktów, które nigdy nie są wyświetlane. Ponadto zwracanie wszystkich produktów może być marnotrawne, jeśli wyświetlasz tylko podzestaw kategorii, co może być przypadkiem, jeśli zaimplementowano stronicowanie.
Jak zawsze, jeśli chodzi o analizowanie wydajności dwóch technik, jedyną miarą surefire jest uruchamianie kontrolowanych testów dostosowanych do typowych scenariuszy przypadków aplikacji.
Podsumowanie
W tym samouczku pokazano, jak zagnieżdżać jedną kontrolkę sieci Web danych w innej, w szczególności sprawdzając sposób wyświetlania zewnętrznego repeatera elementu dla każdej kategorii z wewnętrznym powtarzaczem zawierającym listę produktów dla każdej kategorii na liście punktowanej. Głównym wyzwaniem w tworzeniu zagnieżdżonego interfejsu użytkownika jest dostęp i powiązanie prawidłowych danych z wewnętrzną kontrolą sieci Web danych. Dostępnych jest wiele technik, z których dwa zostały zbadane w tym samouczku. Pierwsze podejście zbadane używało obiektu ObjectDataSource w kontrolce sieci Web danych zewnętrznych powiązanych z wewnętrzną kontrolką sieci Web ItemTemplate
danych za pośrednictwem jej DataSourceID
właściwości. Druga technika uzyskiwała dostęp do danych za pośrednictwem metody w klasie ASP.NET s code-behind. Następnie można powiązać tę metodę z właściwością wewnętrznej kontrolki DataSource
sieci Web danych za pomocą składni łączenia danych.
Podczas gdy zagnieżdżony interfejs użytkownika zbadany w tym samouczku używał zagnieżdżonego modułu powtarzającego, te techniki można rozszerzyć na inne kontrolki sieci Web danych. Można zagnieżdżać repeater w elemencie GridView lub GridView w obrębie listy danych itd.
Szczęśliwe programowanie!
Informacje o autorze
Scott Mitchell, autor siedmiu książek ASP/ASP.NET i założyciel 4GuysFromRolla.com, współpracuje z technologiami internetowymi firmy Microsoft od 1998 roku. Scott pracuje jako niezależny konsultant, trener i pisarz. Jego najnowsza książka to Sams Teach Yourself ASP.NET 2.0 w ciągu 24 godzin. Można do niego dotrzeć pod adresem mitchell@4GuysFromRolla.com. Lub za pośrednictwem swojego bloga, który można znaleźć na stronie http://ScottOnWriting.NET.
Specjalne podziękowania
Ta seria samouczków została sprawdzona przez wielu pomocnych recenzentów. Recenzenci w tym samouczku to Zack Jones i Liz Shulok. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, upuść mi wiersz pod adresem mitchell@4GuysFromRolla.com.