Log in to like this post! 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: Visual Studio 2010 Silverlight 4 Tools for Visual Studio 2010 Silverlight 4 (Silverlight Tools for Visual Studio 2010). WCF RIA Services are included in this installation. Silverlight 4 Toolkit One of these distributions: NetAdvantage for Silverlight Line of Business 2010 Vol.3 NetAdvantage for .NET 2010 Vol.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 "Super" 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.