.NET Blog

Tony Cavaliere

 
My Favourite Albums
  And the Grappa wins.
E-mail me Send mail

Disclaimer

Hey unlike other bloggers I stand by what I say but just in case. The opinions expressed herein are my own except on Tuesday when the second card is not turned up otherwise it ain't worth squat.

© Copyright 2013

LINQ to XML, Object Graphs and the ListView control

I have a subscription to msdn magazine and am a regular reader. I especially try to read all the articles on any ASP.NET related topics. Dino Exposito is a regular contributor to this magazine and has written many great articles on ASP.NET. In the April 2008 he wrote an excellent article on the new ASP.NET ListView control, specially, he discusses in detail how to bind a ListView control to hierarchical data.

This is by no means the first article that I have read on the topic of ListView controls. In all the articles or blog postings that I have read, I have yet to see how to use LINQ to create custom objects with complex object graphs. Instead anonymous types are used. In this post I will demonstrate how to create a custom object graph from a LINQ to XML query. Furthermore, I will bind this object to a nested ListView control. I will use the same example as presented by Dino in the August edition of msdn magazine.

We will be creating a hierarchical menu that is rendered using LINQ to XML, ListView control and an XML file. Listing 1 shows the menu.xml file.

Listing 1: menu.xml

<?xml version="1.0" encoding="utf-8" ?>

<menu>

  <item>

    <title>Menu Title 1</title>

    <link url="url1-1" text="text1-1"></link>

    <link url="url1-2" text="text1-2"></link>

    <link url="url1-3" text="text1-3"></link>

    <link url="url1-4" text="text1-4"></link>

  </item>

  <item>

    <title>Menu Title 2</title>

    <link url="url2-1" text="text2-1"></link>

    <link url="url2-2" text="text2-2"></link>

    <link url="url2-3" text="text2-3"></link>

  </item>

  <item>

    <title>Menu Title 3</title>

    <link url="url3-1" text="text3-1"></link>

    <link url="url3-2" text="text3-2"></link>

  </item>

</menu>

The schema of this XML file is straight forward. Each <item> node contains a menu title and then has 1 or more links. Each link has the URL to navigate too and the title that is to be used as textual display for the link.

Ultimately we would like to bind this data to a nested ListView control. The markup for this nested ListView control is shown in Listing 2. The title is render in the outer ListView control, whereas, the links are render in the inner ListView control. The code bind will be responsible for binding the outer ListView. The inner is bound declaratively. Please note that DataSource for the inner ListView control must be named links.

List 2: Nested ListView Control

    <asp:ListView ID="ListViewMenu" runat="server" ItemPlaceholderID="PlaceHolder1">

 

      <LayoutTemplate>

          <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>

      </LayoutTemplate>

      <ItemTemplate>

 

        <h1><%# Eval("title")%></h1>

 

        <asp:ListView ID="ListViewSubMenu" runat="server"

          DataSource='<%# Eval("links") %>' ItemPlaceholderID="PlaceHolder2">

 

          <LayoutTemplate>

            <ul><asp:PlaceHolder ID="PlaceHolder2" runat="server"></asp:PlaceHolder></ul>

          </LayoutTemplate>

 

          <ItemTemplate>

            <li><a href='<%# Eval("url") %>'><%#Eval("text")%></a></li>

          </ItemTemplate>

 

        </asp:ListView>

      </ItemTemplate>

    </asp:ListView>

Before we use custom objects, let's review how we can use LINQ to XML and anonymous types as a data source to a ListView control. Listing 3 shows the code behind that accomplishes this.

List 3: Binding ListView Control with an Anonymous Type

        Dim doc As XDocument = XDocument.Load(Server.MapPath("App_Data/Menu.xml"))

        Dim menu = From mi In doc.<menu>.<item> _

                   Select _

                        title = mi.<title>.Value, _

                        links = From link In mi.<link> _

                                Select _

                                    url = link.@url, _

                                    [text] = link.@text

 

        ListViewMenu.DataSource = menu

        ListViewMenu.DataBind()

In this code snippet we are using the LINQ to XML features of VB.NET to query the XML file. The doc.<menu>.<item> retrieves all the <item> children for the document. The content of the <item> node is loaded into an anonymous type having two properties; title and links. The property links is a collection that is populated by the sub query. In the end, the anonymous type, menu is of type IEnumerable(Of <anonymous type>). The menu object is then used as data source to the outer ListView control.

Anonymous type are great, however, there are cases when you may need to pass the data between methods, tiers or even across process boundaries. In this scenario you should us a custom type. To do this you must first create the classes. In our case two classes are required. Listing 4 shows the two classes.

List 4: Custom Types 

<DebuggerStepThrough()> _

Public Class Menu

    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _

    Private _title As String

    Public Property title() As String

        Get

            Return _title

        End Get

        Set(ByVal value As String)

            _title = value

        End Set

    End Property

    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _

    Private _links As IEnumerable(Of Link)

    Public Property links() As IEnumerable(Of Link)

        Get

            Return _links

        End Get

        Set(ByVal value As IEnumerable(Of Link))

            _links = value

        End Set

    End Property

End Class

 

<DebuggerStepThrough()> _

Public Class Link

    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _

    Private _url As String

    Public Property url() As String

        Get

            Return _url

        End Get

        Set(ByVal value As String)

            _url = value

        End Set

    End Property

    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _

    Private _text As String

    Public Property text() As String

        Get

            Return _text

        End Get

        Set(ByVal value As String)

            _text = value

        End Set

    End Property

End Class

The two classes are named, Menu and Link. Menu contains two properties; title and links. links is of type IEnumerable(Of Link) which is important as LINQ queries of this type return the generic type IEnumerable(Of T). I have tried other generic types that derive from IEnumerable(Of T) such as List(Of T) but they generate Unable to cast object of type... exception. Perhaps there is a way to explicitly cast the object but that would require further investigation. Perhaps a future blog post?

Listing 5 shows LINQ to XML code to make use of the custom type.

List 5: Binding ListView Control with a Custom Type

        Dim doc As XDocument = XDocument.Load(Server.MapPath("App_Data/Menu.xml"))

        Dim menu = From mi In doc.<menu>.<item> _

                   Select New Menu With { _

                        .title = mi.<title>.Value, _

                        .links = From l In mi.<link> _

                                Select New Link With { _

                                    .url = l.@url, _

                                    .[text] = l.@text _

                            } _

                    }

        ListViewMenu.DataSource = menu

        ListViewMenu.DataBind()

The menu object is now of type IEnumerable(Of Menu), which can be easily passed as a parameter to a method or serialized across the wire.

Guess the movie

You loved my father, I know. But so did I. That makes us brothers, doesn't it? Smile for me now, brother.

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: ASP.NET | VB.NET | LINQ | XML
Posted by CynotWhyNot on Wednesday, April 16, 2008 4:10 PM
Permalink | Comments (44) | Post RSSRSS comment feed