Hierarchische Datenbindung in ASP.NET
Fritz Onion
Entwicklungsmentor
Gilt für:
Microsoft® ASP.NET
Zusammenfassung: Erfahren Sie mehr über Techniken zum Ausführen ASP.NET Datenbindung an Datenquellen, die mehr als zwei Dimensionen aufweisen und hierarchisch sind. (27 gedruckte Seiten)
Laden Sie HierarchicalDataBindingSample.msiherunter.
Inhalte
Einführung
Datenbindung
Hierarchische Daten
Bindung an hierarchische Datenbankdaten
Bindung an XML-Daten
Zugreifen auf geschachtelte Steuerelemente
Hierarchische DataGrid- und DataList-Bindung
Einschränkungen und Effizienz
Zusammenfassung
Einführung
ASP.NET bietet eine praktische Architektur zum Binden von Daten an serverseitige Steuerelemente, die von den Steuerelementen dann auf dem Client in jedem Format gerendert werden, für das das Steuerelement angezeigt werden soll. Die meisten Beispiele für Datenbindung in ASP.NET sind Beispiele für die Bindung an flache Datenquellen, die das Ergebnis von Abfragen an eine Datenbank sind. Während diese Art der Datenbindung in vielen Anwendungen am häufigsten vorkommt, passen die Daten nicht in einen einfachen zweidimensionalen Raum, und Standarddatenbindungstechniken sind zu kurz.
In diesem Artikel werden Techniken zum Durchführen der Datenbindung an Datenquellen behandelt, die mehr als zwei Dimensionen aufweisen und hierarchischer Natur sind.
Datenbindung
Die Datenbindung in ASP.NET ist der Prozess der Bindung von Daten auf dem Server an ein serverseitiges Steuerelement, das diese Daten dann in irgendeiner Form auf dem Client rendert. Die einzigen Einschränkungen für die Datenbindung bestehen darin, dass das serverseitige Steuerelement eine Eigenschaft namensDataSource
und eine Methode mit dem Namen DataBind()
unterstützen muss und dass die Datenquelle, an die das Steuerelement gebunden ist, dieIEnumerable
Schnittstelle implementiert.
Hinweis Es gibt zwei bemerkenswerte Ausnahmen für diese Anforderung: Die
DataSet
,DataTable
kann beide direkt gebunden werden, was zu einer Bindung an den StandardstandardDataView
der Standardtabelle führt (DataView
implementiert).IEnumerable
Dies ist der Einfachheit halber, daDataSets
undDataTable
s häufig als Datenquelle in der Datenbindung verwendet werden.
Um Daten an ein Steuerelement zu binden, weisen Sie die Datenquelle der DataSource
Eigenschaft des Steuerelements zu und rufen dessenDataBind()
Methode auf.
Betrachten Sie beispielsweise die folgende Datenquelle, die eineArrayList
Fülle von Instanzen derItem
-Klasse zurückgibt:
public class Item { private string _name; public Item(string name) { _name = name; } public string Name { get { return _name; } } } public class TestDataSource { public static ArrayList GetData() { ArrayList items = new ArrayList(); for (int i=0; i<10; i++) { Item item = new Item("item" + i.ToString()); items.Add(item); } return items; } }
Da implementiertArrayList
IEnumerable
wird, ist das Ergebnis unsererGetData()
Methode in der -Klasse eine gültige Datenquelle für dieTestDataSource
Bindung. Wir verwenden dasRepeater
als serverseitiges Steuerelement, an das wir die Daten binden, was erfordert, dass wir eineItemTemplate
Beschreibung bereitstellen, wie die einzelnen Elemente in der aufzählbaren Datenquelle gerendert werden sollen. In unserem Beispiel wird einCheckBox
Steuerelement gerendert, dessen Text auf dieName
Eigenschaft der Item
Klasse instance festgelegt ist, an die es gebunden ist:
<asp:Repeater Runat="server" ID="_itemsRepeater" EnableViewState="false"> <ItemTemplate> <asp:CheckBox Runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' /> <br/> </ItemTemplate> </asp:Repeater>
Es bleibt nur noch, die tatsächliche Bindung der Daten an unsere Repeater
durchzuführen, was in derLoad
Regel im Handler der Page
abgeleiteten Klasse erfolgt, wie hier gezeigt:
private void Page_Load(object sender, EventArgs e) { _itemsRepeater.DataSource = TestDataSource.GetData(); _itemsRepeater.DataBind(); }
Ein Beispielrendering dieser Seite ist unten dargestellt:
Abbildung 1. Seite "Datenbindung"
Hierarchische Daten
Unser erstes Datenquellenbeispiel war flach mit nur einer Datenebene. Angenommen, wir fügen jedem Element in unserer Datenquelle wie folgt eine Auflistung von Unterelementen hinzu:
public class Item { string _name; ArrayList _subItems = new ArrayList(); public Item(string name) { _name = name; } public string Name { get { return _name; } } public ArrayList SubItems { get { return _subItems; } } }
In unserer Methode GetData
zum Auffüllen künstlicher Datenquellen fügen wir jedem Element 5 Unterelemente hinzu, wie unten gezeigt:
public class TestDataSource { public static ArrayList GetData() { ArrayList items = new ArrayList(); for (int i=0; i<10; i++) { Item item = new Item("item" + i.ToString()); for (int j=0; j<5; j++) { Item subItem = new Item("subitem" + j.ToString()); item.SubItems.Add(subItem); } items.Add(item); } return items; } }
Unsere Datenstruktur ist jetzt hierarchisch mit einer Datenebene unterhalb der Auflistung von Elementen auf oberster Ebene. Die zuvor durchgeführte Datenbindung funktioniert weiterhin ordnungsgemäß, zeigt jedoch nur die erste Datenebene an, wobei die Unterelemente ignoriert werden, wenn sie gerendert werden. Was wir an diesem Punkt benötigen, um alle Daten ordnungsgemäß anzuzeigen, ist eine geschachtelte Datenbindung für die Unterelemente jedes Elements auszuführen. Logisch bedeutet dies, dass wir ein weiteres datengebundenes Steuerelement innerhalb desItemTemplate
von dem platzieren müssen, dasRepeater
wir bereits haben, und es an dieSubItems
Sammlung der einzelnenItem
Elemente binden müssen, die von der obersten Ebene Repeater
aufgezählt werden. Dies kann deklarativ in unserer ASPX-Datei erreicht werden, indem sie ein geschachteltes Repeater
hinzufügen. Der einzige knifflige Teil besteht darin, dieSubItems
Auflistung derItem
aktuell an die DataSource
Eigenschaft des geschachtelten Repeater
gebundenen zuzuordnen. Dies geschieht durch deklaratives Festlegen derDataSource
Eigenschaft des geschachteltenRepeater
auf einen Datenbindungsausdruck, was zur SubItems
Auflistung führt, wie unten gezeigt:
<asp:Repeater Runat="server" ID="_itemsRepeater" EnableViewState="false"> <ItemTemplate> <asp:CheckBox Runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' /> <asp:Repeater Runat="server" ID="_subitemsRepeater" EnableViewState="false" DataSource= '<%# DataBinder.Eval(Container.DataItem, "SubItems") %>'> <ItemTemplate> <br/> <asp:CheckBox Runat="server" Text= '<%# DataBinder.Eval(Container.DataItem, "Name") %>' /> </ItemTemplate> </asp:Repeater> <br/> </ItemTemplate> </asp:Repeater>
An unserer CodeBehind-Klasse muss sich nichts ändern, da wir unsere Datenquelle bereits an die oberste Ebene Repeater
binden. Die geschachtelte Datenbindung erfolgt einmal pro EinmalItem
in der Auflistung der obersten Ebene. Beachten Sie beim Lesen eines Paares geschachtelter datengebundener Steuerelemente wie dieses, dass die Datenbindungsausdrücke (<%# %>
) durch ihr nächstes Steuerelement begrenzt werden. In unserem Beispiel sind die ersten beiden Datenbindungsausdrücke auf die äußere Datenbindung der obersten EbeneRepeater
ausgerichtet und werden in das aktuelle Element in der Auflistung der obersten Ebene aufgelöst. Der dritte Datenbindungsausdruck ist auf den inneren Repeater
Bereich begrenzt und löst in ein Element in derSubItems
Auflistung des aktuellItem
gebundenen auf. Das Rendern dieser Seite ist unten dargestellt:
Abbildung 2.Rendering einer Seite mit Datenbindung an ein Repeaterbeispiel
Beachten Sie, dass diese geschachtelte Datenbindung nicht nur auf eine Ebene beschränkt ist, sondern beliebig tief erweitert werden kann. Solange die Schachtelung der datengebundenen Steuerelemente mit der Schachtelung der Auflistungen in der Datenquelle übereinstimmt und die Datenquelle regelmäßig in Form ist, funktioniert die Bindung. Als Beispiel erweitern wir unsere Datenquelle, um eine andere Datenebene einzuschließen, sodass jede Item
in den vorhandenen SubItems
Sammlungen ihre eigene Sammlung von SubItems
erhält.
public class TestDataSource { public static ArrayList GetData() { ArrayList items = new ArrayList(); for (int i=0; i<10; i++) { Item item = new Item("item" + i.ToString()); for (int j=0; j<5; j++) { Item subItem = new Item("subitem" + j.ToString()); item.SubItems.Add(subItem); for (int k=0; k<4; k++) { Item subsubItem = new Item("subsubitem" + k.ToString()); subItem.SubItems.Add(subsubItem); } } items.Add(item); } return items; } }
Die einzige Änderung auf unserer Seite, die erforderlich ist, um diese neuen geschachtelten Daten anzuzeigen, besteht darin, einen weiteren geschachtelten Repeater
Wert hinzuzufügen, dessenDataSource
Eigenschaft an dieSubItems
Eigenschaft des Elements gebunden ist, das derzeit von der zweiten Ebene Repeater
aufgelistet wird.
<asp:Repeater Runat="server" ID="_itemsRepeater" EnableViewState="false"> <ItemTemplate> <asp:CheckBox Runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' /> <asp:Repeater Runat="server" ID="_subitemsRepeater" EnableViewState="false" DataSource= '<%# DataBinder.Eval(Container.DataItem, "SubItems") %>' > <ItemTemplate> <br/> <asp:CheckBox Runat="server" Text= '<%# DataBinder.Eval(Container.DataItem, "Name") %>'/> <asp:Repeater Runat="server" EnableViewState="false" DataSource= '<%# DataBinder.Eval(Container.DataItem, "SubItems") %>'> <ItemTemplate> <br/> <asp:CheckBox Runat="server" Text= '<%# DataBinder.Eval(Container.DataItem, "Name") %>' /> </ItemTemplate> </asp:Repeater> </ItemTemplate> </asp:Repeater> <br /> </ItemTemplate> </asp:Repeater>
Im Folgenden wird ein teilweises Rendering dieser Seite gezeigt:
Abbildung 3. Seite mit zusätzlichem geschachtelten Repeater
Bindung an hierarchische Datenbankdaten
Nachdem wir nun über die Grundlagen der hierarchischen Datenbindung verfügen, ist es an der Zeit, eine praktischere Anwendung zu erkunden. Da die meisten Datenbindungen die Bindung an die Ergebnisse einer Datenbankabfrage umfassen, verwenden wir als Nächstes hierarchische Daten, die aus einer Datenbank abgerufen werden. Hierarchische Daten werden in der Regel in relationalen Datenbanken gespeichert, indem 1:n-Beziehungen zwischen Tabellen verwendet werden. Beispielsweise besteht in der Northwind-Beispieldatenbank, die in Standardinstallationen SQL Server und Microsoft Access verfügbar ist, eine 1:n-Beziehung zwischen derCustomers
Tabelle und derOrders
Tabelle. Ebenso besteht eine 1:n-Beziehung zwischen derOrders
Tabelle und derOrder Details
Tabelle. Diese Beziehungen sind in der folgenden Abbildung dargestellt:
Abbildung 4. Tabellenbeziehungen
Es gibt mehrere Möglichkeiten, diese Daten abzufragen, aber für unsere Zwecke besteht die einfachste Möglichkeit, die Anzahl von Roundtrips zur Datenbank auf eins zu reduzieren, darin, den Inhalt aller drei Tabellen in eine DataSet
zu ziehen und die Möglichkeit zu nutzen, Beziehungen innerhalb eines DataSet
zu definieren, um die Daten hierarchisch zu extrahieren. Der folgendeLoad
Handler führt dies aus und bindet dann den resultierendenDataSet
an einen Repeater mit der ID von _customerRepeater
.
private void Page_Load(object sender, EventArgs e) { string strConn = "server=.;trusted_connection=yes;database=northwind"; string strSql = "SELECT CustomerID, CompanyName FROM " + " Customers; " + "SELECT OrderID, CustomerID, " + " EmployeeID FROM Orders;" + "SELECT OrderID, Products.ProductID," + "ProductName, Products.UnitPrice FROM" + " [Order Details], Products WHERE " + " [Order Details].ProductID = " + "Products.ProductID"; SqlConnection conn = new SqlConnection(strConn); SqlDataAdapter da = new SqlDataAdapter(strSql, conn); da.TableMappings.Add("Customers1", "Orders"); da.TableMappings.Add("Customers2", "OrderDetails"); _ds = new DataSet(); da.Fill(_ds, "Customers"); _ds.Relations.Add("Customer_Order", _ds.Tables["Customers"].Columns["CustomerID"], _ds.Tables["Orders"].Columns["CustomerID"]); _ds.Relations[0].Nested = true; _ds.Relations.Add("Order_OrderDetail", _ds.Tables["Orders"].Columns["OrderID"], _ds.Tables["OrderDetails"].Columns["OrderID"]); _ds.Relations[1].Nested = true; _customerRepeater.DataSource = _ds.Tables["Customers"]; _customerRepeater.DataBind(); }
Sobald die Daten in den DataSet
geladen wurden, können sie nun hierarchisch mithilfe der von uns eingerichteten Beziehungen navigiert werden. Bei einer beliebigen Zeile in derCustomers
Tabelle in unserer DataSet
können wir beispielsweise mit der Zeichenfolge "Customer_Order
" aufrufenGetChildRows()
, um eine Auflistung von Zeilen aus der Tabelle abzurufen, die Orders
diesem Kunden zugeordnet ist. Ebenso können wir alle Einträge finden, dieOrder Detail
einer bestimmten Reihenfolge zugeordnet sind, indem wir mit der Zeichenfolge "Order_OrderDetail
" für eine Zeile aus der Tabelle aufrufenGetChildRows
, um alleOrder Detail
einträge abzurufen, dieOrders
dieser Reihenfolge zugeordnet sind. Noch nützlicher für unsere Zwecke ist dieCreateChildView
Methode derDataRowView
-Klasse, die eineDataView
zurückgibt, die alle Zeilen für eine bestimmte Beziehung anzeigt.
Nachdem wir nun die Daten für die Bindung vorbereitet haben, müssen wir datengebundene Steuerelemente mit entsprechenden Schachtelungsebenen hinzufügen, um die Daten zu rendern. Ähnlich wie in unserem vorherigen Beispiel mit einer benutzerdefinierten Datenstruktur verfügen auch die Daten, an die wir hier binden, über zwei Tiefenebenen, was bedeutet, dass wir zwei geschachtelte Steuerelemente benötigen, um jede Unterebene der Daten zu rendern. Genauer gesagt benötigen wir eine oberste EbeneRepeater
, um an dieCustomers
Tabelle in unserer DataSet
zu binden, eine geschachtelteRepeater
, um an alleOrders
den einzelnen Kunden zugeordneten zu binden, und eine andere geschachteltRepeater
, um an alle Einträge zu binden, dieOrder Detail
jeder Bestellung zugeordnet sind. DasDataSource
für die beiden geschachteltenRepeaters
ist das Ergebnis des AufrufensCreateChildView
der übergeordneten Zeile mit dem entsprechenden Beziehungsnamen. Anstatt zu versuchen, denDataView
in einem einzelnen Ausdruck in derRepeater
Deklaration zu erstellen, ist es praktisch, eine Funktion in unserer Code Behind-Klasse zu definieren, die die übergeordnete Zeile und den Namen der Beziehung übernimmt und zurückgibt DataView
.
protected DataView GetChildRelation(object dataItem, string relation) { DataRowView drv = dataItem as DataRowView; if (drv != null) return drv.CreateChildView(relation); else return null; }
Mit dieser Funktion und unserer Datenquelle können wir jetzt dieRepeater
Steuerelementdeklarationen in unserer ASPX-Datei schreiben und ein sehr einfaches Layout der Daten mithilfe von Umbrüchen und Leerzeichen darstellen:
<asp:Repeater Runat="server" ID="_customerRepeater" EnableViewState="false"> <ItemTemplate> Customer: <%# DataBinder.Eval(Container.DataItem, "CustomerID") %> <%# DataBinder.Eval(Container.DataItem,"CompanyName") %> <br /> <asp:Repeater runat="server" EnableViewState="false" DataSource= '<%# GetChildRelation(Container.DataItem, "Customer_Order")%>' > <itemTemplate> Orderid:<b> <%#DataBinder.Eval(Container.DataItem, "OrderID")%> </b><br/> <asp:Repeater runat="server" EnableViewState="false" DataSource= '<%# GetChildRelation(Container.DataItem, "Order_OrderDetail")%>' > <itemTemplate> <b><%# DataBinder.Eval(Container.DataItem, "ProductName") %></b> $<%# DataBinder.Eval(Container.DataItem, "UnitPrice") %> <br/> </itemTemplate> </asp:Repeater> </itemTemplate> </asp:Repeater> </ItemTemplate> </asp:Repeater>
Bindung an XML-Daten
Jede Diskussion über hierarchische Daten wäre ohne eine Diskussion über XML unvollständig, da dies das vorherrschende Format für hierarchische Daten in den meisten Systemen ist. Es gibt einige Optionen zum Binden von Serversteuerelementen an XML-Daten in ASP.NET. Eine Möglichkeit besteht darin, die XML-Daten in ein DataSet
zu lesen und dann die im vorherigen Abschnitt gezeigten Techniken zu verwenden. Eine weitere Option besteht darin, die XML-API in .NET zu verwenden, um die Daten direkt zu laden und an aufzählbare Klassen in den geladenen Daten zu binden. Die letzte und vielleicht attraktivste Option besteht darin, das spezialisierte Xml
Websteuerelement zu verwenden, das sich selbst rendert, indem eine XSL-Transformation auf ein XML-Dokument angewendet wird.
DieXmlDocument
-Klasse stellt eine Implementierung des XML-DOM in .NET bereit und kann direkt für die Bindung an Steuerelemente verwendet werden, die Datenbindung unterstützen. Die primäre Klasse, die zum Navigieren imXmlDocument
DOM verwendet wird, ist XmlNode
ein Element im Dokument. Glücklicherweise implementiert dieXmlNode
Klasse, um einen Enumerator über ihre untergeordnetenIEnumerable
Elemente zurückzugeben, was bedeutet, dass wir eine beliebigeXmlNode
Datenquelle in der Datenbindung verwenden können. Es stellt sich heraus, dassXmlDocument
auch von XmlNode
abgeleitet wird, da ein Dokument eigentlich nur ein einzelner Knoten mit untergeordneten Elementen ist, was die Navigation sehr einfach macht. Betrachten Sie als Beispiel das folgende XML-Dokument, das in der Datei "publishers.xml" gespeichert ist:
<publishers> <publisher name="New Moon Books" city="Boston" state="MA" country="USA"> <author name="Albert Ringer "> <title name="Is Anger the Enemy?" /> <title name="Life Without Fear" /> </author> <author name="John White "> <title name="Prolonged Data Deprivation " /> </author> <author name="Charlene Locksley "> <title name="Emotional Security: A New Algorithm" /> </author> <author name="Marjorie Green "> <title name="You Can Combat Computer Stress!" /> </author> </publisher> <publisher name="Binnet and Hardley" city="Washington" state="DC" country="USA"> <author name="Sylvia Panteley "> <title name="Onions, Leeks, and Garlic" /> </author> <author name="Burt Gringlesby "> <title name="Sushi, Anyone?" /> </author> <author name="Innes del Castillo "> <title name="Silicon Valley Gastronomic Treats" /> </author> <author name="Michel DeFrance "> <title name="The Gourmet Microwave" /> </author> <author name="Livia Karsen "> <title name="Computer Phobic AND Non-Phobic" /> </author> </publisher> <!-- ... --> </publishers>
Wir können diese Datei in eineXmlDocument
Klasse im Handler unserer SeiteLoad
laden und das Element der obersten Ebenepublishers
wie folgt an einen Repeater binden:
private void Page_Load(object sender, EventArgs e) { XmlDocument doc = new XmlDocument(); doc.Load(Server.MapPath("~/Publishers.xml")); _rep1.DataSource = doc.FirstChild; _rep1.DataBind(); }
Als Nächstes müssen wir herausfinden, wie das erforderliche geschachtelte geschrieben wirdRepeaters
, um die Daten aus dem XML-Dokument zu extrahieren und auf dem Client zu rendern. Anhand unserer beiden vorherigen Beispiele können wir diese Daten auf die gleiche Weise modellieren. Da unser Dokument über drei Datenebenen (Herausgeber, Autoren und Titel) verfügt, definieren wir dreiRepeater
Steuerelemente, wobei die AutorenRepeater
innerhalb der Herausgeber Repeater
geschachtelt sind und die TitelRepeater
in den Autoren Repeater
geschachtelt sind. Diese Anordnung ist unten dargestellt:
<asp:Repeater id="_rep1" runat="server" EnableViewState="false"> <itemTemplate> Publisher: <%# ((XmlNode)Container.DataItem). Attributes["name"].Value %><br/> <asp:Repeater runat="server" EnableViewState="false" DataSource='<%# Container.DataItem %>' > <itemTemplate> Author: <%# ((XmlNode)Container.DataItem) .Attributes["name"].Value %><br/> <asp:Repeater runat="server" EnableViewState="false" DataSource='<%# Container.DataItem %>' > <itemTemplate> <i> <%# ((XmlNode)Container.DataItem). Attributes["name"].Value %> </i><br /> </itemTemplate> </asp:Repeater> </itemTemplate> </asp:Repeater> <hr /> </itemTemplate> </asp:Repeater>
Dies wird wie folgt dargestellt:
Abbildung 5. Datenbindungstest
Der Prozess der Bindung an XML-Daten unterscheidet sich deutlich von den beiden vorherigen Beispielen. Beachten Sie zunächst, dass unsere deklarativen DataSource
Ausdrücke bemerkenswert einfach waren –Container.DataItem
. Dies liegt daran, dass die Datenquelle auf jeder Ebene der Datenbindung einfach eine XmlNode
ist, die einen Enumerator über die untergeordneten Elemente implementiert. Beachten Sie auch, dass zum Extrahieren von Daten aus dem aktuellen Datenelement dieContainer.DataItem
XmlNode
Attribute in und (in diesem Fall) extrahiert werden mussten. Die in der Regel praktischeDataBinder.Eval()
Methode ist in diesem Fall nutzlos, da sie für die Arbeit mit Datenbankquellen und nicht mit XML-Quellen konzipiert ist.
Im Allgemeinen ist das Binden beliebiger XML-Daten mithilfe von Datenbindungssteuerelementen eine ziemlich umständliche Aufgabe. Im vorherigen Beispiel wurden Daten verwendet, die aus einer Reihe von Datenbanktabellen extrahiert wurden und daher recht regelmäßig und gut strukturiert waren, sodass es möglich war, einen Satz geschachtelter Steuerelemente zu definieren, die der Struktur der Daten entsprachen. Dies wird schwieriger, wenn die Daten unregelmäßig sind oder wenn die Daten nicht hierarchisch sind. Betrachten Sie beispielsweise das folgende XML-Dokument:
<animals> <animal> <name>Dog</name> <sound>woof</sound> <hasHair>true</hasHair> </animal> <animal> <name>Cat</name> <sound>meow</sound> <hasHair>true</hasHair> </animal> <animal> <name>Pig</name> <sound>oink</sound> <hasHair>false</hasHair> </animal> </animals>
Wenn wir dieselbe Technik wie in unserem vorherigen Beispiel verwendet haben, können wir versuchen, einen Repeater auf oberster Ebene zu definieren, um jedes Tierelement mit einem anderen geschachtelten Repeater aufzulisten, um jedes Unterelement von Tier anzuzeigen:
<asp:Repeater ID="_animalRep" Runat="server" EnableViewState="false"> <ItemTemplate> <asp:Repeater Runat="server" EnableViewState="false" DataSource='<%# Container.DataItem %>' > <ItemTemplate> <%# ((XmlNode)Container.DataItem).InnerText %><br /> </ItemTemplate> </asp:Repeater> <hr /> </ItemTemplate> </asp:Repeater>
Dies ist jedoch nicht sehr überzeugend, da es einfach den Inhalt jedes untergeordneten Knotens rendert, ohne den Elementnamen überhaupt zu verwenden. Es gibt keine einfache Möglichkeit, den Repeater anweisen, sich selbst in einer Weise zu rendern, wenn das Element name ist, und eine andere Möglichkeit, wenn es sound ist. Stattdessen würden wir am Ende viele bedingte Ausdrücke schreiben, um den XML-Code so zu rendern, wie wir es wollten.
An diesem Punkt sollte offensichtlich sein, dass die Datenbindungssteuerelemente in ASP.NET nicht für die Bindung an beliebige XML-Dokumente konzipiert waren. Stattdessen ist es viel bequemer, die vorhandene XML-Transformationssprache XSL zum Rendern von XML zu nutzen. ASP.NET bietet eine bequeme Möglichkeit, dies auch für einen Teil einer Seite über das Xml-Steuerelement zu tun. Es verwendet als Eingabe ein XML-Dokument und eine XSL-Transformation und rendert, indem die Transformation auf das Dokument angewendet wird. Für unser Tier-XML-Dokument können wir animal.xsl wie folgt schreiben:
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="animal"> <hr /> <xsl:apply-templates /> </xsl:template> <xsl:template match="name"> <i><xsl:value-of select="." /></i><br/> </xsl:template> <xsl:template match="sound"> <b><xsl:value-of select="." />!!!</b><br/> </xsl:template> <xsl:template match="hasHair"> Has hair? <xsl:value-of select="." /><br/> </xsl:template> </xsl:transform>
Die wir dann als Eingabe für ein Xml-Steuerelement auf unserer Seite angeben könnten:
<asp:Xml Runat="server" DocumentSource="animals.xml" TransformSource="animals.xsl" />
Und rendert wie folgt:
Abbildung 6. Rendering von animals.xsl
Zugreifen auf geschachtelte Steuerelemente
In unseren bisherigen Beispielen haben wir uns nur auf die Darstellung der Daten konzentriert, ohne dass daten vom Benutzer erfasst werden. Das Abrufen von Daten aus den Tiefen eines hierarchisch gebundenen Steuerelements kann ziemlich mühsam sein, da Sie durch die Hierarchie dynamisch erstellter Steuerelemente navigieren und ihren Zustand abrufen müssen. Eine weitere, bequemere Option ist das Hinzufügen von Änderungsbenachrichtigungshandlern zu Steuerelementen, die in das datengebundene Steuerelement eingebettet sind. Wenn der Benachrichtigungshandler ausgelöst wird, können Sie die dem Steuerelement zugeordneten Daten extrahieren.
Um dieses Verfahren zu veranschaulichen, können wir unser erstes Beispiel für die Bindung einesRepeater
an benutzerdefinierte Daten und Rendering-Kontrollkästchen für jedes Element und Unterelement verwenden. Wenn der Benutzer eines der Kontrollkästchen aktiviert und die Seite übermittelt, drucken wir einfach die Tatsache, dass sie unten auf der Seite aktiviert wurde, in ein Label
aus. Die ASPX-Datei dafür sieht wie folgt aus:
<asp:Repeater Runat="server" ID="_itemsRepeater" EnableViewState="False"> <ItemTemplate> <asp:CheckBox Runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' OnCheckedChanged="OnCheckedItem" /> <asp:Repeater Runat="server" ID="_subitemsRepeater" EnableViewState="False" DataSource='<%# DataBinder.Eval(Container.DataItem, "SubItems") %>'> <ItemTemplate> <br/> <asp:CheckBox Runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' OnCheckedChanged="OnCheckedItem" /> <asp:Repeater Runat="server" EnableViewState="False" DataSource='<%# DataBinder.Eval(Container.DataItem, "SubItems") %>'> <ItemTemplate> <br/> <asp:CheckBox Runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' OnCheckedChanged="OnCheckedItem"/> </ItemTemplate> </asp:Repeater> </ItemTemplate> </asp:Repeater> <br /> </ItemTemplate> </asp:Repeater> <asp:Button Runat="server" Text="Submit" /> <asp:Label EnableViewState="False" Runat="server" ID="_message" />
Wenn nun der Client die Seite postet, wird unserOnCheckedItem
Handler einmal für jedes Element aufgerufen, dessen Status geändert wurde (auf deaktiviert oder deaktiviert) aktiviert. Wir können ermitteln, welches Steuerelement vom Client geändert wurde, indem wir den Senderparameter in unseren Ereignishandler betrachten. Hier wird eine Beispielimplementierung eines solchen Handlers angezeigt, der eine Nachricht auf die Seite schreibt, die angibt, dass ein Kontrollkästchen den Zustand geändert hat:
protected void OnCheckedItem(object sender, EventArgs e) { CheckBox cb = sender as CheckBox; if (cb.Checked) _message.Text += string.Format("{0} was checked<br />", cb.Text); else _message.Text += string.Format("{0} was unchecked<br/>", cb.Text); }
Hierarchische DataGrid- und DataList-Bindung
Alle bisherigen Beispiele konzentrierten sich auf die Steuerung aus Gründen derRepeater
Einfachheit. Es ist jedoch möglich, hierarchische Datenbindungen sowohl mit denDataList
- als auch mit denDataGrid
-Steuerelementen durchzuführen. Tatsächlich sind die Details der Bindung der Daten identisch, unabhängig davon, welches Steuerelement Sie verwenden. Sie können sie sogar mischen und abgleichen, indem Sie z. B. einRepeater
mit einem geschachtelten DataList
haben. Die Codebeispiele für diesen Artikel enthalten identische Beispiele mit den Beispielen, die sowohl für dieRepeater
DataList
DataGrid
- als auch die -Klasse angezeigt werden.
DieDataList
-Klasse rendert eine Tabelle, in der jede Zelle in der Tabelle ein Rendering einer Zeile im Resultset ist. Die Verwendung vonDataList
zum hierarchischen Binden führt zu geschachtelten Tabellenrenderings, bei denen eine Zelle im Steuerelement der obersten Ebene eine gesamte Tabelle enthält, die vom geschachtelten Steuerelement gerendert wird. Unten wird ein Beispielrendering desDataSet
mit den Northwind-Daten aufgefüllten Daten mit drei Ebenen der hierarchischen Datenbindung an einDataList
angezeigt (nur eine Zelle der obersten Ebene).
Abbildung 7. DataSet mit Northwind-Daten aufgefüllt
DieDataGrid
-Klasse rendert eine Tabelle, in der jede Zeile in der Tabelle ein Rendering einer Zeile im Resultset ist. Die Verwendung vonDataGrid
zum hierarchischen Binden führt auch zu geschachtelten Tabellenrenderings, aber im Gegensatz zu den DataList
liegt es an Ihnen, zu entscheiden, welche Zelle die geschachtelten Tabellen enthält, indem Sie diese Spalte zu einer Vorlagenspalte machen und eine geschachtelte als Teil der Definition der VorlagenspalteDataGrid
hinzufügen. Unten wird ein Beispielrendering desDataSet
mit den Northwind-Daten aufgefüllten Daten mit drei Ebenen der hierarchischen Datenbindung an einDataGrid
gezeigt (nur eine Zeile auf oberster Ebene).
Abbildung 8. DataSet mit Northwind-Daten aufgefüllt
Einschränkungen und Effizienz
Es ist wichtig zu beachten, dass der Datenbindungsmechanismus in ASP.NET für die Bindung an flache Datenquellen konzipiert wurde, und obwohl er zur hierarchischen Bindung verwendet werden kann, ist er möglicherweise nicht immer die beste Wahl für das Rendern von Daten, die wirklich hierarchisch sind. Die Datenquelle muss regelmäßig sein. Es ist nicht möglich, an eine Datenquelle zu binden, die an einigen Stellen 2 Ebenen tief und an anderen 4 oder 5 Ebenen tief ist. XSL eignet sich viel besser zum Rendern von Daten mit beliebiger hierarchischer Form. Daher ist das Konvertieren Ihrer Daten in XML und die Verwendung von XSL-Transformationen mit dem ASP Xml-Steuerelement wahrscheinlich die beste Option.
Möglicherweise haben Sie bemerkt, dass alle Beispiele in diesem Artikel darauf bedacht waren, dasEnableViewState
Flag in jedem datengebundenen Steuerelement auf false festzulegen.ViewState
wird verwendet, um den Zustand im Auftrag von Steuerelementen zwischen POST-Anforderungen auf derselben Seite zu speichern. Standardmäßig behalten alle serverseitigen Steuerelemente in ASP.NET ihren gesamten Zustand zwischen POST-Anforderungen bei, was praktisch ist, da Sie sich auf die Zustandsaufbewahrung für alle Ihre Steuerelemente verlassen können. Es kann jedoch auch die Renderinggröße Ihrer Seiten erheblich verbessern, und wenn Sie die Statusaufbewahrung nicht nutzen, sollten Sie Schritte unternehmen, um diese Steuerelemente zu deaktivierenViewState
. Diese Technik ist besonders anfällig fürViewState
Aufblähungen, da der gesamte Inhalt jedes datengebundenen Steuerelements und aller untergeordneten Steuerelemente standardmäßig inViewState
gespeichert wird. In den meisten Fällen wird der Inhalt diesesViewState
Steuerelements überhaupt nicht verwendet, da die datengebundenen Steuerelemente bei jeder Anforderung an ihre Datenquellen neu gebunden werden, unabhängig davon, ob es sich um die erste GET-Anforderung an eine Seite oder einen nachfolgenden POST zurück an dieselbe Seite handelt. Wenn Sie also keinen guten Grund haben, etwas anderes zu tun, empfiehlt es sich, in allen datengebundenen Steuerelementen auf false festzulegenEnableViewState
, insbesondere, wenn Sie die hierarchischen Datenbindungstechniken verwenden, die in diesem Artikel beschrieben werden. Es ist in Ordnung, geschachtelte serverseitige Steuerelemente innerhalb einesItemTemplate
datengebundenen Steuerelements aktiviert zu lassenViewState
. Denken SieCheckBox
Repeater
nur daran, es für das eigentliche datengebundene Steuerelement selbst zu deaktivieren. Ein Beispiel dafür, wie dramatisch dies sein kann, wenn Sie den Ansichtszustand für alle Steuerelemente in unserem Beispiel aktivieren, dieRepeater
an ein DataSet
gebunden sind, wurde dasViewState
Feld auf 250.000 Zeichen vergrößert. Dies steht im Gegensatz zu der Anzahl der sichtbaren Zeichen, die auf der Seite gerendert wurden, in der Größenordnung von 100.000 Zeichen.
Zusammenfassung
In diesem Artikel wurde eine Technik zum Binden an hierarchische Daten vorgestellt, indem datengebundene Steuerelemente in Vorlagen geschachtelt und deren Datenquellen dynamisch zugewiesen werden. Diese Technik kann sehr nützlich sein, um reguläre, strukturierte hierarchische Daten wie die in Tabellenbeziehungen innerhalb einer Datenbank zu rendern. Es ist auch möglich, andere hierarchische Datenquellen mit diesem Verfahren zu rendern, aber es ist umständlich zu verwenden, wenn die Daten nicht in allen Dimensionen regelmäßig sind. Alternative Techniken, die XSL mit XML-Datenquellen verwenden, sind in der Regel präziser und geben Ihnen mehr Kontrolle über das Rendering.