Implementing the ISupportIncrementalLoading interface in a Windows Store app
The ISupportIncrementalLoading interface allows to easily create a collection in which data is loaded incrementally, when a user is about to the end of the items available on the user interface. Using it, we can obtain a fast & fluid scrolling while loading an huge set of records.
First of all, we need a class that inherits from ObservableCollection and implements the ISupportIncrementalLoading interface:
public interface IIncrementalSource<T>
{
Task<IEnumerable<T>> GetPagedItems(int pageIndex, int pageSize);
}
public class IncrementalLoadingCollection<T, I> : ObservableCollection<I>,
ISupportIncrementalLoading
where T : IIncrementalSource<I>, new()
{
private T source;
private int itemsPerPage;
private bool hasMoreItems;
private int currentPage;
public IncrementalLoadingCollection(int itemsPerPage = 20)
{
this.source = new T();
this.itemsPerPage = itemsPerPage;
this.hasMoreItems = true;
}
public bool HasMoreItems
{
get { return hasMoreItems; }
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
var dispatcher = Window.Current.Dispatcher;
return Task.Run<LoadMoreItemsResult>(
async () =>
{
uint resultCount = 0;
var result = await source.GetPagedItems(currentPage++, itemsPerPage);
if (result == null || result.Count() == 0)
{
hasMoreItems = false;
}
else
{
resultCount = (uint)result.Count();
await dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() =>
{
foreach (I item in result)
this.Add(item);
});
}
return new LoadMoreItemsResult() { Count = resultCount };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
}
The ISupportIncrementalLoading interface defines a property and a method:
- HasMoreItems is used to determine if the collection has more available items;
- LoadMoreItemsAsync is the method that actually performs the incremental loading. This method is invoked every time the control the collection is attached to needs more items to show. For example, if we are using a GridView, when the scroll reaches the edge of the screen, after having checked the HasMoreItems property, it calls the LoadMoreItemsAsync method and await for the end of the operation.
At line 35, LoadMoreItemsAsync calls the GetPagedItems method defined in the IIncrementalSource interface. We need to create a class that implements it and provides the items based on page index and page size. If this method returns a null or 0-length collection, we assume that no more items are available. Otherwise, we add the items to the ObsersevableCollection, so that the CollectionChanged event will be raised to update the user interface.
Let’s see how to use these classes. Suppose to have a Person class:
public class Person
{
public string Name { get; set; }
}
In order to incrementally load a collection of this object, we define the following class:
public class PersonSource : IIncrementalSource<Person>
{
private List<Person> persons;
public PersonSource()
{
persons = new List<Person>();
for (int i = 0; i < 1024; i++)
{
var p = new Person { Name = "Person " + i };
persons.Add(p);
}
}
public async Task<IEnumerable<Person>> GetPagedItems(int pageIndex, int pageSize)
{
return await Task.Run<IEnumerable<Person>>(() =>
{
var result = (from p in persons
select p).Skip(pageIndex * pageSize).Take(pageSize);
return result;
});
}
}
In this simple example, we create a list of Person in the constructor and, in the GetPagedItems method, we return portions of it according to the values of pageIndex and pageSize parameters. In a real scenario, of course, the GetPagedItems will tipically call an external service to retrieve data.
Now the only thing left to do is to create the IncrementalLoadingCollection and assign it to the ItemsSource property of the GridView:
var collection = new IncrementalLoadingCollection<PersonSource, Person>(); gridView.ItemsSource = collection;
Note that also the ListView control can be bound to collections that implements the ISupportIncrementalLoading interface.
Http Message Handlers with the new version of Azure Mobile Services client for Windows Store apps
Some times ago in this blog we talked about how to use Service filters with Azure Mobile Services client for Windows Store apps. With the new version of the client (0.3.3 RC at the time of writing), the Service Filters mechanism has been replaced by Http Message Handlers, that are part of HttpClient. So, now we can use a standard class instead of a specific one.
Converting a service filter to a message handler is straightforward. The Trace filter that has been shown in these pages becomes the following:
public class TraceHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
Debug.WriteLine("MOBILE SERVICE REQUEST:");
Debug.WriteLine("Uri: " + request.RequestUri);
Debug.WriteLine("Method: " + request.Method);
if (request.Content != null)
{
var content = await request.Content.ReadAsStringAsync();
Debug.WriteLineIf(!string.IsNullOrWhiteSpace(content),
"Content: " + content);
}
Debug.WriteLine("======");
// Sends the actual request to the Mobile Service.
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
In order to add this handler to the execution pipeline, we need to use a new overload of the MobileServiceClient constructor:
public static MobileServiceClient MobileService = new MobileServiceClient(
"mobile_service_url",
"mobile_service_application_key",
new TraceHandler()
);
The third parameter of the constructor accepts a param array of HttpMessageHandler, so we can easily add as many handlers as we want.
EnumToVisibilityConverter for Windows Store apps
Suppose that, in our Windows Store app, we have an Enum like the following:
public enum ResponseState
{
Valid,
Invalid,
NotFound
}
If we’re using the MVVM pattern and we have a ViewModel with a property of that Enum type, we could need to show certain parts of the UI depending on its value. In this case, the simplest solution is to use a converter:
public class EnumToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null || parameter == null || !(value is Enum))
return Visibility.Collapsed;
var currentState = value.ToString();
var stateStrings = parameter.ToString();
var found = false;
foreach (var state in stateStrings.Split(','))
{
found = (currentState == state.Trim());
if (found)
break;
}
return found ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter,
string language)
{
throw new NotImplementedException();
}
}
The converter will return Visibility.Visible if the parameter argument contains a string equal to value, Visible.Collapsed otherwise.
So, if we want to alternatively show Grid objects depending on the value of a ViewModel property named CurrentResponse (of ResponseState enum type), we can write something like this:
<Grid Visibility="{Binding CurrentResponse,
Converter={StaticResource EnumToVisibilityConverter},
ConverterParameter='Valid'}">
<TextBlock Text="Valid"></TextBlock>
</Grid>
<Grid Visibility="{Binding CurrentResponse,
Converter={StaticResource EnumToVisibilityConverter},
ConverterParameter='Invalid, NotFound'}">
<TextBlock Text="Invalid or NotFound"></TextBlock>
</Grid>
As shown in the example, it is possible to specify more values in the ConverterParameter tag, separated by a comma.
Calling ViewModel methods in response to Page navigation events using MVVM Light in WinRT
The OnNavigatedTo and OnNavigatedFrom methods allow us to execute code when a Page of a Windows Store app is, respectively, loaded or unloaded. However, if we’re using the MVVM pattern, we shouldn’t use these methods directly.
A possible solution, using MVVM Light Toolkit, is to define an interface that must be implemented by every ViewModel that have to handle the load/unload events:
public interface INavigable
{
void Activate(object parameter);
void Deactivate(object parameter);
}
A ViewModel that uses this interface appears like the following:
public class MainViewModel : ViewModelBase, INavigable
{
// ...
public void Activate(object parameter)
{
}
public void Deactivate(object parameter)
{
}
}
Then, we must create a class that inherits from Page and calls the methods defined in the INavigable interface:
public class BindablePage : Page
{
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var navigableViewModel = this.DataContext as INavigable;
if (navigableViewModel != null)
navigableViewModel.Activate(e.Parameter);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
var navigableViewModel = this.DataContext as INavigable;
if (navigableViewModel != null)
navigableViewModel.Deactivate(e.Parameter);
}
}
In the OnNavigatedTo and OnNavigatedFrom methods, we check if the DataContext implements the INavigable interface: in this case, we call its Activate or Deactivate methods, passing the corresponding parameter to them.
Finally, we need to modify our View in order to use BindablePage instead of the standard Page:
<common:BindablePage
x:Class="App1.MainPage"
xmlns:common="using:App1.Common"
...>
</common:BindablePage>
And, of course, we need to modify the code-behind too, changing the base class from which MainPage inherits.
If we’re using the LayoutAwarePage class, we can follow the same solution, but is this case we can override the LoadState and SaveState methods, so we can also save and restore state using the ViewModel:
public interface INavigable
{
void Activate(object parameter, Dictionary<string, object> state);
void Deactivate(Dictionary<string, object> state);
}
public class MainViewModel : ViewModelBase, INavigable
{
// ...
public void Activate(object parameter, Dictionary<string, object> state)
{
}
public void Deactivate(Dictionary<string, object> state)
{
}
}
public class BindablePage : LayoutAwarePage
{
protected override void LoadState(object navigationParameter,
Dictionary<string, object> pageState)
{
base.LoadState(navigationParameter, pageState);
var navigableViewModel = this.DataContext as INavigable;
if (navigableViewModel != null)
navigableViewModel.Activate(navigationParameter, pageState);
}
protected override void SaveState(Dictionary<string, object> pageState)
{
base.SaveState(pageState);
var navigableViewModel = this.DataContext as INavigable;
if (navigableViewModel != null)
navigableViewModel.Deactivate(pageState);
}
}
Remember always to change the opening tag of every XAML Page, as described earlier, in order to use the new BindablePage and its support to activation/deactivation through ViewModel.
Adding search recommendations to Windows Store apps
Search recommendations are a special kind of suggestions that can include also an image. They are used, for example, in the Store app:
While search suggestions start a normal search, using the selected query, recommendations tipically bring directly to the item detail page.
We can add this feature to our Windows Store apps too. We simply need to add recommendations in the SuggestionRequest event of SearchPane object, just as we do for search suggestions:
protected override void OnWindowCreated(WindowCreatedEventArgs args)
{
SearchPane.GetForCurrentView().SuggestionsRequested += OnSuggestionsRequested;
SearchPane.GetForCurrentView().ResultSuggestionChosen += OnResultSuggestionChosen;
base.OnWindowCreated(args);
}
private void OnSuggestionsRequested(SearchPane sender,
SearchPaneSuggestionsRequestedEventArgs args)
{
var suggestions = args.Request.SearchSuggestionCollection;
suggestions.AppendQuerySuggestion("Suggestion 1");
suggestions.AppendQuerySuggestion("Suggestion 2");
suggestions.AppendQuerySuggestion("Suggestion 3");
suggestions.AppendSearchSeparator("Recommendation");
suggestions.AppendResultSuggestion("DotNetToscana",
detailText: ".NET User Group in Italy",
tag: "dotnettoscana",
image: RandomAccessStreamReference.CreateFromUri(
new Uri("http://www.dotnettoscana.org/logo.png")),
imageAlternateText: "Alternate text");
}
private void OnResultSuggestionChosen(SearchPane sender,
SearchPaneResultSuggestionChosenEventArgs args)
{
// args.Tag contains the tag of the selected recommendations.
// You can use this value to directly navigate to the item detail page.
var selectedTag = args.Tag;
// ...
}
To add search recommendations in the OnSuggestionsRequested event handler, we use the AppendResultSuggestion method. We also use the AppendSearchSeparator method to visually separate recommendations from normal suggestions. So, the resulting search pane look like this:
When the user selects a reccomendation, the ResultSuggestionChosen event will fire. We can then read the SearchPaneResultSuggestionChosenEventArgs.Tag property to retrieve the tag value of the item that has been selected: it is equal to the tag argument that we have set in the AppendResultSuggestion method. Using this value, we can for example navigate directly to the item detail page.
Note that a maximum of 5 elements will be shown in the Search pane, including search suggestions, separator and recommendations. So, a typical solution is to provide 3 suggestions, 1 separator and 1 recommendation. In any case, remember what is stated on MSDN, you should use result suggestions instead of query suggestions only to display high-confidence results that take the user directly to the item instead of to a view that shows search results.
How to automatically open the Search pane when typing in a Windows Store app
If you type something while you are in the Start screen of Windows 8, the Search pane will automatically open, with the search text box containing the pressed character. We can obtain the same behavior in our Windows Store apps. We just need to set the ShowOnKeyboardInput of the SearchPage object to true:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Windows.ApplicationModel.Search.SearchPane.GetForCurrentView()
.ShowOnKeyboardInput = true;
}
In this way, all keystrokes are automatically routed to Search pane, so the current view behaves similar to Start screen. However, this happens even if input controls like TextBox have focus: in a such situation, because all pressed characters will be sent to Search pane, the TextBox will remain empty.
So, it’s up to us to enable or disable the ShowOnKeyboardInput property according to the content of the page (fortunately, we can change it every time we need).
StringFormat converter for Windows Store apps
WinRT misses the StringFormat property for data binding. However, we can implement the same behavior using a converter:
public sealed class StringFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return null;
if (parameter == null)
return value;
return string.Format((string)parameter, value);
}
public object ConvertBack(object value, Type targetType, object parameter,
string language)
{
throw new NotImplementedException();
}
}
Its usage is straightforward:
<TextBlock Text="{Binding Name,
Converter={StaticResource StringFormatConverter},
ConverterParameter='Welcome, {0}!'}" />
<TextBlock Text="{Binding Amount,
Converter={StaticResource StringFormatConverter},
ConverterParameter='{}{0:C}'}" />

