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 namensDataSourceund eine Methode mit dem Namen DataBind() unterstützen muss und dass die Datenquelle, an die das Steuerelement gebunden ist, dieIEnumerableSchnittstelle 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 StandardstandardDataViewder Standardtabelle führt (DataView implementiert). IEnumerable Dies ist der Einfachheit halber, daDataSetsund DataTables 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 eineArrayListFü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 dieTestDataSourceBindung. Wir verwenden dasRepeaterals serverseitiges Steuerelement, an das wir die Daten binden, was erfordert, dass wir eineItemTemplateBeschreibung bereitstellen, wie die einzelnen Elemente in der aufzählbaren Datenquelle gerendert werden sollen. In unserem Beispiel wird einCheckBoxSteuerelement gerendert, dessen Text auf dieNameEigenschaft 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 derLoadRegel 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:

Aa478959.aspn-hierdatabinding_01(en-us,MSDN.10).gif

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 desItemTemplatevon dem platzieren müssen, dasRepeaterwir bereits haben, und es an dieSubItemsSammlung der einzelnenItemElemente 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, dieSubItemsAuflistung derItemaktuell an die DataSource Eigenschaft des geschachtelten Repeater gebundenen zuzuordnen. Dies geschieht durch deklaratives Festlegen derDataSourceEigenschaft des geschachteltenRepeaterauf 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/>&nbsp;&nbsp;&nbsp;
           <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 EinmalItemin 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 EbeneRepeaterausgerichtet 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 derSubItemsAuflistung des aktuellItemgebundenen auf. Das Rendern dieser Seite ist unten dargestellt:

Aa478959.aspn-hierdatabinding_02(en-us,MSDN.10).gif

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, dessenDataSourceEigenschaft an dieSubItemsEigenschaft 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/>&nbsp;&nbsp;&nbsp;
        <asp:CheckBox Runat="server"
             Text=
       '<%# DataBinder.Eval(Container.DataItem, "Name") 
         %>'/>
        <asp:Repeater Runat="server" EnableViewState="false"
             DataSource=
    '<%# DataBinder.Eval(Container.DataItem, "SubItems") 
      %>'>
          <ItemTemplate>
            <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <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:

Aa478959.aspn-hierdatabinding_03(en-us,MSDN.10).gif

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 derCustomersTabelle und derOrdersTabelle. Ebenso besteht eine 1:n-Beziehung zwischen derOrdersTabelle und derOrder DetailsTabelle. Diese Beziehungen sind in der folgenden Abbildung dargestellt:

Aa478959.aspn-hierdatabinding_04(en-us,MSDN.10).gif

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 folgendeLoadHandler führt dies aus und bindet dann den resultierendenDataSetan 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 derCustomersTabelle 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 Detaileiner bestimmten Reihenfolge zugeordnet sind, indem wir mit der Zeichenfolge "Order_OrderDetail" für eine Zeile aus der Tabelle aufrufenGetChildRows, um alleOrder Detaileinträge abzurufen, dieOrdersdieser Reihenfolge zugeordnet sind. Noch nützlicher für unsere Zwecke ist dieCreateChildViewMethode derDataRowView-Klasse, die eineDataViewzurü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 dieCustomersTabelle in unserer DataSet zu binden, eine geschachtelteRepeater, um an alleOrdersden einzelnen Kunden zugeordneten zu binden, und eine andere geschachteltRepeater, um an alle Einträge zu binden, dieOrder Detailjeder Bestellung zugeordnet sind. DasDataSourcefür die beiden geschachteltenRepeatersist das Ergebnis des AufrufensCreateChildViewder übergeordneten Zeile mit dem entsprechenden Beziehungsnamen. Anstatt zu versuchen, denDataViewin einem einzelnen Ausdruck in derRepeaterDeklaration 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 dieRepeaterSteuerelementdeklarationen 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") %>
   &nbsp; &nbsp;
    <%# DataBinder.Eval(Container.DataItem,"CompanyName") %>
    <br />
    <asp:Repeater runat="server" EnableViewState="false"
         DataSource=
            '<%# GetChildRelation(Container.DataItem, 
                                  "Customer_Order")%>'
    >
      <itemTemplate>
       &nbsp;&nbsp;&nbsp;&nbsp;
       Orderid:<b>
       <%#DataBinder.Eval(Container.DataItem, "OrderID")%> 
       </b><br/>
       <asp:Repeater runat="server" EnableViewState="false"
            DataSource=
                '<%# GetChildRelation(Container.DataItem, 
                                     "Order_OrderDetail")%>'
       >
         <itemTemplate>
           &nbsp;&nbsp;&nbsp;&nbsp;
           &nbsp;&nbsp;&nbsp;&nbsp;
           <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 imXmlDocumentDOM verwendet wird, ist XmlNode ein Element im Dokument. Glücklicherweise implementiert dieXmlNodeKlasse, um einen Enumerator über ihre untergeordnetenIEnumerableElemente zurückzugeben, was bedeutet, dass wir eine beliebigeXmlNodeDatenquelle in der Datenbindung verwenden können. Es stellt sich heraus, dassXmlDocumentauch 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 eineXmlDocumentKlasse im Handler unserer SeiteLoadladen und das Element der obersten Ebenepublisherswie 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 dreiRepeaterSteuerelemente, wobei die AutorenRepeaterinnerhalb der Herausgeber Repeater geschachtelt sind und die TitelRepeaterin 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>
        &nbsp;&nbsp;Author: <%# 
          ((XmlNode)Container.DataItem)
                            .Attributes["name"].Value 
                              %><br/>
        <asp:Repeater runat="server" EnableViewState="false"
                   DataSource='<%# Container.DataItem %>' >
           <itemTemplate>
              &nbsp;&nbsp;&nbsp;&nbsp;<i>
           <%# ((XmlNode)Container.DataItem).
                         Attributes["name"].Value %>
           </i><br />
           </itemTemplate>
         </asp:Repeater>
      </itemTemplate>
    </asp:Repeater>
    <hr />
  </itemTemplate>
 </asp:Repeater>

Dies wird wie folgt dargestellt:

Aa478959.aspn-hierdatabinding_05(en-us,MSDN.10).gif

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.DataItemXmlNodeAttribute 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:

Aa478959.aspn-hierdatabinding_06(en-us,MSDN.10).gif

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 einesRepeateran 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/>&nbsp;&nbsp;&nbsp;
        <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/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <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 unserOnCheckedItemHandler 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 derRepeaterEinfachheit. 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. einRepeatermit einem geschachtelten DataList haben. Die Codebeispiele für diesen Artikel enthalten identische Beispiele mit den Beispielen, die sowohl für dieRepeaterDataListDataGrid- 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 vonDataListzum 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 desDataSetmit den Northwind-Daten aufgefüllten Daten mit drei Ebenen der hierarchischen Datenbindung an einDataListangezeigt (nur eine Zelle der obersten Ebene).

Aa478959.aspn-hierdatabinding_07(en-us,MSDN.10).gif

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 vonDataGridzum 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 VorlagenspalteDataGridhinzufügen. Unten wird ein Beispielrendering desDataSetmit den Northwind-Daten aufgefüllten Daten mit drei Ebenen der hierarchischen Datenbindung an einDataGridgezeigt (nur eine Zeile auf oberster Ebene).

Aa478959.aspn-hierdatabinding_08(en-us,MSDN.10).gif

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, dasEnableViewStateFlag in jedem datengebundenen Steuerelement auf false festzulegen.ViewStatewird 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ürViewStateAufblähungen, da der gesamte Inhalt jedes datengebundenen Steuerelements und aller untergeordneten Steuerelemente standardmäßig inViewStategespeichert wird. In den meisten Fällen wird der Inhalt diesesViewStateSteuerelements ü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 einesItemTemplatedatengebundenen Steuerelements aktiviert zu lassenViewState. Denken SieCheckBoxRepeaternur 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, dieRepeateran ein DataSet gebunden sind, wurde dasViewStateFeld 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.

© Microsoft Corporation. Alle Rechte vorbehalten.