On Demand Data Loading in XamTree

[Infragistics] Mihail Mateev / Friday, January 28, 2011

The use of hierarchical data in applications often requires loading data on demand. This is very typical problem, especially when using WCF services from Silverlight applications.

Infragistics Silverlight controls as XamTree, XamDataTree, XamGrid offer an easy way to visualize these data. This article provides an example of the use of Infragistics XamTree with such data as the data loading is done in 2 different ways.
1. Downloading data at every level processing the events from the user interface of the client.
2. Using specially created by Infragistics VirtualCollection <T>, which can take data from the data source of parts on request.

To do that is created Silverlight Navigation Application and Silverlight-enabled WCF service, which returns the sample data.

Requirements:

  1. NetAdvantage for Silverlight  Line of Business 2010 Vol.3
  2. NetAdvantage for .NET 2010 Vol.3
  3. NetAdvantage Ultimate 2010 Vol.3

Sample data:

Demo application uses a sample data from XML file. The only reason to use this file is to make sample easier to install and use. Data is about books and its chapters.

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <books>
   3:   <book Title="The Elegant Universe" Author="Brian Greene" UnitPrice="15.95" Url="http://www.amazon.com/Elegant-Universe-Superstrings-Dimensions-Ultimate/dp/0393046885" ReleaseDate="02/01/1999" >
   4:     <chapter Title="Tied Up with String" />
   5:     <chapter Title="Space, Time, and the Eye of the Beholder" />
   6:     <chapter Title="Of Warps and Ripples" />
   7:     <chapter Title="Microscopic Weirdness" />
   8:     <chapter Title="The Need for a New Theory: General Relativity vs. Quantum Mechanics" />
   9:     <chapter Title="Nothing but Music: The Essentials of Superstring Theory" />
  10:     <chapter Title="The &quot;Super&quot; in Superstrings" />
  11:     <chapter Title="More Dimensions Than Meet the Eye" />
  12:     <chapter Title="The Smoking Gun: Experimental Signatures" />
  13:     <chapter Title="Quantum Gravity" />
  14:     <chapter Title="Tearing the Fabric of Space" />
  15:     <chapter Title="Beyond Strings: In Search of M-Theory" />
  16:     <chapter Title="Black Holes: A String/M-Theory Perspective" />
  17:     <chapter Title="Reflections on Cosmology" />
  18:     <chapter Title="Prospects" />
  19:   </book>
  20:  
  21:   <book Title="Dive Into Python" Author="Mark Pilgrim" UnitPrice="39.99" Url="http://www.amazon.com/Dive-Into-Python-Mark-Pilgrim/dp/1590593561" ReleaseDate="07/19/2004" >
  22:     <chapter Title="Installing Python" />
  23:     <chapter Title="Your First Python Program" />
  24:     <chapter Title="Native Datatypes" />
  25:     <chapter Title="The Power of Introspection" />
  26:     <chapter Title="Objects and Object-Orientation" />
  27:     <chapter Title="Exceptions and File Handling" />
  28:     <chapter Title="Regular Expressions" />
  29:     <chapter Title="HTML Processing" />
  30:     <chapter Title="XML Processing" />
  31:     <chapter Title="Scripts and Streams" />
  32:     <chapter Title="HTTP Web Services" />
  33:     <chapter Title="SOAP Web Services" />
  34:     <chapter Title="Unit Testing" />
  35:     <chapter Title="Test-First Programming" />
  36:     <chapter Title="Refactoring" />
  37:     <chapter Title="Functional Programming" />
  38:     <chapter Title="Dynamic Functions" />
  39:     <chapter Title="Performance Tuning" />
  40:   </book>
  41:  
  42:   <book Title="The Selfish Gene (30th Anniversary Edition)" Author="Richard Dawkins" UnitPrice="15.95" Url="http://www.amazon.com/Selfish-Gene-Anniversary-Introduction/dp/0199291152" ReleaseDate="05/25/2006" >
  43:     <chapter Title="Why are people?" />
  44:     <chapter Title="The replicators" />
  45:     <chapter Title="Immortal coils" />
  46:     <chapter Title="The gene machine" />
  47:     <chapter Title="Aggression: stability and the selfish machine" />
  48:     <chapter Title="Genesmanship" />
  49:     <chapter Title="Family planning" />
  50:     <chapter Title="Battle of the generations" />
  51:     <chapter Title="Battle of the sexes" />
  52:     <chapter Title="You scratch my back, I'll ride on yours" />
  53:     <chapter Title="Memes: the new replicators" />
  54:     <chapter Title="Nice guys finish first" />
  55:     <chapter Title="The long reach of the gene" />
  56:   </book>
  57:  
  58:   <book Title="The Ideological Origins of the American Revolution (Enlarged Edition)" Author="Bernard Bailyn" UnitPrice="24.00" Url="http://www.amazon.com/Ideological-Origins-American-Revolution/dp/0674443020" ReleaseDate="03/01/1992" >
  59:     <chapter Title="The Literature of Revolution" />
  60:     <chapter Title="Sources and Traditions" />
  61:     <chapter Title="Power and Liberty: A Theory of Politics" />
  62:     <chapter Title="The Logic of Rebellion" />
  63:     <chapter Title="Transformation" />
  64:     <chapter Title="The Contagions of Liberty" />
  65:     <chapter Title="Fulfillment: A Commentary on the Constitution" />
  66:   </book>
  67:  
  68:   <book Title="Gödel, Escher, Bach: an Eternal Golden Braid" Author="Douglas R. Hofstadter" UnitPrice="20.00" Url="http://www.amazon.com/Godel-Escher-Bach-Douglas-Hofstadter/dp/B001IAM392" ReleaseDate="01/01/1999" >
  69:     <chapter Title="The MU-puzzle." />
  70:     <chapter Title="Meaning and Form in Mathematics." />
  71:     <chapter Title="Figure and Ground." />
  72:     <chapter Title="Consistency, Completeness, and Geometry." />
  73:     <chapter Title="Recursive Structures and Processes." />
  74:     <chapter Title="The Location of Meaning." />
  75:     <chapter Title="The Propositional Calculus." />
  76:     <chapter Title="Typographical Number Theory." />
  77:     <chapter Title="Mumon and Gödel." />
  78:     <chapter Title="Levels of Description, and Computer Systems." />
  79:     <chapter Title="Brains and Thoughts." />
  80:     <chapter Title="Minds and Thoughts." />
  81:     <chapter Title="BlooP and FlooP and GlooP." />
  82:     <chapter Title="On Formally Undecidable Propositions of TNT and Related Systems." />
  83:     <chapter Title="Jumping out of the System." />
  84:     <chapter Title="Self-Ref and Self-Rep." />
  85:     <chapter Title="Church, Turing, Tarski, and Others." />
  86:     <chapter Title="Artificial Intelligence: Retrospects." />
  87:     <chapter Title="Artificial Intelligence: Prospects." />
  88:     <chapter Title="Strange Loops, Or Tangled Hierarchies." />
  89:   </book>
  90:  
  91:   <book Title="Silverlight 1.0" Author="Devin Rader, Jason Beres, J. Ambrose Little, Grant Hinkson" UnitPrice="23.37" Url="http://www.amazon.com/Silverlight-1-0-Devin-Rader/dp/0470228407" ReleaseDate="10/29/2007" >
  92:    <chapter Title="Introduction to Silverlight" />
  93:     <chapter Title="Building Silverlight Applications Using XAML" />
  94:     <chapter Title="Designing Silverlight Applications Using Expression Blend 2"  />
  95:     <chapter Title="Coding Silverlight Applications with JavaScript and XAML"  />
  96:     <chapter Title="Using Silverlight with ASP.NET"  />
  97:     <chapter Title="Silverlight 1.1 and the CLR"  />
  98:     <chapter Title="Video Player: Silverlight 1.0 Case Example"  />
  99:   </book>
 100:   <book Title="Harry Potter and the Deathly Hallows" Author="J. K. Rowling" UnitPrice="20.99" Url="http://www.amazon.com/Harry-Potter-Deathly-Hallows-Book/dp/0545010225" ReleaseDate="07/21/2007" />
 101:   <book Title="Silverlight 2 Silverlight 2 Programmer's Reference's Reference" Author="J. Ambrose Little, Jason Beres, Grant Hinkson, Devin Rader, Joe Croney" UnitPrice="31.49" Url="http://www.amazon.com/Silverlight-Programmers-Reference-Ambrose-Little/dp/0470385405" ReleaseDate="03/23/2009" />
 102:   <book Title="Breaking Dawn (The Twilight Saga, Book 4)" Author="Stephenie Meyer" UnitPrice="12.64" Url="http://www.amazon.com/New-Moon-Twilight-Saga-Book/dp/0316024961" ReleaseDate="08/02/2008" />
 103:   <book Title="The Last Lecture" Author="Randy Pausch and Jeffrey Zaslow" UnitPrice="13.17" Url="http://www.amazon.com/Last-Lecture-Randy-Pausch/dp/1401323251" ReleaseDate="04/08/2008" />
 104:   <book Title="Brisingr" Author="Christopher Paolini" UnitPrice="16.50" Url="http://www.amzon.com/Brisingr-Inheritance-Book-Christopher-Paolini/dp/0375826726" ReleaseDate="09/20/2008" />
 105:   <book Title="The Story of Edgar Sawtelle" Author="David Wroblewski" UnitPrice="14.27" Url="http://www.amazon.com/Story-Edgar-Sawtelle-Novel-Oprah/dp/0061768065" ReleaseDate="09/19/2008"  />
 106:   <book Title="The Tales of Beedle the Bard" Author="J. K. Rowling" UnitPrice="7.14" Url="http://www.amazon.com/Tales-Beedle-Bard-Standard/dp/0545128285" ReleaseDate="12/04/2008" />
 107:   <book Title="The Appeal" Author="John Grisham " UnitPrice="18.45" Url="http://www.amazon.com/Appeal-John-Grisham/dp/0385515049" ReleaseDate="01/29/2008" />
 108:   <book Title="When You Are Engulfed in Flames" Author="David Sedaris" UnitPrice="15.59" Url="http://www.amazon.com/When-You-Are-Engulfed-Flames/dp/0316143472" ReleaseDate="06/03/2008" />
 109:   <book Title="In Defense of Food: An Eater's Manifesto" Author="Michael Pollan" UnitPrice="13.17" Url="http://www.amazon.com/Defense-Food-Eaters-Manifesto/dp/1594201455" ReleaseDate="01/01/2008" />
 110:   <book Title="The Revolution: A Manifesto" Author="Ron Paul" UnitPrice="13.17" Url="http://www.amazon.com/Host-Novel-Stephenie-Meyer/dp/031606804" ReleaseDate="04/01/2008" />
 111:   <book Title="The Host: A Novel" Author="Stephenie Meyer " UnitPrice="13.17" Url="http://www.amazon.com/Host-Novel-Stephenie-Meyer/dp/031606804" ReleaseDate="05/06/2008" />
 112:   <book Title="The Post-American World" Author="Fareed Zakaria" UnitPrice="17.13" Url="http://www.amazon.com/Post-American-World-Fareed-Zakaria/dp/039306235X" ReleaseDate="05/05/2008" />
 113:   <book Title="Hungry Girl" Author="Lisa Lillien" UnitPrice="12.21" Url="http://www.amazon.com/Hungry-Girl-Survival-Strategies-Guilt-Free/dp/0312377428" ReleaseDate="04/29/2008" />
 114:   <book Title="Fearless Fourteen (Stephanie Plum, No. 14)" Author="Janet Evanovich" UnitPrice="18.45" Url="http://www.amazon.com/Fearless-Fourteen-Stephanie-Plum-No/dp/0312349513" ReleaseDate="06/17/2008" />
 115:   <book Title="The Tales of Beedle the Bard, Collector's Edition" Author="J. K. Rowling" UnitPrice="100.00" Url="http://www.amazon.com/Beedle-Collectors-Offered-Exclusively-Amazon/dp/0956010903" ReleaseDate="12/04/2008" />
 116:   <book Title="Unaccustomed Earth" Author="Jhumpa Lahiri" UnitPrice="16.50" Url="http://www.amazon.com/Unaccustomed-Earth-Jhumpa-Lahiri/dp/0307265730" ReleaseDate="04/01/2008" />
 117:   <book Title="sTORI Telling" Author="Tori Spelling" UnitPrice="16.47" Url="http://www.amazon.com/sTORI-Telling-Tori-Spelling/dp/1416950737" ReleaseDate="03/11/2008" />
 118:   <book Title="Audition: A Memoir" Author="Barbara Walters" UnitPrice="19.77" Url="http://www.amazon.com/Audition-Memoir-Barbara-Walters/dp/030726646X"  ReleaseDate="05/06/2008" />
 119: </books>

 

Steps to reproduce:

  • Download sample data
  • Create a sample Silverlight Navigation Application, hosted in an ASP.Net application.
  • Imeplement a Silverlight-enabled WCF service that returns collections of book and book chapters.
  • Create in the Silverlight application a  MVVM using for data ObservabeCollection<T>
  • Create in the Silverlight application a  MVVM using for data custom collections that inherits VirtualCollection<T>

 

WCF Service implementation:
XML file is read and stored in a static XDocument instance. All methods that returns collections of books or book chapters will use this instance to get fast required data. Sample application will visualize data. Data updates from UI are not implemented.

   1: static string dataPath = HttpContext.Current.Server.MapPath(".") + Path.DirectorySeparatorChar +
   2: onfigurationManager.AppSettings["DataDirectory"] + Path.DirectorySeparatorChar +
   3:                ConfigurationManager.AppSettings["DataFile"];
   4:  
   5: static XDocument doc = XDocument.Load(dataPath);

 

Create a classes for Books and Chapters

Initially we will create classes for books and chapters of books to use in transporting data to the client.

   1: #region BaseViewModel
   2: [DataContract]
   3: public abstract class BaseViewModel : INotifyPropertyChanged
   4: {
   5:  
   6:     #region INotifyPropertyChanged Members
   7:  
   8:     public event PropertyChangedEventHandler PropertyChanged;
   9:  
  10:     #endregion
  11:  
  12: }
  13: #endregion //BaseViewModel
  14:  
  15: #region Chapter
  16: [DataContract]
  17: public class Chapter : BaseViewModel
  18: {
  19:  
  20:  
  21:     #region Title
  22:     private string _title;
  23:  
  24:     [DataMember]
  25:     public string Title
  26:     {
  27:         get
  28:         {
  29:             return this._title;
  30:         }
  31:         set
  32:         {
  33:             if (this._title != value)
  34:             {
  35:                 this._title = value;
  36:             }
  37:         }
  38:     }
  39:     #endregion //Title
  40: }
  41: #endregion //Chapter
  42:  
  43: #region Book
  44: [DataContract]
  45: public class Book : BaseViewModel
  46: {
  47:  
  48:     #region Properties
  49:  
  50:     #region Author
  51:     private string _author;
  52:  
  53:     [DataMember]
  54:     public string Author
  55:     {
  56:         get
  57:         {
  58:             return this._author;
  59:         }
  60:         set
  61:         {
  62:             if (this._author != value)
  63:             {
  64:                 this._author = value;
  65:             }
  66:         }
  67:     }
  68:     #endregion //Author
  69:  
  70:     #region ChaptersCount
  71:     private int _chaptersCount;
  72:  
  73:     [DataMember]
  74:     public int ChaptersCount
  75:     {
  76:         get
  77:         {
  78:             return this._chaptersCount;
  79:         }
  80:         set
  81:         {
  82:             if (this._chaptersCount != value)
  83:             {
  84:                 this._chaptersCount = value;
  85:             }
  86:         }
  87:     }
  88:     #endregion //ChaptersCount
  89:  
  90:     #region Title
  91:     private string _title;
  92:  
  93:     [DataMember]
  94:     public string Title
  95:     {
  96:         get
  97:         {
  98:             return this._title;
  99:         }
 100:         set
 101:         {
 102:             if (this._title != value)
 103:             {
 104:                 this._title = value;
 105:             }
 106:         }
 107:     }
 108:     #endregion //Title
 109:  
 110:     #region ReleaseDate
 111:     private string _releaseDate;
 112:  
 113:     [DataMember]
 114:     public string ReleaseDate
 115:     {
 116:         get
 117:         {
 118:             return this._releaseDate;
 119:         }
 120:         set
 121:         {
 122:             if (this._releaseDate != value)
 123:             {
 124:                 this._releaseDate = value;
 125:                 //this.OnPropertyChanged("ReleaseDate");
 126:             }
 127:         }
 128:     }
 129:     #endregion //ReleaseDate
 130:  
 131:     #region UnitPrice
 132:     private Decimal _unitPrice;
 133:  
 134:     [DataMember]
 135:     public Decimal UnitPrice
 136:     {
 137:         get
 138:         {
 139:             return this._unitPrice;
 140:         }
 141:         set
 142:         {
 143:             if (this._unitPrice != value)
 144:             {
 145:                 this._unitPrice = value;
 146:                 //this.OnPropertyChanged("UnitPrice");
 147:             }
 148:         }
 149:     }
 150:     #endregion //UnitPrice
 151:  
 152:     #region Url
 153:     private string _url;
 154:  
 155:     [DataMember]
 156:     public string Url
 157:     {
 158:         get
 159:         {
 160:             return this._url;
 161:         }
 162:         set
 163:         {
 164:             if (this._url != value)
 165:             {
 166:                 this._url = value;
 168:             }
 169:         }
 170:     }
 171:     #endregion //Url
 172:  
 173:     #region Chapters
 174:     private IEnumerable<Chapter> _chapters;
 175:  
 176:     [DataMember]
 177:     public IEnumerable<Chapter> Chapters
 178:     {
 179:         get
 180:         {
 181:             return this._chapters;
 182:         }
 183:         set
 184:         {
 185:             if (this._chapters == null)
 186:             {
 187:                 this._chapters = new ObservableCollection<Chapter>();
 188:             }
 189:             if (this._chapters != value)
 190:             {
 191:                 this._chapters = value;
 193:             }
 194:         }
 195:     }
 196:     #endregion //Chapters
 197:  
 198:     #endregion //Properties
 199:  
 200: }
 201: #endregion //Book

Create a static method GetBooks that returns IEnumerable<Book>:

   1: private static IEnumerable<Book> GetBooks()
   2: {
   3:     //Return books as IEnumerable
   4:     if (doc != null)
   5:     {
   6:  
   7:         var dataSource = (from d in doc.Descendants("book")
   8:                           where (d.Descendants("chapter") != null || d.Descendants("chapter").Count() >= 0)
   9:                           select new Book
  10:                           {
  11:                               Author = d.Attribute("Author").Value,
  12:                               Title = d.Attribute("Title").Value,
  13:                               UnitPrice = d.Attribute("UnitPrice").GetDecimalValue(),
  14:                               Url = d.Attribute("Url").Value,
  15:                               ReleaseDate = d.Attribute("ReleaseDate").Value,
  16:                               ChaptersCount = d.Descendants("chapter").Count()
  17:                           });
  18:  
  19:         return dataSource.ToList();
  20:     }
  21:     return null;
  22: }

 

 

Create an operation contract that calls the GetBooks method and return the result.

   1: [OperationContract]
   2:  public IEnumerable<Book> GetBookDataSource()
   3:  {
   4:  
   5:      return GetBooks();
   6:  
   7:  }

 

 Create a static method GetChapters(string url) that returns IEnumerable<Chapter> for a Book with a specific URL:

   1: private static IEnumerable<Chapter> GetChapters(string url)
   2: {
   3:     //Return the chapters for a specified book
   4:     if (doc != null)
   5:     {
   6:         var chapters = (from d in doc.Descendants("book")
   7:                   where
   8:                       (d.Attribute("Url").Value.Equals(url) && d.Descendants("chapter") != null &&
   9:                        d.Descendants("chapter").Count() > 0)
  10:                   select GetChapters(d));
  11:  
  12:         return chapters.First().ToList();
  13:     }
  14:  
  15:     return null;
  16: }

 

Chapters are created in the method GetChapters(XElement) :

   1: private static IEnumerable<Chapter> GetChapters(XElement parent)
   2: {
   3:     return (from d in parent.Descendants("chapter")
   4:             select new Chapter
   5:             {
   6:                 Title = d.Attribute("Title").Value
   7:             }).ToList<Chapter>();
   8: }

 

Create an operation contract that calls the GetBookChapters method and return the result.

   1: [OperationContract]
   2: public IEnumerable<Chapter> GetBookChapters(string url)
   3: {
   4:     return GetChapters(url);
   5: }

 

Client implementation:

Create in the Silverlight application a  MVVM using for data ObservabeCollection<T>

ViewModel using an ObservableCollection<T>:

Create a BookDataViewModel class:

   1: #region BookDataViewModel
   2: public class BookDataViewModel : BaseViewModel
   3: {
   4:     #region Memrber Variables
   5:  
   6:     private BookDataSourceServiceClient _client;
   7:  
   8:     public event EventHandler<EventArgs> DataLoaded;
   9:  
  10:     #endregion //Memrber Variables
  11:  
  12:     #region Constructors
  13:     public BookDataViewModel()
  14:     {
  15:         _client = new BookDataSourceServiceClient();
  16:         _client.GetBookDataSourceCompleted += new EventHandler<GetBookDataSourceCompletedEventArgs>(ClientGetBookDataSourceCompleted);
  17:         _client.GetBookDataSourceAsync();
  18:     }
  19:     #endregion //Constructors
  20:  
  21:     #region Properties
  22:  
  23:     #region Books
  24:     private ObservableCollection<Book> _books;
  25:     public ObservableCollection<Book> Books
  26:     {
  27:         get
  28:         {
  29:             if (this._books == null)
  30:             {
  31:                 this._books = new ObservableCollection<Book>();
  32:             }
  33:             return this._books;
  34:         }
  35:         set
  36:         {
  37:             this._books = value;
  38:             OnPropertyChanged("Books");
  39:             if (this.DataLoaded != null)
  40:             {
  41:                 EventArgs args = new EventArgs();
  42:  
  43:                 this.DataLoaded(this, args);
  44:             }
  45:  
  46:         }
  47:     }
  48:     #endregion //Books
  49:  
  50:     #region CurrentBook
  51:     /// <summary>
  52:     /// local variable _CurrentBook
  53:     /// </summary>
  54:     private Book _currentBook;
  55:  
  56:     /// <summary>
  57:     /// Identifies the CurrentBook property.
  58:     /// </summary>        
  59:     public Book CurrentBook
  60:     {
  61:         get { return _currentBook; }
  62:         set
  63:         {
  64:             _currentBook = value;
  65:             OnPropertyChanged("CurrentBook");
  66:         }
  67:     }
  68:     #endregion  //CurrentBook
  69:  
  70:     #endregion //Properties
  71:  
  72:  
  73:     #region Methods
  74:  
  75:     #region GetChapters
  76:     public void GetChapters(Book book)
  77:     {
  78:         if (_client == null)
  79:         {
  80:             _client = new BookDataSourceServiceClient();
  81:         }
  82:         CurrentBook = book;
  83:  
  84:         _client.GetBookChaptersCompleted -= ClientGetBookChaptersCompleted;
  85:         _client.GetBookChaptersCompleted += new EventHandler<GetBookChaptersCompletedEventArgs>(ClientGetBookChaptersCompleted);
  86:         _client.GetBookChaptersAsync(book.Url);
  87:     }
  88:     #endregion //GetChapters
  89:  
  90:     #endregion //Methods
  91:  
  92:     #region EventHandlers
  93:  
  94:     #region ClientGetBookChaptersCompleted
  95:     void ClientGetBookChaptersCompleted(object sender, GetBookChaptersCompletedEventArgs e)
  96:     {
  97:         if (this.CurrentBook != null)
  98:         {
  99:             this.CurrentBook.Chapters = e.Result;
 100:         }
 101:  
 102:     }
 103:     #endregion //ClientGetBookChaptersCompleted
 104:  
 105:     #region ClientGetBookDataSourceCompleted
 106:     void ClientGetBookDataSourceCompleted(object sender, GetBookDataSourceCompletedEventArgs e)
 107:     {
 108:         this.Books = e.Result;
 109:     }
 110:     #endregion //ClientGetBookDataSourceCompleted
 111:  
 112:     #endregion //EventHandlers
 113:  
 114: }
 115: #endregion //BookDataViewModel

 

Create a view with XamTree, named XamTreeLazyLoadingView:

   1: <ig:XamTree x:Name="dataTree" Width="370" ActiveItemChanged="DataTreeActiveItemChanged" ItemsSource="{Binding Books}" BorderThickness="0,0,0,0" Background="#FFFFFFFF" HorizontalAlignment="Left" ItemContainerStyle="{StaticResource XamTreeItemStyle}">
   2:     <ig:XamTree.HierarchicalItemTemplate>
   3:         <ig:HierarchicalDataTemplate ItemsSource="{Binding Chapters}">
   4:             <ig:HierarchicalDataTemplate.ItemTemplate>
   5:                 <DataTemplate>
   6:                     <StackPanel Orientation="Horizontal">
   7:                         <Image Source="../Images/PageIcon.png" Width="16" Height="16" />
   8:                         <TextBlock Text="{Binding Title}" Foreground="Gray" />
   9:                     </StackPanel>
  10:                 </DataTemplate>
  11:             </ig:HierarchicalDataTemplate.ItemTemplate>
  12:             <DataTemplate>
  13:                 <StackPanel Orientation="Horizontal" MouseLeftButtonUp="StackPanel_MouseLeftButtonUp">
  14:                     <Image Source="../Images/BookIcon.png" Width="16" Height="16"  />
  15:                     <TextBlock Text="{Binding Title}" FontWeight="Bold"/>
  16:                 </StackPanel>
  17:             </DataTemplate>
  18:         </ig:HierarchicalDataTemplate>
  19:     </ig:XamTree.HierarchicalItemTemplate>
  20: </ig:XamTree>

 

Implement an event handler where when is raised double click over XamTreeItem in the root level (Book) a service client returns book chapters as IEnumerable<Chapter>

   1: #region Constructors
   2: public XamTreeLazyLoadingView()
   3: {
   4:  
   5:     this._viewModel = Application.Current.Resources["BookDataViewModel"] as BookDataViewModel;
   6:     InitializeComponent();
   7:     if (this._viewModel != null)
   8:     {
   9:         this._viewModel.DataLoaded += new EventHandler<EventArgs>(ViewModelDataLoaded);
  10:         this.DataContext = this._viewModel;
  11:     }
  12:  
  13: }
  14: #endregion //Constructors
  15:  
  16: ...
  17:  
  18: #region ViewModelDataLoaded
  19: void ViewModelDataLoaded(object sender, EventArgs e)
  20: {
  21:     this.DataContext = this._viewModel;
  22: }
  23: #endregion //ViewModelDataLoaded
  24:  
  25: ....
  26:  
  27: #region StackPanel_MouseLeftButtonUp
  28: private void StackPanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
  29: {
  30:     var book = ((FrameworkElement)sender).DataContext as BookDataServiceReference.Book;
  31:     if (book != null && (book.Chapters == null || book.Chapters.Count == 0))
  32:     {
  33:         if (!this.IgnoreMouseDown && (DateTime.Now.Ticks - this._doubleClickTimeStamp) <= 4000000)
  34:         {
  35:             this._viewModel.GetChapters(book);
  36:             this.IgnoreMouseDown = true;
  37:         }
  38:  
  39:         if (!this.IgnoreMouseDown)
  40:         {
  41:             this._doubleClickTimeStamp = DateTime.Now.Ticks;
  42:         }
  43:  
  44:         this.IgnoreMouseDown = false;
  45:  
  46:     }
  47:  
  48: }
  49: #endregion //StackPanel_MouseLeftButtonUp

Run the application:

A list with books is loaded

  

Select a specific book: Data for this book is visualized in the form in the right side of the Silverlight application.

 

Double click specific item. An event handler calls from WCF service all chapters for the selected book and expand this node.

  

 

Implementation using a VirtualCollection:

WCF Service implementation:

Implement a WCF methods and operation contracts that returns a range of books

   1: private static IEnumerable<Book> GetBooks(int startIndex, int itemsCount)
   2: {
   3:     //Return range of books as IEnumerable
   4:     if (doc != null)
   5:     {
   6:  
   7:         var dataSource = (from d in doc.Descendants("book")
   8:                           where (d.Descendants("chapter") != null || d.Descendants("chapter").Count() >= 0)
   9:                           select new Book
  10:                           {
  11:                               Author = d.Attribute("Author").Value,
  12:                               Title = d.Attribute("Title").Value,
  13:                               UnitPrice = d.Attribute("UnitPrice").GetDecimalValue(),
  14:                               Url = d.Attribute("Url").Value,
  15:                               ReleaseDate = d.Attribute("ReleaseDate").Value,
  16:                               ChaptersCount = d.Descendants("chapter").Count(),
  17:                           });
  18:  
  19:  
  20:         List<Book> lst = dataSource.ToList();
  21:  
  22:         itemsCount = Math.Min(lst.Count - 1 - startIndex, itemsCount);
  23:  
  24:         return lst.GetRange(startIndex, itemsCount);
  25:     }
  26:     return null;
  27: }

 

   1: [OperationContract(Name = "GetRangeBookDataSource")]
   2: public IEnumerable<Book> GetBookDataSource(int startIndex, int itemsCount)
   3: {
   4:     return GetBooks(startIndex, itemsCount);
   5:  
   6: }

 

Implement a WCF methods and operation contracts  that returns a range of book chapters .

   1: private static IEnumerable<Chapter> GetChapters(string url, int startIndex, int itemsCount)
   2: {
   3:  
   4:  
   5:     //Return the range of chapters for a specified book
   6:     if(doc != null) 
   7:     {
   8:  
   9:  
  10:         var chapters = (from d in doc.Descendants("book")
  11:                         where
  12:                             (d.Attribute("Url").Value.Equals(url) && d.Descendants("chapter") != null &&
  13:                              d.Descendants("chapter").Count() > 0)
  14:                         select GetChapters(d).ToList());
  15:  
  16:         if (chapters.Count() > 0 && chapters.First().Count > 0)
  17:         {
  18:             List<Chapter> lst = chapters.First();
  19:             itemsCount = Math.Min(lst.Count - 1 - startIndex, itemsCount);
  20:             var x1 = new Chapter[itemsCount];
  21:  
  22:             lst.CopyTo(0, x1, startIndex, itemsCount);
  23:  
  24:             return lst;                   
  25:         }
  26:  
  27:  
  28:     }
  29:  
  30:     return null;
  31: }

 

 

 

   1: [OperationContract(Name = "GetRangeBookChapters")]
   2: public IEnumerable<Chapter> GetBookChapters(string url, int startIndex, int itemsCount)
   3: {
   4:     return GetChapters(url, startIndex, itemsCount);
   5: }

  

Client implementation:

Create in the Silverlight application a  MVVM using for data custom collections that inherits VirtualCollection<T>

ViewModel using an VirtualCollection<T>:

Create a BookViewModel class. When receive IEnumerable<T> from WCF service client generation could use several kind of collections, but not a custom collection. One possible solution is to use a separate view model in the client, where to add property for books that is VirtualCollection<T> or its inheritor.

   1: public class BookViewModel : Book
   2: {
   3:     #region Constructors
   4:     public BookViewModel(Book book)
   5:     {
   6:         this.Author = book.Author;
   7:         this.Title = book.Title;
   8:         this.Url = book.Url;
   9:         this.ReleaseDate = book.ReleaseDate;
  10:         this.UnitPrice = book.UnitPrice;
  11:         this.Chapters = book.Chapters;
  12:         this.ChaptersCount = book.ChaptersCount;
  13:     }
  14:     #endregion //Constructors
  15:  
  16:     #region Properties
  17:  
  18:     #region Client
  19:     private BookDataSourceServiceClient _client;
  20:     public BookDataSourceServiceClient Client
  21:     {
  22:         get
  23:         {
  24:             if (this._client == null)
  25:             {
  26:                 this._client = new BookDataSourceServiceClient();
  27:             }
  28:             return this._client;
  29:         }
  30:         set
  31:         {
  32:             this._client = value;
  33:             this.RaisePropertyChanged("Client");
  34:         }
  35:     }
  36:  
  37:     #endregion //Client
  38:  
  39:     #region BookChapters
  40:     /// <summary>
  41:     /// local variable _bookChapters
  42:     /// </summary>
  43:     private BookChaptersData _bookChapters;
  44:  
  45:     /// <summary>
  46:     /// Identifies the Chapters property.
  47:     /// </summary>        
  48:     public BookChaptersData BookChapters
  49:     {
  50:         get
  51:         {
  52:             if (_bookChapters == null)
  53:             {
  54:                 this._bookChapters = new BookChaptersData { Client = this.Client, ItemsCount = this.ChaptersCount, ChapterBook = this };
  55:                 this.RaisePropertyChanged("BookChapters");
  56:             }
  57:             return _bookChapters;
  58:         }
  59:         set
  60:         {
  61:             _bookChapters = value;
  62:             this.RaisePropertyChanged("BookChapters");
  63:         }
  64:     }
  65:  
  66:     #endregion  //BookChapters
  67:  
  68:     #endregion //Properties
  69:  
  70: }

 

Implement BookSourceData: VirtualCollection<BookViewModel>:

   1: #region BookSourceData
   2:  public class BookSourceData : VirtualCollection<BookViewModel>
   3:  {
   4:      #region Member Variables
   5:  
   6:      private int _loadSize = 5;
   7:      private int _startIndex = 0;
   8:  
   9:      #endregion //Member Variables
  10:  
  11:      #region Constructors
  12:      public BookSourceData()
  13:      {
  14:  
  15:          this.ItemDataRequested += new EventHandler<ItemDataRequestedEventArgs>(BookSourceData_ItemDataRequested);
  16:          this.LoadSize = _loadSize;
  17:          //this.AnticipatedCount = _itemsCount;
  18:      }
  19:      #endregion //Constructors
  20:  
  21:      #region Properties
  22:  
  23:      #region ItemsCount
  24:      /// <summary>
  25:      /// local variable _ItemsCount
  26:      /// </summary>
  27:      private int _itemsCount = 5;
  28:  
  29:      /// <summary>
  30:      /// Identifies the ItemsCount property.
  31:      /// </summary>        
  32:      public int ItemsCount
  33:      {
  34:          get { return _itemsCount; }
  35:          set
  36:          {
  37:              _itemsCount = value;
  38:              this.AnticipatedCount = _itemsCount;
  39:              this.LoadSize = _itemsCount;
  40:              OnPropertyChanged("ItemsCount");
  41:          }
  42:      }
  43:      #endregion  //ItemsCount
  44:  
  45:  
  46:      #region Client
  47:      private BookDataSourceServiceClient _client;
  48:      public BookDataSourceServiceClient Client
  49:      {
  50:          get
  51:          {
  52:              if (this._client == null)
  53:              {
  54:                  this._client = new BookDataSourceServiceClient();
  55:                  this._client.GetRangeBookDataSourceCompleted += new EventHandler<GetRangeBookDataSourceCompletedEventArgs>(ClientGetRangeBookDataSourceCompleted);
  56:              }
  57:              return this._client;
  58:          }
  59:          set
  60:          {
  61:              if (_client != null)
  62:              {
  63:                  _client.GetRangeBookDataSourceCompleted -= ClientGetRangeBookDataSourceCompleted;
  64:              }
  65:              this._client = value;
  66:              this._client.GetRangeBookDataSourceCompleted += new EventHandler<GetRangeBookDataSourceCompletedEventArgs>(ClientGetRangeBookDataSourceCompleted);
  67:          }
  68:      }
  69:      #endregion //Client
  70:  
  71:      #endregion //Properties
  72:  
  73:      #region EventHandlers
  74:  
  75:      #region BookSourceData_ItemDataRequested
  76:      void BookSourceData_ItemDataRequested(object sender, ItemDataRequestedEventArgs e)
  77:      {
  78:          this._startIndex = e.StartIndex;
  79:          this._client.GetRangeBookDataSourceAsync(e.StartIndex, e.ItemsCount);
  80:      }
  81:      #endregion //BookSourceData_ItemDataRequested
  82:  
  83:      #region ClientGetRangeBookDataSourceCompleted
  84:      void ClientGetRangeBookDataSourceCompleted(object sender, GetRangeBookDataSourceCompletedEventArgs e)
  85:      {
  86:          if (e.Result != null)
  87:          {
  88:  
  89:              ObservableCollection<BookViewModel> books = new ObservableCollection<BookViewModel>();
  90:              foreach (Book b in e.Result)
  91:              {
  92:                  BookViewModel book = new BookViewModel(b);
  93:                  book.Client = this.Client;
  94:                  books.Add(book);
  95:              }
  96:              this.LoadItems(this._startIndex, books);
  97:          }
  98:  
  99:      }
 100:      #endregion //ClientGetRangeBookDataSourceCompleted
 101:  
 102:      #endregion //EventHandlers
 103:  
 104:  }
 105:  #endregion //BookSourceData

 

Implement BookChaptersData: VirtualCollection<Chapter>

   1: #region BookChaptersData
   2:  
   3: public class BookChaptersData : VirtualCollection<Chapter>
   4: {
   5:     #region Member Variables
   6:  
   7:     private int _loadSize = 10;
   8:     private int _startIndex = 0;
   9:  
  10:     #endregion //Member Variables
  11:  
  12:     #region Constructors
  13:  
  14:     public BookChaptersData()
  15:     {
  16:         this.LoadSize = _loadSize;
  17:         this.AnticipatedCount = _itemsCount;
  18:         this.ItemDataRequested += new EventHandler<ItemDataRequestedEventArgs>(BookChaptersData_ItemDataRequested);
  19:     }
  20:  
  21:     #endregion //Constructors
  22:  
  23:     #region Properties
  24:  
  25:     #region Client
  26:     private BookDataSourceServiceClient _client;
  27:     public BookDataSourceServiceClient Client
  28:     {
  29:         get
  30:         {
  31:             if (this._client == null)
  32:             {
  33:                 this._client = new BookDataSourceServiceClient();
  34:             }
  35:             return this._client;
  36:         }
  37:         set
  38:         {
  39:             this._client = value;
  40:             this.OnPropertyChanged("Client");
  41:         }
  42:     }
  43:     #endregion //Client
  44:  
  45:     #region ChapterBook
  46:     /// <summary>
  47:     /// local variable _ChapterBook
  48:     /// </summary>
  49:     private BookViewModel _chapterBook;
  50:  
  51:     /// <summary>
  52:     /// Identifies the ChapterBook property.
  53:     /// </summary>        
  54:     public BookViewModel ChapterBook
  55:     {
  56:         get { return _chapterBook; }
  57:         set
  58:         {
  59:             _chapterBook = value;
  60:             OnPropertyChanged("ChapterBook");
  61:         }
  62:     }
  63:     #endregion  //ChapterBook
  64:  
  65:     #region ItemsCount
  66:     /// <summary>
  67:     /// local variable _ItemsCount
  68:     /// </summary>
  69:     private int _itemsCount = 5;
  70:  
  71:     /// <summary>
  72:     /// Identifies the ItemsCount property.
  73:     /// </summary>        
  74:     public int ItemsCount
  75:     {
  76:         get { return _itemsCount; }
  77:         set
  78:         {
  79:             _itemsCount = value;
  80:             this.AnticipatedCount = _itemsCount;
  81:             this.LoadSize = _itemsCount;
  82:             OnPropertyChanged("ItemsCount");
  83:         }
  84:     }
  85:     #endregion  //ItemsCount
  86:  
  87:     #endregion //Properties
  88:  
  89:     #region EventHandlers
  90:  
  91:     #region BookChaptersData_ItemDataRequested
  92:     void BookChaptersData_ItemDataRequested(object sender, ItemDataRequestedEventArgs e)
  93:     {
  94:         if (this.ChapterBook == null)
  95:         {
  96:             return;
  97:         }
  98:  
  99:         this._startIndex = e.StartIndex;
 100:         this._client.GetRangeBookChaptersAsync(this.ChapterBook.Url, e.StartIndex, e.ItemsCount);
 101:         this._client.GetRangeBookChaptersCompleted += new EventHandler<GetRangeBookChaptersCompletedEventArgs>(ClientGetRangeBookChaptersCompleted);
 102:     }
 103:     #endregion //BookChaptersData_ItemDataRequested
 104:  
 105:     #region ClientGetRangeBookChaptersCompleted
 106:     void ClientGetRangeBookChaptersCompleted(object sender, GetRangeBookChaptersCompletedEventArgs e)
 107:     {
 108:         if (e.Result != null)
 109:         {
 110:             this.LoadSize = Math.Min(_loadSize, e.Result.Count);
 111:  
 112:             this.LoadItems(this._startIndex, e.Result);
 113:  
 114:         }
 115:     }
 116:     #endregion //ClientGetRangeBookChaptersCompleted
 117:  
 118:     #endregion //EventHandlers
 119: }
 120:  
 121: #endregion //BookChaptersData

 

Create a BookDataVcViewModel class. It will be used as a DataContext for the view.

   1: #region BookDataVcViewModel
   2: public class BookDataVcViewModel : BaseViewModel
   3: {
   4:     #region Member Variables
   5:     private BookDataSourceServiceClient _client;
   6:     private int _booksCount;
   7:     public event EventHandler<EventArgs> DataLoaded;
   8:     #endregion //Member Variables
   9:  
  10:  
  11:     #region Constructors
  12:     public BookDataVcViewModel()
  13:     {
  14:         _client = new BookDataSourceServiceClient();
  15:         _client.GetBookCountAsync();
  16:         _client.GetBookCountCompleted += new EventHandler<GetBookCountCompletedEventArgs>(ClientGetBookCountCompleted);
  17:     }
  18:     #endregion //Constructors
  19:  
  20:     #region Properties
  21:  
  22:     #region Books
  23:     private BookSourceData _books;
  24:     public BookSourceData Books
  25:     {
  26:         get
  27:         {
  28:             if (this._books == null)
  29:             {
  30:  
  31:                 this._books = new BookSourceData();
  32:  
  33:             }
  34:             return this._books;
  35:         }
  36:         set
  37:         {
  38:             this._books = value;
  39:             OnPropertyChanged("Books");
  40:             if (this.DataLoaded != null)
  41:             {
  42:                 EventArgs args = new EventArgs();
  43:  
  44:                 this.DataLoaded(this, args);
  45:             }
  46:  
  47:         }
  48:     }
  49:     #endregion //Books
  50:  
  51:     #region CurrentBook
  52:     /// <summary>
  53:     /// local variable _CurrentBook
  54:     /// </summary>
  55:     private Book _currentBook;
  56:  
  57:     /// <summary>
  58:     /// Identifies the CurrentBook property.
  59:     /// </summary>        
  60:     public Book CurrentBook
  61:     {
  62:         get { return _currentBook; }
  63:         set
  64:         {
  65:             _currentBook = value;
  66:             OnPropertyChanged("CurrentBook");
  67:         }
  68:     }
  69:     #endregion  //CurrentBook
  70:  
  71:     #endregion //Properties
  72:  
  73:     #region EventHandlers
  74:  
  75:     #region ClientGetBookCountCompleted
  76:     void ClientGetBookCountCompleted(object sender, GetBookCountCompletedEventArgs e)
  77:     {
  78:         _booksCount = e.Result;
  79:         this.Books.Client = _client;
  80:         this.Books.ItemsCount = _booksCount;
  81:     }
  82:     #endregion //ClientGetBookCountCompleted
  83:  
  84:     #endregion //EventHandlers
  85:  
  86: }
  87: #endregion //BookDataVcViewModel

 

Create a view with XamTree, named XamTreeVCLazyLoadingView:

Implementation is a similar to the previous one, but simpler, because collections maintain requests to the WCF service.

   1: <ig:XamTree x:Name="dataTree" Width="370" ActiveItemChanged="DataTreeActiveItemChanged" ItemsSource="{Binding Books}" BorderThickness="0,0,0,0" Background="#FFFFFFFF" HorizontalAlignment="Left" ItemContainerStyle="{StaticResource XamTreeItemStyle}">
   2:     <ig:XamTree.HierarchicalItemTemplate>
   3:         <ig:HierarchicalDataTemplate ItemsSource="{Binding BookChapters}">
   4:             <ig:HierarchicalDataTemplate.ItemTemplate>
   5:                 <DataTemplate>
   6:                     <StackPanel Orientation="Horizontal">
   7:                         <Image Source="../Images/PageIcon.png" Width="16" Height="16" />
   8:                         <TextBlock Text="{Binding Title}" Foreground="Gray" />
   9:                     </StackPanel>
  10:                 </DataTemplate>
  11:             </ig:HierarchicalDataTemplate.ItemTemplate>
  12:             <DataTemplate>
  13:                 <StackPanel Orientation="Horizontal" >
  14:                     <Image Source="../Images/BookIcon.png" Width="16" Height="16"  />
  15:                     <TextBlock Text="{Binding Title}" FontWeight="Bold"/>
  16:                 </StackPanel>
  17:             </DataTemplate>
  18:         </ig:HierarchicalDataTemplate>
  19:     </ig:XamTree.HierarchicalItemTemplate>
  20: </ig:XamTree>

 

   1: public partial class XamTreeVcLazyLoadingView : Page
   2: {
   3:     #region Member Variables
   4:  
   5:     private readonly BookDataVcViewModel _viewModel;
   6:  
   7:     #endregion //Member Variables
   8:  
   9:     #region Constructors
  10:  
  11:     public XamTreeVcLazyLoadingView()
  12:     {
  13:         this._viewModel = Application.Current.Resources["BookDataViewModelVC"] as BookDataVcViewModel;
  14:         InitializeComponent();
  15:         if (this._viewModel != null)
  16:         {
  17:             _viewModel.DataLoaded += new EventHandler<EventArgs>(ViewModelDataLoaded);
  18:             this.DataContext = this._viewModel;                
  19:         }
  20:  
  21:     }
  22:  
  23:     #endregion //Constructors
  24:  
  25:     #region Methods
  26:  
  27:     #region OnNavigatedTo
  28:     // Executes when the user navigates to this page.
  29:     protected override void OnNavigatedTo(NavigationEventArgs e)
  30:     {
  31:     }
  32:     #endregion //OnNavigatedTo
  33:  
  34:     #endregion //Methods
  35:  
  36:     #region EventHandlers
  37:  
  38:     #region ViewModelDataLoaded
  39:     void ViewModelDataLoaded(object sender, EventArgs e)
  40:     {
  41:         this.DataContext = this._viewModel;
  42:     }
  43:     #endregion //ViewModelDataLoaded
  44:  
  45:  
  46:     #region DataTreeActiveItemChanged
  47:     private void DataTreeActiveItemChanged(object sender, EventArgs e)
  48:     {
  49:         var book = dataTree.ActiveItem as BookViewModel; // BookDataServiceReference.Book;
  50:         if (book != null)
  51:         {
  52:             if (bookGrid.Visibility == System.Windows.Visibility.Collapsed)
  53:             {
  54:                 bookGrid.Visibility = System.Windows.Visibility.Visible;
  55:             }
  56:  
  57:             bookGrid.DataContext = dataTree.ActiveItem;
  58:         }
  59:  
  60:     }
  61:     #endregion //DataTreeActiveItemChanged
  62:  
  63:  
  64:     #endregion //EventHandlers
  65:  
  66: }

 

Run the application:

Select the page with implementation where is used a VirtualCollection:|
List with books is loaded. BookChaptersData collections are created for each BookViewModel. Now it is possible to understand witch books has information about chapters (books with chapters data have visible expanders).

Select a specific book: Data for this book is visualized in the form in the right side of the Silverlight application.

Double click specific item. When a BookChaptersData collection has expanded there has raised an event ItemDataRequiested from a virtual collection. BookChaptersData implementation gets from the WCF service required in this moment. The selected book node has expanded and user could see all book chapters.

Sample code could be downloaded here:

For implementation with a VirtualCollection you need to download the latest SR for NetAdvantage 2010 Vol.3.
If you have a trial version please ask Infragistics Developer Support for the latest Service Release for NetAdvantage 2010 Vol.3.