Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.
W dzisiejszym artykule zostanie zaprezentowane wiązanie danych w ASP .NET.
Wprowadzenie do wiązania danych
Wiązanie danych w ASP .NET może być klasyfikowane w proste, złożone bądź hierarchiczne kontrolki. Proste kontrolki są to takie, które dziedziczą z ListControl. Złożone są klasami dziedziczącymi z CompositeDataBoundControl, (takie jak GridView, DetailsView, FormsView itp). Hierarchiczne to kontrolki Menu oraz TreeView.
.NET Framework dostarcza kilka klas bazowych, które są stosowane do dostarczania wspólnych właściwości oraz zachować do konkretnych kontrolek. W ten sposób wiązanie danych wszędzie wygląda podobnie.
Poniżej rysunek przedstawiający hierarchię klas bazowych, wykorzystywanych do wiązania danych:
BaseDataBoundControl jest pierwszą kontrolką w hierarchii, która dziedziczy po WebControl. Zawiera właściwości DataSource oraz DataSourceId potrzebne podczas wiązania danych. Właściwość DataSoruce pobiera bądź ustawia obiekt,z którego kontrolka data-bound pobiera dane.
Następną kontrolką jest HierarchicalDataBoundControl, dziedzicząca z BaseDataBoundControl. Jest rodzicem dla kontrolek takich jak Menu czy ListView.
Proste kontrolki Data-Bound
Istnieje wiele kontrolek w ASP .NET zawierających podstawowe, oparte na listach wiązanie danych. Kontrolki te nie są przeznaczone do pracy z danymi, w sensie ich edytowania itp. Dostarczają za to listy danych, z którymi możemy pracować. Poniżej rysunek hierarchii klasy ListControl:

Klasa ListControll jest abstrakcyjną klasą bazową, która dostarcza wspólną funkcjonalność dla klas dziedziczących po niej. Zawiera kolekcję Items, która jest kolekcją obiektów ListItem. Każdy ListItem zawiera właściwość Text, która jest wyświetlana użytkownikowi oraz Value, która jest wysyłana przy post backu do serwera.
Możemy dodawać elementy do ListItems w kodzie lub deklaratywnie w znacznikach. Możemy również wiązać dane ustawiając właściwość DataSource bądź DataMember jeśli źródło danych posiada więcej niż jedną tabele. Poniższy przykład pokazuje jak zbindować dane z kontrolką ListBox:
1: <asp:ListBox
2: ID="ListBox1"
3: runat="server"
4: //podajemy Id źródła danych
5: DataSourceID="SqlDataSource1"
6: //tu jaki ma być tekst na wyświetlanym polu
7: DataTextField="CompanyName"
8: //tu wartość
9: DataValueField="ShipperID">
10: </asp:ListBox>
Kontrolka DropDownList
Kontrolka DropDownList służy do wyświetlania listy, z których użytkownik może wybrać sobie jakąś wartość. Aby określić jaki przedmiot z listy wybrał użytkownik możemy użyć właściwości SelecValue, SelectedItem bądź SelectedIndex.
Poniższy przykład pokazuje jak zbindować DropDownList z SqlDataSource, który zwraca tabelę Territorities z bazy danych Northwind.
1: <asp:DropDownList runat="server" Width="250px"
2: ID="DropDownList1"
3: DataSourceID="SqlDataSource1"
4: DataTextField="TerritoryDescription"
5: DataValueField="TerritoryID" >
6: </asp:DropDownList>
Widzimy, że jako tekst będzie wyświetlany opis Territority a jako wartość ID.
Aby przechwycić wartość wybraną przez użytkownika możemy napisać tak:
1: //przypisanie wybranej wartośći do labela
2: Label1.Text = "You selected TerritoryID: " + DropDownList1.SelectedValue;
Kontrolka ListBox
Dzięki kontrolka ListBox pozwala użytkownikom widzieć więcej danych w jednym czasie niż DropDownList. Może być również skonfigurowana tak, aby można było wybrać z listy więcej niż jeden element. Aby to zmienić należy ustawić właściwość SelectionMode. Kontrolka ta posiada również właściwość Rows, której używamy do określenia ilości wyświetlanych elementów w danym momencie. Poniższy przykład pokazuje jak ustawić wielo wybór oraz pokazać 13 wierszy w ListBox. Podczepione jest również DataSource tak jak w poprzednich przykładach:
1: <asp:ListBox runat="server" Height="225px" Width="275px"
2: ID="ListBox1"
3: Rows="13"
4: DataSourceID="SqlDataSource1"
5: DataTextField="TerritoryDescription"
6: DataValueField="TerritoryID"
7: SelectionMode="Multiple">
8: </asp:ListBox>
Aby teraz wyświetlić zaznaczone elementy, musimy przejechać po kolekcji itemów np tak:
1: foreach (ListItem i in ListBox1.Items)
2: {
3: if(i.Selected)
4: Label1.Text = Label1.Text + "You selected TerritoryID: " + i.Value + "<br />";
5: }
W training kicie wygląda to tak:

Kontrolki CheckBoxList oraz RadioButtonList
Tytułowe kontrolki są bardzo podobne. Sądze, że każdy wie do czego służą :) Możemy również powiązać je z DataSource. Wygląda to niemal identycznie jak na poprzednich przykładach. Jedyną nowością jest właściwość RepeatColumns, w której określamy liczbę kolumn wyświetlanych poziomo.
1: <asp:CheckBoxList runat="server"
2: ID="CheckBoxList1"
3: DataSourceID="SqlDataSource1"
4: DataTextField="TerritoryDescription"
5: DataValueField="TerritoryID" RepeatColumns="5">
6: </asp:CheckBoxList>
Kontrolki Composite Data-Bound
Kontrolki Composite Data-Bound dziedziczą po klasie bazowej CompositeDataBoundControl. Klasa ta implementuje interfejs INamingContainer. Poniżej hierarchia:
Kontrolka GridView
GridView, jak wiemy, służy do wyświetlania danych w postaci tabelki, która jest renderowana w kodzie html.
Składa się on (gridview) z kolekcji obiektów GridBiewRow oraz DataControlField. Obiekty GridViewRow dziedziczą z TableRow, które zawierają właściwość Cell. Ta właściwośc jest kolekcją obiektów DataControlFieldCell. Poniżej rysunek przedstawiający budowę grida:

Poniżej hierarchia kontrolki DataControlField. Klasy pochodne są wykorzystywane do tworzenia DataControlFieldCell z właściwą treścią.

Training kit poświęcił tu dość sporo miejsca na opisywanie tego, jak zmienić wygląd GridView’a ale myślę że o tym możecie doczytać sobie gdzieś przy okazji bo jest tu mowa również o stylach itp :)
Warto natomiast wspomnieć, że aby zbindować dane z GridView należy również uzupełnić właściwość DataSourceID i wpisać tam id naszego datasource.
Kontrolka DetailsView
Kontrolka DetailsView pozwala na wyświetlenie szczegółowych informacji dotyczących pojedyńczego rekordu. Pozwala również na edytowanie, kasowanie i dodawanie rekordu.
Jako przykład po raz kolejny kontrolka z podanym DataSourceId. Nowością jest właściwość AllowPaging. Jeśli ustawiona jest na true, pozwala kontrolce na samodzielne poruszanie się po źródle danych.
1: <asp:DetailsView runat="server" Width="300px"
2: ID="DetailsView1"
3: AllowPaging="True"
4: AutoGenerateRows="False"
5: DataKeyNames="ProductID"
6: DataSourceID="SqlDataSource1">
7: <Fields>
8: <asp:BoundField DataField="ProductID" HeaderText="ProductID"
9: InsertVisible="False" ReadOnly="True" SortExpression="ProductID" />
10: <asp:BoundField DataField="ProductName" HeaderText="ProductName"
11: SortExpression="ProductName" />
12: <asp:BoundField DataField="SupplierID" HeaderText="SupplierID"
13: SortExpression="SupplierID" />
14: <asp:BoundField DataField="CategoryID" HeaderText="CategoryID"
15: SortExpression="CategoryID" />
16: <asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit"
17: SortExpression="QuantityPerUnit" />
18: <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice"
19: SortExpression="UnitPrice" />
20: <asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock"
21: SortExpression="UnitsInStock" />
22: <asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder"
23: SortExpression="UnitsOnOrder" />
24: <asp:BoundField DataField="ReorderLevel" HeaderText="ReorderLevel"
25: SortExpression="ReorderLevel" />
26: <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
27: SortExpression="Discontinued" />
28: <asp:CommandField ShowDeleteButton="True" ShowEditButton="True"
29: ShowInsertButton="True" />
30: </Fields>
31: </asp:DetailsView>
Po uruchomieniu wygląda to tak:
Kontrolki Hierarchical Data-Bound
Kontrolka HierarchicaldataBoundControl jest klasą bazową dla kontrolek renderujących dane hierarchicznie.

Kontrolka TreeView
TreeView wyświetla dane w postaci hierarchicznej tak jak lista plików i folderów. Kontrolka może współpracować również z SiteMapą, jeśli uzupełniona jest właściwość DataSourceID.
Typowa struktura TreeView ma tylko jeden główny węzeł ale nic nie stoi na przeszkodzie, aby dodać więcej takich węzłów :)
TreeView może być wypełniony przy użyciu danych statycznych bądź możemy bindować go z danymi.
Aby wypełnić kontrolkę danymi statycznymi należy umieścić je w sposób deklaratywny w znaczniku <Node>, w elemencie TreeView, a następnie zagnieździć w nim elementy <asp:TreeNode>. Każdy taki element ma właściwości, które można ustawić poprzez dodanie atrybutów.
Aby wypełnić dane poprzez wiązanie, należy użyć jakiegoś źródła danych, które implementuje interfejs IHierarchicalDataSource. Takim źródłem są np. XmlDataSource bądź SiteMapDataSource. Teraz wystarczy ustawić DataSourceID na taki jaki mamy podany w naszym źródłem i już :)
Załóżmy, że chcemy skorzystać z kontrolki do wyświetlenia danych klientów z pliku o nazwie Customers.xml, który zawiera liste klientów, zamówień i faktur a także pozycję każdego zamówienia. Dane te są przechowywane w formacie xml. Plik Customers.xml wygląda tak:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <Customers>
3: <Customer CustomerId="1" Name="Northwind Traders">
4: <Orders>
5: <Order OrderId="1" ShipDate="06-22-2006">
6: <OrderItems>
7: <OrderItem OrderItemId="1" PartNumber="123"
8: PartDescription="Large Widget" Quantity="5"
9: Price="22.00" />
10: <OrderItem OrderItemId="2" PartNumber="234"
11: PartDescription="Medium Widget" Quantity="2"
12: Price="12.50" />
13: </OrderItems>
14: </Order>
15: <Order OrderId="2" ShipDate="06-25-2006">
16: <OrderItems>
17: <OrderItem OrderItemId="5" PartNumber="432"
18: PartDescription="Small Widget" Quantity="30"
19: Price="8.99" />
20: <OrderItem OrderItemId="4" PartNumber="234"
21: PartDescription="Medium Widget" Quantity="2"
22: Price="12.50" />
23: </OrderItems>
24: </Order>
25: </Orders>
26: <Invoices>
27: <Invoice InvoiceId="6" Amount="99.37" />
28: <Invoice InvoiceId="7" Amount="147.50" />
29: </Invoices>
30: </Customer>
31: <Customer CustomerId="2" Name="Tailspin Toys">
32: <Orders>
33: <Order OrderId="8" ShipDate="07-11-2006">
34: <OrderItems>
35: <OrderItem OrderItemId="9" PartNumber="987"
36: PartDescription="Combo Widget" Quantity="2"
37: Price="87.25" />
38: <OrderItem OrderItemId="10" PartNumber="654"
39: PartDescription="Ugly Widget" Quantity="1"
40: Price="2.00" />
41: </OrderItems>
42: </Order>
43: <Order OrderId="11" ShipDate="08-21-2006">
44: <OrderItems>
45: <OrderItem OrderItemId="12" PartNumber="999"
46: PartDescription="Pretty Widget" Quantity="50"
47: Price="78.99" />
48: <OrderItem OrderItemId="14" PartNumber="575"
49: PartDescription="Tiny Widget" Quantity="100"
50: Price="1.20" />
51: </OrderItems>
52: </Order>
53: </Orders>
54: <Invoices>
55: <Invoice InvoiceId="26" Amount="46.58" />
56: <Invoice InvoiceId="27" Amount="279.15" />
57: </Invoices>
58: </Customer>
59: </Customers>
XmlDataSource oraz TreeView są dodane do strony oraz skonfigurowane. Poniżej kod kontrolki TreeView:
1: <asp:TreeView ID="TreeView1" runat="server"
2: DataSourceID="XmlDataSource1"
3: ShowLines="True" ExpandDepth="0">
4: <DataBindings>
5: <asp:TreeNodeBinding DataMember="Customer"
6: TextField="Name" ValueField="CustomerId" />
7: <asp:TreeNodeBinding DataMember="Order"
8: TextField="ShipDate" ValueField="OrderId" />
9: <asp:TreeNodeBinding DataMember="OrderItem"
10: TextField="PartDescription" ValueField="OrderItemId" />
11: <asp:TreeNodeBinding DataMember="Invoice"
12: TextField="Amount" ValueField="InvoiceId"
13: FormatString="{0:C}" />
14: </DataBindings>
15: </asp:TreeView>
Poniżej kod dodany w code-behind, który w prosty sposób wyświetla wybrany węzeł:
1: public partial class TreeView_Control : System.Web.UI.Page
2: {
3: protected void TreeView1_SelectedNodeChanged(object sender, EventArgs e)
4: {
5: Response.Write("Value:" + TreeView1.SelectedNode.Value);
6: }
7: }
Wszystko wygląda tak:

Kontrolka Menu
Kontrolka Menu najczęściej używana jest w połączeniu z SiteMapDataSource, dzięki której tworzymy nawigację po strnie.
Kontrolka ta podobna jest do poprzedniej. Również możemy uzupełniać ją statycznie oraz dynamicznie. Również aby uzupełniać ją dynamicznie musimy użyć jakiegoś źródła danych, które implementuje interfejs IHierarchicalDataSource.
Poniżej podobny przykład jak w poprzedniej kontrolce. Załóżmy, że chcemy skorzystać z kontrolki Menu, aby wyświetlic menu z danymi z pliku o nazwie MenuItems.xml, który zawiera listę rzeczy do wyświetlenia w menu. Tak wygląda MenuItems.xml:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <MenuItems>
3: <Home display="Home" url="~/" />
4: <Products display="Products" url="~/products/">
5: <SmallWidgets display="Small Widgets"
6: url="~/products/smallwidgets.aspx" />
7: <MediumWidgets display="Medium Widgets"
8: url="~/products/mediumwidgets.aspx" />
9: <BigWidgets display="Big Widgets"
10: url="~/products/bigwidgets.aspx" />
11: </Products>
12: <Support display="Support" url="~/Support/">
13: <Downloads display="Downloads"
14: url="~/support/downloads.aspx" />
15: <FAQs display="FAQs"
16: url="~/support/faqs.aspx" />
17: </Support>
18: <AboutUs display="About Us" url="~/aboutus/">
19: <Company display="Company"
20: url="~/aboutus/company.aspx" />
21: <Locations display="Location"
22: url="~/aboutus/locations.aspx" />
23: </AboutUs>
24: </MenuItems>
Sama kontrolka menu dodana do strony wygląda tak:
1: <asp:Menu runat="server"
2: ID="Menu1"
3: DataSourceID="XmlDataSource1"
4: OnMenuItemClick="Menu1_MenuItemClick">
5: </asp:Menu>
Widzimy powiązanie z DataSource oraz określone zostało zdarzenie OnMenuItemClick, które wskazuje na metodę Menu1_MenuItemClick.
Jeszcze na koniec kod, który wyświetli właściwość ValuePath wybranego elementu z MenuItem. Kod umieszczamy w Code-behind:
1: public partial class Menu_Control : System.Web.UI.Page
2: {
3: protected void Menu1_MenuItemClick(object sender, MenuEventArgs e)
4: {
5: Label1.Text = e.Item.ValuePath;
6: }
7: }
No i obrazek :)

To tyle na dzisiaj. Przyznam szczerze, że ten rozdział w TK jakiś dziwny, bardzo niekonkretny.
Tymczasem zapraszam na kolejny artykuł z naszej serii, który pojawi się, jak zwykle, w piątek.