View Single Post
  #8 (permalink)  
Old February 13th, 2009, 12:22 PM
Lee Dumond's Avatar
Lee Dumond Lee Dumond is offline
Wrox Author
Points: 4,942, Level: 29
Points: 4,942, Level: 29 Points: 4,942, Level: 29 Points: 4,942, Level: 29
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jan 2008
Location: Decatur, IL, USA.
Posts: 923
Thanks: 12
Thanked 166 Times in 162 Posts
Default

Quote:
Originally Posted by philthy View Post
Thats exactly how I did it, only difference is that I also bound the inner ListView to an ObjectDataSource. I have a class Tag, and this class has a method "GetTagsByArticle". So, I made that the select method, and then the Tag class as Type in this ObjectDataSource.

I bind the outer ListView to an ObjectDataSource to get the articles, and the inner ListView to an ObjectDataSource to get the tags for each article.
Yes, this is exactly how I envisioned it, and it is exactly the first approach I tried. And (no surprise!) I had the same problem you had.

So then, I figured I would intercept the ItemDataBound event on the outer ListView, grab the inner ListView from the Controls collection, grab the articleID from the eventargs, and pass it in that way. No joy there though, because once ItemDataBound has fired on the outer list, it's too late to bind the inner list.

Then I finally figured out how to do this the right way. The key is NOT to bind the inner list to a datasource at all, but to bind the inner list to a property of the outer list item using a data expression. That way, when the outer list item is databound, it will bind the innerlist at the same time, all in one pass.

Here is the page:

Code:
<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
   <title>Articles</title>
</head>
<body>
   <form id="form1" runat="server">
      <div>
         <asp:ListView ID="lstArticles" runat="server" DataSourceID="objArticles"
            ItemPlaceholderID="itemPlaceHolder1">
            <ItemTemplate>
               <p>
                  <asp:Label ID="lblArticleTitle" runat="server" Font-Bold="true"
                     Text='<%# Eval("Title") %>' />
                  <br />
                  <asp:Label ID="lblPublishedDate" runat="server" Font-Italic="true"
                     Text='<%# Eval("PublishedDate","{0:MMM d, yyyy}") %>' />
                  <br />
                  Tags:
                  <asp:ListView ID="lstTags" runat="server" ItemPlaceholderID="itemPlaceHolder2"
                     DataSource='<%# Eval("Tags") %>'>
                      <ItemTemplate>
                        <asp:Label ID="lblTagName" runat="server" Text='<%# Eval("TagName") %>' />
                     </ItemTemplate>
                     <LayoutTemplate>
                        <asp:PlaceHolder ID="itemPlaceHolder2" runat="server" />
                     </LayoutTemplate>
                  </asp:ListView>
               </p>
            </ItemTemplate>
            <LayoutTemplate>
               <asp:PlaceHolder ID="itemPlaceHolder1" runat="server" />
            </LayoutTemplate>
         </asp:ListView>
         <asp:ObjectDataSource ID="objArticles" runat="server" SelectMethod="GetArticles"
            TypeName="LD.Blog.Article" />
      </div>
   </form>
</body>
</html>
Note that the inner list (lstTags) is not bound to a data source, but is instead bound using the following databinding expression:

Code:
DataSource='<%# Eval("Tags") %>'
Tags is a property of Article that uses the GetTagsByArticle method to get its value.

Code:
namespace LD.Blog
{
   public class Article
   {
      public int ArticleID { get; set; }

      public string Title { get; set; }

      public DateTime PublishedDate { get; set; }

      private List<Tag> tags;

      public List<Tag> Tags
      {
         get
         {
            if (tags == null)
            {
               tags = Tag.GetTagsByArticle(this.ArticleID);
            }

            return tags;
         }
      }  

      public static List<Article> GetArticles()
      {
         // snipped out         
      }
   }
}
The "Tags" databinding expression is evaluated at the same time as "Title" and "PublishedDate" -- that is, when each Article item is fetched. The inner list then binds to Tags, which internally uses GetTagsByArticle(), just like the ObjectDataSource would have, except it does it at the right time -- when the Article data is actually available.

I tried this with some fake data and it works, so I hope that helps you out. If you need, I can zip the whole thing up and email it to ya.

Have fun.
__________________
Visit my blog at http://leedumond.com
Follow me on Twitter: http://twitter.com/LeeDumond

Code:
if (this.PostHelpedYou)
{
   ClickThanksButton(); 
}
The Following User Says Thank You to Lee Dumond For This Useful Post:
philthy (February 14th, 2009)