Zagnieżdżone kontrolki internetowe danych (C#)

przez Scott Mitchell

Pobierz przykładową aplikację lub Pobierz plik PDF

W tym samouczku dowiesz się, jak używać elementu wzmacniak zagnieżdżonego w innym Wzmacniake. Przykłady pokazują, jak wypełnić wewnętrzny wzmacniak jednocześnie i programowo.

Wprowadzenie

Oprócz statycznej składni HTML i powiązania DataBinding szablony mogą również obejmować formanty sieci Web i kontrolki użytkownika. Te kontrolki sieci Web mogą mieć przypisane do nich właściwości za pośrednictwem instrukcji deklaracyjnej, wiązania z danymi lub można programistycznie uzyskiwać dostęp do odpowiednich programów obsługi zdarzeń po stronie serwera.

Osadzając kontrolki w szablonie, można dostosować i ulepszyć środowisko użytkownika. Na przykład w przypadku korzystania z używanie TemplateField w ramach samouczka kontrolki GridView dowiesz się, jak dostosować wyświetlanie widoku GridView, dodając kontrolkę Calendar w TemplateField, aby pokazać datę zatrudnienia pracownika. w obszarze Dodawanie kontrolek weryfikacji do edycji i wstawiania interfejsów i dostosowywania samouczków dotyczących interfejsu modyfikacji danych przedstawiono sposób dostosowywania edycji i wstawiania interfejsów przez dodanie formantów sprawdzania poprawności, pól tekstowych, kontrolek DropDownList i innych kontrolek sieci Web.

Szablony mogą również zawierać inne kontrolki sieci Web danych. Oznacza to, że możemy mieć element DataList, który zawiera inną wartość DataList (lub wzmacniak lub GridView lub DetailsView itd.) w ramach swoich szablonów. Wyzwanie z takim interfejsem wiąże odpowiednie dane z wewnętrznym formantem sieci Web danych. Istnieje kilka różnych metod, od opcji deklaratywnych używających elementu ObjectDataSource do programowania.

W tym samouczku dowiesz się, jak używać elementu wzmacniak zagnieżdżonego w innym Wzmacniake. Wzmacniak zewnętrzny będzie zawierać element dla każdej kategorii w bazie danych, wyświetlając nazwę i opis kategorii. Każdy element kategorii-wzmacniak wewnętrzny będzie wyświetlał informacje dla każdego produktu należącego do tej kategorii (patrz rysunek 1) na liście punktowanej. Nasze przykłady pokazują, jak wypełnić wewnętrzny wzmacniak jednocześnie i programowo.

każdej kategorii wraz z jej produktami są wymienione

Rysunek 1. na liście są wyświetlane wszystkie kategorie wraz z produktami (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Krok 1. Tworzenie listy kategorii

Podczas kompilowania strony korzystającej z zagnieżdżonych kontrolek sieci Web, łatwiej jest zaprojektować, utworzyć i przetestować zewnętrzną kontrolkę sieci Web, bez nawet martw się o wewnętrzną kontrolkę zagnieżdżoną. W związku z tym Zacznijmy od kroków niezbędnych do dodania do strony, która zawiera nazwę i opis każdej kategorii.

Aby rozpocząć, Otwórz stronę NestedControls.aspx w folderze DataListRepeaterBasics i Dodaj kontrolkę wzmacniak do strony, ustawiając jej Właściwość ID na CategoryList. Z tagu inteligentnego wzmacniaka wybierz, aby utworzyć nowy element ObjectDataSource o nazwie CategoriesDataSource.

Nazwa nowego elementu ObjectDataSource CategoriesDataSource

Rysunek 2. nazwa nowego CategoriesDataSource elementu ObjectDataSource (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Skonfiguruj element ObjectDataSource w taki sposób, aby pobierał swoje dane z metody GetCategories CategoriesBLL Class.

skonfigurować element ObjectDataSource do używania metody GetCategories klasy CategoriesBLL

Rysunek 3. Konfigurowanie elementu ObjectDataSource do używania metody GetCategories CategoriesBLL Class (kliknij,Aby wyświetlić obraz w pełnym rozmiarze)

Aby określić zawartość szablonu wzmacniaka, należy przejść do widoku źródła i ręcznie wprowadzić składnię deklaratywną. Dodaj ItemTemplate, który wyświetla nazwę kategorii w elemencie <h4> i opis kategorii s w elemencie Paragraph (<p>). Ponadto poinformuj każdą kategorię z regułą poziomą (<hr>). Po wprowadzeniu tych zmian strona powinna zawierać składnię deklaratywną dla elementu wzmacniak i ObjectDataSource, która jest podobna do następującej:

<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>

Na rysunku 4 przedstawiono postęp wyświetlania w przeglądarce.

każda nazwa i opis kategorii s są wyświetlane według reguły poziomej

Ilustracja 4. nazwa i opis kategorii s są wyświetlane według reguły poziomej (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Krok 2. Dodawanie zagnieżdżonego wzmacniaka produktu

Po zakończeniu tworzenia listy kategorii następnym zadaniem jest dodanie wzmacniak do CategoryList s ItemTemplate, który wyświetla informacje o tych produktach należących do odpowiedniej kategorii. Istnieje kilka sposobów, dla których możemy pobrać dane dla tego dwukierunkowego wzmacniaka, z którego korzystamy wkrótce. Na razie pozwól, aby po prostu utworzyć Wzmacniakę produktów w ramach ItemTemplate``CategoryList. W szczególności poinformuj, że każdy produkt na liście punktowanej zawiera wzmacniak z każdym elementem listy, w tym nazwę produktu i cenę.

Aby utworzyć ten wzmacniak, należy ręcznie wprowadzić wewnętrzną składnię i szablony deklaracyjnego wzmacniania w CategoryList s ItemTemplate. Dodaj następującą adiustację w ItemTemplate``CategoryList wzmacniak:

<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 specyficznych dla kategorii z ProductsByCategoryList

Jeśli w tym momencie odwiedzasz stronę za pomocą przeglądarki, Twój ekran będzie wyglądać tak samo jak na rysunku 4, ponieważ jeszcze tu powiążemy wszelkie dane z Wzmacniaką. Istnieje kilka sposobów, dzięki którym możemy pobrać odpowiednie rekordy produktu i powiązać je z Wzmacniaką, a bardziej wydajniej niż inne. Głównym wyzwaniem w tym miejscu jest przywrócenie odpowiednich produktów dla określonej kategorii.

Do danych, które mają być powiązane z wewnętrznym formantem wzmacniania, można uzyskać deklaratywnie dostęp za pośrednictwem elementu ObjectDataSource w ItemTemplate``CategoryListego, lub programowo, na stronie ASP.NET strony z kodem. Podobnie te dane mogą być powiązane z wewnętrznym Wzmacniakem, w sposób deklaratywny przez wewnętrzną właściwość "Wzmacniake" DataSourceID lub za pomocą składniowej funkcji DataBinding lub programowo poprzez odwołanie do wewnętrznego wzmacniak w obsłudze zdarzeń ItemDataBound wzmacniak CategoryList, programowe Ustawianie jego właściwości DataSource i wywoływanie jego metody DataBind(). Zapoznaj się z każdą z tych metod.

Uzyskiwanie dostępu do danych w sposób deklaratywny przy użyciu kontrolki ObjectDataSource i programu obsługi zdarzeńItemDataBound

Ponieważ niedawno użyto elementu ObjectDataSource w całości w tej serii samouczków, najbardziej naturalną opcją uzyskiwania dostępu do danych na potrzeby tego przykładu jest naklejenie z elementem ObjectDataSource. Klasa ProductsBLL ma metodę GetProductsByCategoryID(categoryID), która zwraca informacje o tych produktach, które należą do określonego categoryID . W związku z tym możemy dodać element ObjectDataSource do ItemTemplate CategoryList wzmacniak i skonfigurować go do uzyskiwania dostępu do danych z tej metody klasy s.

Niestety, wzmacniak nie zezwoli na edycję jego szablonów za pomocą widok Projekt, dlatego musimy ręcznie dodać składnię deklaratywną dla tej kontrolki ObjectDataSource. Poniższa składnia przedstawia CategoryList wzmacniak ItemTemplate po dodaniu nowego elementu 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 metody ObjectDataSource musimy ustawić właściwość ProductsByCategoryList wzmacniak DataSourceID do ID elementu ObjectDataSource (ProductsByCategoryDataSource). Należy również zauważyć, że nasz element ObjectDataSource ma <asp:Parameter> elementu, który określa categoryID wartość, która zostanie przeniesiona do metody GetProductsByCategoryID(categoryID). Ale jak określamy tę wartość? W idealnym przypadku można po prostu ustawić właściwość DefaultValue elementu <asp:Parameter> przy użyciu składni DataBinding, na przykład:

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

Niestety, składnia DataBinding jest prawidłowa tylko w kontrolkach, które mają zdarzenie DataBinding. Klasa Parameter nie ma takiego zdarzenia, w związku z czym powyższa składnia jest niedozwolona i spowoduje błąd w czasie wykonywania.

Aby ustawić tę wartość, musimy utworzyć procedurę obsługi zdarzeń dla zdarzenia ItemDataBound CategoryListowego. Odwołaj, że zdarzenie ItemDataBound wyzwalane jednokrotnie dla każdego elementu powiązanego z Wzmacniaką. W związku z tym, za każdym razem, gdy to zdarzenie jest wyzwalane przez wzmacniak zewnętrzny, możemy przypisać bieżącą wartość CategoryID do parametru ProductsByCategoryDataSource ObjectDataSource s CategoryID.

Utwórz procedurę obsługi zdarzeń dla zdarzenia CategoryList wzmacniak ItemDataBound za pomocą następującego kodu:

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();
    }
}

Ta procedura obsługi zdarzeń rozpoczyna się od zagwarantowania, że będziemy odprowadzić się do elementu danych, a nie nagłówka, stopki lub elementu separatora. Następnie odwołujemy się do rzeczywistego wystąpienia CategoriesRow, które zostało właśnie powiązane z bieżącym RepeaterItem. Na koniec odwołujemy się do elementu ObjectDataSource w ItemTemplate i przypisz jego wartość parametru CategoryID do CategoryID bieżącej RepeaterItem.

W przypadku tego programu obsługi zdarzeń ProductsByCategoryList wzmacniak w każdym RepeaterItem jest powiązany z tymi produktami w kategorii RepeaterItem s. Rysunek 5 pokazuje zrzut ekranu przedstawiający wynikowe dane wyjściowe.

zewnętrzny wzmacniak list każdej kategorii; Wewnętrzny element zawiera listę produktów dla tej kategorii

Ilustracja 5. wzmacniak zewnętrzny wymienia każdą kategorię; Wewnętrzny element zawiera listę produktów dla tej kategorii (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Programistyczne uzyskiwanie dostępu do produktów według kategorii

Zamiast używać elementu ObjectDataSource do pobrania produktów dla bieżącej kategorii, możemy utworzyć metodę w klasie ASP.NET strony powiązanej z kodem (lub w folderze App_Code lub w oddzielnym projekcie biblioteki klas), która zwraca odpowiedni zestaw produktów w przypadku przekazywania ich do CategoryID. Załóżmy, że mamy taką metodę w klasie z kodem związanym ze stroną ASP.NET i że została ona nazwana GetProductsInCategory(categoryID). Przy użyciu tej metody można powiązać produkty z bieżącą kategorią z wewnętrznym Wzmacniakem przy użyciu następującej składni deklaracyjnej:

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

Właściwość DataSource wzmacniak używa składni wiązania danych, aby wskazać, że jej dane pochodzą z metody GetProductsInCategory(categoryID). Ponieważ Eval("CategoryID") zwraca wartość typu Object, rzutowanie obiektu na Integer przed przekazaniem go do metody GetProductsInCategory(categoryID). Należy pamiętać, że CategoryID dostępnym w tym miejscu za pośrednictwem składni wiązania danych jest CategoryID w wzmacniake zewnętrznym (CategoryList), który jest powiązany z rekordami w tabeli Categories. W związku z tym wiemy, że CategoryID nie może być wartością NULL bazy danych, co oznacza, że możemy odrzucać metodę Eval bez sprawdzania, czy będziemy ponownie korzystać z DBNull.

W tym podejściu musimy utworzyć metodę GetProductsInCategory(categoryID) i pobrać odpowiedni zestaw produktów z uwzględnieniem podanej categoryID . Możemy to zrobić, po prostu zwracając ProductsDataTable zwrócone przez metodę ProductsBLL klasy s GetProductsByCategoryID(categoryID). Niech s utworzy metodę GetProductsInCategory(categoryID) w klasie powiązanej z kodem dla naszej strony NestedControls.aspx. Należy to zrobić przy użyciu następującego kodu:

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);
}

Ta metoda po prostu tworzy wystąpienie metody ProductsBLL i zwraca wyniki metody GetProductsByCategoryID(categoryID). Należy zauważyć, że metoda musi być oznaczona Public lub Protected; Jeśli metoda jest oznaczona Private, nie będzie dostępna ze znaczników deklaratywnych ASP.NET strony.

Po wprowadzeniu tych zmian w celu skorzystania z tej nowej techniki Poświęć chwilę na wyświetlenie strony za pomocą przeglądarki. Dane wyjściowe powinny być identyczne z danymi wyjściowymi przy użyciu metody obsługi zdarzeń ObjectDataSource i ItemDataBound (patrz z powrotem do rysunku 5, aby wyświetlić zrzut ekranu).

Note

Może się wydawać, że pracy utworzyć metodę GetProductsInCategory(categoryID) w klasie z kodem ASP.NET strony. Po wykonaniu tej metody po prostu tworzy wystąpienie klasy ProductsBLL i zwraca wyniki metody GetProductsByCategoryID(categoryID). Dlaczego nie tylko Wywołaj tę metodę bezpośrednio ze składni wiązania danych w Wzmacniake wewnętrznym, np. DataSource='<%# ProductsBLL.GetProductsByCategoryID((int)(Eval("CategoryID"))) %>'? Mimo że ta składnia nie będzie współdziałać z bieżącą implementacją klasy ProductsBLL (ponieważ metoda GetProductsByCategoryID(categoryID) jest metodą wystąpienia), można zmodyfikować ProductsBLL w celu uwzględnienia statycznej metody GetProductsByCategoryID(categoryID) lub dodać do klasy statyczną metodę Instance(), aby zwrócić nowe wystąpienie klasy ProductsBLL.

Chociaż takie modyfikacje eliminują potrzebę GetProductsInCategory(categoryID) metody w klasie z kodem związanym ze stroną ASP.NET, metoda klasy związanej z kodem zapewnia większą elastyczność podczas pracy z pobranymi danymi, ponieważ wkrótce zobaczymy.

Pobieranie wszystkich informacji o produkcie jednocześnie

Dwie metody poprzedniej zostały zbadane, aby pobrały te produkty dla bieżącej kategorii przez nadanie wywołania metody GetProductsByCategoryID(categoryID) klasy ProductsBLL (pierwsze podejście zostało wykonane przez element ObjectDataSource, drugi za pomocą metody GetProductsInCategory(categoryID) w klasie związanej z kodem). Za każdym razem, gdy ta metoda jest wywoływana, Warstwa logiki biznesowej wywołuje do warstwy dostępu do danych, która wysyła zapytanie do bazy danych za pomocą instrukcji SQL, która zwraca wiersze z tabeli Products, której pole CategoryID pasuje do podanego parametru wejściowego.

W systemie nie ma określonych kategorii n , tym podejście polega na tym, że połączenie n + 1 wywołuje do bazy danych jedno zapytanie bazy danych, aby uzyskać wszystkie kategorie, a następnie N wywołania, aby uzyskać odpowiednie produkty dla każdej kategorii. Firma Microsoft może jednak pobrać wszystkie potrzebne dane w zaledwie dwóch wywołaniach, aby pobrać wszystkie kategorie i inne, aby uzyskać wszystkie produkty. Gdy mamy wszystkie produkty, możemy odfiltrować te produkty, aby tylko produkty pasujące do bieżącego CategoryID były powiązane z tym Wzmacniakem wewnętrznym kategorii.

Aby zapewnić tę funkcjonalność, należy jedynie wprowadzić niewielką modyfikację metody GetProductsInCategory(categoryID) w naszej klasie z kodem ASP.NET Page s. Zamiast odwracania wyników metody GetProductsByCategoryID(categoryID) klasy ProductsBLL, można w pierwszej kolejności uzyskać dostęp do wszystkich produktów (jeśli nie były już dostępne), a następnie zwrócić tylko przefiltrowany widok produktów w oparciu o zatwierdzono CategoryID.

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;
}

Zwróć uwagę na dodanie zmiennej poziomu strony, allProducts. Zawiera informacje o wszystkich produktach i jest wypełniane podczas pierwszego wywołania metody GetProductsInCategory(categoryID). Po upewnieniu się, że obiekt allProducts został utworzony i wypełniony, Metoda filtruje wyniki tabeli DataTable, tak aby były dostępne tylko wiersze, których CategoryID odpowiadają określonym CategoryID. 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 do renderowanego oznakowania strony ani nie zmniejsza liczby rekordów niż inne podejście. Po prostu zmniejsza liczbę wywołań do bazy danych.

Note

Jeden z nich może mieć intuicyjny powód, aby zmniejszyć liczbę dostępu do bazy danych. Jednak taka sytuacja może nie być taka sama. Jeśli masz dużą liczbę produktów, których CategoryID jest NULL, na przykład wywołanie metody GetProducts zwraca liczbę produktów, które nigdy nie są wyświetlane. Ponadto zwracanie wszystkich produktów może być wasteful, jeśli zostanie wyświetlony tylko podzbiór kategorii, co może być przypadekem, jeśli wdrożono stronicowanie.

Tak jak zawsze, gdy chodzi o analizowanie wydajności dwóch technik, jedyną miarą Surefire jest uruchamianie kontrolowanych testów, które są dostosowane do typowych scenariuszy przypadków aplikacji.

Podsumowanie

W tym samouczku przedstawiono sposób zagnieżdżania jednej kontrolki sieci Web danych w innym, szczególnie badanie, w jaki sposób mamy zewnętrzny wzmacniak wyświetlić element dla każdej kategorii z wewnętrznym Wzmacniakem 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 uzyskiwanie dostępu do i powiązywanie prawidłowych danych w wewnętrznej kontrolce danych w sieci Web. Dostępne są różne techniki, z których każda została sprawdzona w tym samouczku. Pierwsze podejście zbadał użycie elementu ObjectDataSource w zewnętrznym formancie sieci Web ItemTemplate, który został powiązany z wewnętrzną kontrolką danych w sieci Web za pomocą właściwości DataSourceID. Druga technika uzyskała dostęp do danych za pośrednictwem metody w klasie z kodem ASP.NET Page s. Tę metodę można następnie powiązać z właściwością wewnętrznej formantu sieci Web danych DataSource za pomocą składni DataBinding.

Chociaż zagnieżdżony interfejs użytkownika, który został zbadany w tym samouczku, użył elementu wzmacniak zagnieżdżonego w obrębie wzmacniaka, techniki te można rozszerzyć na inne kontrolki sieci Web danych. Można zagnieżdżać wzmacniak w widoku GridView lub GridView w obrębie elementu DataList i tak dalej.

Szczęśliwe programowanie!

Informacje o autorze

Scott Mitchell, autor siedmiu grup ASP/ASP. NET Books i założyciel of 4GuysFromRolla.com, pracował z technologiami sieci Web firmy Microsoft od czasu 1998. Scott działa jako niezależny konsultant, trainer i składnik zapisywania. Jego Najnowsza książka to Sams ASP.NET 2,0 w ciągu 24 godzin. Można go osiągnąć w mitchell@4GuysFromRolla.com. lub za pośrednictwem swojego blogu, który można znaleźć w http://ScottOnWriting.NET.

Specjalne podziękowania

Ta seria samouczków została sprawdzona przez wielu przydatnych recenzentów. Recenzenci liderzy dla tego samouczka to Zack Kowalski i Liz Shulok. Chcesz przeglądać moje nadchodzące artykuły MSDN? Jeśli tak, upuść mi linię w mitchell@4GuysFromRolla.com.