Intersoft ClientUI 8 > ClientUI Controls > Control Library > Navigation Controls Overview > UXBreadCrumb > UXBreadCrumb How-to Topics > How-to: Enable Load on Demand for UXBreadCrumb |
This example demonstrates how to configure UXBreadCrumb to enable load-on-demand feature.
This example uses project files from example How-to: Use UXBreadCrumb for Page Navigation.
1. Modify Sitemap.xml to match the following:
Example Title |
Copy Code
|
---|---|
<Sitemap> <SitemapData> <DisplayName>Home</DisplayName> <Path>/</Path> <Children> <SitemapData> <DisplayName>ClientUI</DisplayName> <Path>/ClientUI</Path> <Children> <SitemapData> <DisplayName>Scheduling Controls</DisplayName> <Path>/ClientUI/Scheduling Controls</Path> <Children> <SitemapData> <DisplayName>Schedule View</DisplayName> <Path>/ClientUI/Scheduling Controls/Schedule View</Path> </SitemapData> </Children> </SitemapData> <SitemapData> <DisplayName>Ribbon Controls</DisplayName> <Path>/ClientUI/Ribbon Controls</Path> <Children> <SitemapData> <DisplayName>Ribbon</DisplayName> <Path>/ClientUI/Ribbon Controls/Ribbon</Path> </SitemapData> </Children> </SitemapData> </Children> </SitemapData> <SitemapData> <DisplayName>ASP.NET</DisplayName> <Path>/ASP.NET</Path> </SitemapData> </Children> </SitemapData> </Sitemap> |
Sitemap.xml must be modified as such that the root URI must be equal to the PathSeparator, by default PathSeparator is set to '/'. UXBreadCrumb uses the URI information and separates them using PathSeparator to generate the items, therefore it is crucial that the sitemap structure must be well-formed. |
2. Modify the ViewModel to match the following:
BreadCrumbViewModel.cs |
Copy Code
|
---|---|
using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Threading; using System.Windows; using System.Windows.Resources; using System.Xml.Linq; using Intersoft.Client.Framework; using Intersoft.Client.UI.Navigation; namespace BreadCrumbLoadOnDemand.ViewModels { public class BreadCrumbViewModel : ViewModelBase { #region Fields private object _expandedItem; private string _greetingText; private object _processedItem; private object _queryResult; private string _queryText; private Uri _resolvedUri; #endregion #region Constructor public BreadCrumbViewModel() { //Get the StreamResourceInfo for the XML file StreamResourceInfo resourceStream = Application.GetResourceStream(new Uri("/BreadCrumbLoadOnDemand;Component/XML/Sitemap.xml", UriKind.Relative)); if (resourceStream != null) { XDocument xdoc = XDocument.Load(resourceStream.Stream, LoadOptions.SetBaseUri); //Use the utility method to generate sitemap from XML Sitemap = Intersoft.Client.UI.Navigation.UXBreadCrumb.GenerateSitemap(xdoc, "DisplayName", "Path", "Children", null); } } #endregion #region BackgroundWorker public BackgroundWorker BackgroundWorker { get; set; } void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; if (worker != null) { SitemapData obj = e.Argument as SitemapData; if (obj != null) { var dataThread = new Thread(() => { //This is to simulate request, do not use in production code Thread.Sleep(500); CrossPlatform.BeginInvoke(new Action(() => { if (!worker.CancellationPending) { this.SearchSitemap(obj); if (this.SitemapData != null && this.SitemapData.Children != null && this.SitemapData.Children.Any()) obj.Children = this.SitemapData.Children; else obj.Children = null; this.ProcessedItem = obj; } })); }); dataThread.Start(); } } } #endregion #region Properties public object ExpandedItem { get { return this._expandedItem; } set { //Destroy current BackgroundWorker to cancel if (this.BackgroundWorker != null) { this.BackgroundWorker.DoWork -= BackgroundWorker_DoWork; this.BackgroundWorker.CancelAsync(); this.BackgroundWorker = null; } if (this._expandedItem != value) { this._expandedItem = value; SitemapData obj = value as SitemapData; if (obj != null) { //Wrap the thread to a BackgroundWorker to support cancellation. this.BackgroundWorker = new BackgroundWorker { WorkerSupportsCancellation = true }; this.BackgroundWorker.DoWork += BackgroundWorker_DoWork; this.BackgroundWorker.RunWorkerAsync(obj); } OnPropertyChanged("ExpandedItem"); } } } public object ProcessedItem { get { return this._processedItem; } set { if (this._processedItem != value) { this._processedItem = value; OnPropertyChanged("ProcessedItem"); } } } public object QueryResult { get { return this._queryResult; } set { if (this._queryResult != value) { this._queryResult = value; OnPropertyChanged("QueryResult"); } } } public string QueryText { get { return this._queryText; } set { if (this._queryText != value) { this._queryText = value; var dataThread = new Thread(() => { //This is to simulate request, do not use in production code Thread.Sleep(500); CrossPlatform.BeginInvoke(new Action(() => { this.QueryResult = this.SearchQueryTextFromSitemap(this._queryText); })); }); dataThread.Start(); } else { this._queryText = value; } OnPropertyChanged("QueryText"); } } public SitemapData ResultSitemapData { get; set; } public IEnumerable<SitemapData> Sitemap { get; set; } public string GreetingText { get { return this._greetingText; } set { if (this._greetingText != value) { this._greetingText = value; OnPropertyChanged("GreetingText"); } } } public Uri ResolvedUri { get { return this._resolvedUri; } set { if (this._resolvedUri != value) { this._resolvedUri = value; OnPropertyChanged("ResolvedUri"); //After ResolvedUri has been set, find the appropriate display name to be displayed to GreetingText this.FindDisplayNameRecursive(this.Sitemap); } } } public SitemapData SitemapData { get; set; } #endregion #region Methods private void FindDisplayNameRecursive(IEnumerable sitemap) { if (sitemap != null) { foreach (SitemapData sitemapData in sitemap) { if (sitemapData.NavigateUri == this.ResolvedUri) { this.GreetingText = "You're now at: " + sitemapData.DisplayName; break; } if (sitemapData.Children != null) this.FindDisplayNameRecursive(sitemapData.Children); } } } private void SearchSitemap(SitemapData sitemapData) { this.RecursiveSitemapSearch(sitemapData, this.Sitemap); } private void RecursiveSitemapSearch(SitemapData sitemapData, IEnumerable<SitemapData> collection) { foreach (SitemapData data in collection) { if (sitemapData.NavigateUri == data.NavigateUri) this.SitemapData = data; if (data.Children != null) this.RecursiveSitemapSearch(sitemapData, data.Children); } } private object SearchQueryTextFromSitemap(string queryText) { ObservableCollection<SitemapData> collection = new ObservableCollection<SitemapData>(); this.SearchQueryTextRecursive(queryText, this.Sitemap, collection); return collection; } private void SearchQueryTextRecursive(string queryText, IEnumerable<SitemapData> collection, ObservableCollection<SitemapData> returnCollection) { foreach (SitemapData data in collection) { if (data.NavigateUri != null && data.NavigateUri.OriginalString.StartsWith(queryText)) returnCollection.Add(data); if (data.Children != null) this.SearchQueryTextRecursive(queryText, data.Children, returnCollection); } } #endregion } } |
The load on demand feature mechanism works as follows:
In view mode, when user clicks the dropdown of a UXBreadCrumbItem, the ExpandedItem property is set by the user in the view model, and is returned to the UXBreadCrumb control using the ProcessedItem property.
In editing mode, when user types a query text, it is bound to QueryText property. When QueryText raises the OnPropertyChanged event, the view model searches the sitemap for the matching navigate URI, and returns the result to UXBreadCrumb control via QueryResult property, which will be displayed on suggestion dropdown list.
Consider a scenario when the drop down is opened and user hover to several items in a rapid manner, therefore it is recommended to wrap the request in a BackgroundWorker as shown above to elegantly cancel the previous requests and only process the last item request.
3. Open Customers.xaml in folder Views, and modify to the following code:
Customers.xaml |
Copy Code
|
---|---|
<Intersoft:UXPage xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:Intersoft="http://intersoft.clientui.com/schemas" xmlns:ViewModels="clr-namespace:BreadCrumbLoadOnDemand.ViewModels" x:Class="BreadCrumbLoadOnDemand.Customers" Title="Customers Page" d:DesignWidth="800" d:DesignHeight="600" Style="{StaticResource CommonPageStyle}"> <Intersoft:UXPage.DataContext> <ViewModels:BreadCrumbViewModel /> </Intersoft:UXPage.DataContext> <Grid x:Name="LayoutRoot"> <Grid.Background> <ImageBrush AlignmentY="Bottom" AlignmentX="Right" Stretch="None" Opacity="0.5" ImageSource="../Assets/Images/CustomersFolderLarge.png"> <ImageBrush.Transform> <TranslateTransform X="40" Y="40" /> </ImageBrush.Transform> </ImageBrush> </Grid.Background> <Intersoft:DockPanel Margin="10" FillChildMode="Custom"> <Intersoft:StylishLabel Content="Customers Page" Intersoft:DockPanel.Dock="Top" Style="{StaticResource PageHeaderStyle}" /> <StackPanel Intersoft:DockPanel.IsFillElement="True"> <Intersoft:UXBreadCrumb Sitemap="{Binding Sitemap}" DisplayMemberPath="DisplayName" NavigateUriMemberPath="NavigateUri" CollectionMemberPath="Children" TargetFrame="MainContentFrame" IsLoadOnDemand="True" ExpandedItem="{Binding ExpandedItem, Mode=TwoWay}" ProcessedItem="{Binding ProcessedItem, Mode=TwoWay}" QueryText="{Binding QueryText, Mode=TwoWay}" QueryResult="{Binding QueryResult, Mode=TwoWay}" RootName="Home" RootUri="/" /> <Intersoft:UXFrame x:Name="MainContentFrame" JournalOwnership="OwnsJournal" Source="/ClientUI/Scheduling Controls/Schedule View" > <Intersoft:UXFrame.UriMapper> <Intersoft:UriMapper> <Intersoft:UriMapping Uri="/{page}" MappedUri="/Views/TestPage.xaml?uri={page}" /> </Intersoft:UriMapper> </Intersoft:UXFrame.UriMapper> </Intersoft:UXFrame> </StackPanel> </Intersoft:DockPanel> </Grid> </Intersoft:UXPage> |
Note that ExpandedItem, ProcessedItem, QueryText, QueryResult, RootName, and RootUri is set to support the load on demand scenario. The UXFrame Source and UriMapper is also updated accordingly.
4. Run the project and navigate to Customers page.