Log in to like this post! Creating Windows Phone Applications with Infragistics XamList Damyan Petev / Wednesday, July 4, 2012 I explained why I think list controls are THE grids for mobile phones when introducing the jQuery Mobile version. I believe most would agree they really aim to achieve pretty much the same functionality but within the limited space of a small screen and with the right kind of interaction. Then you could have a look at any of the Infragistics platform product lines… notice every product is packed with powerful grid controls? Now that the mobile platforms got some additions, if you are a Windows Phone developer with big data in mind, you need to look no further: The XamList is here! Just in case you missed it, of course. You can say the last release closed the loop in terms of data handling for our phone control packages and this List control is truly a feat – built for the ground up specifically for Windows Phone. I dare say it’s everything you’d expect – stylish, heavy on data and light on the app as it is virtualized. The latter doesn’t really stop it from maintaining an awesome flick experience either. But that’s not really the functionality I’d like to talk about, those are things expected of a good control and this particular one offers so much more. I’m afraid it will be too much to cover in one blog, but hey – here’s a list: Complete control on looks – you can define templates for anything and everything. Comes complete with a few ready-to-use templates for very quick set up. Built-in support for hierarchy (drill-down) – That’s right! No need for details page, the XamList can do that for you. There’s more – it’s not just details page – frankly, I’m not sure the level of hierarchy supported even has a limit. And you can template every sub-view and you can drill down to different collections of items as well! Fully featured search with the ability to control the scope of fields to be evaluated. Filtering – again complete with the ability to define presets with just a few properties. Presets are defined in groups (sets) with special interaction to let the user activate more than one filter. Sorting with presets for sorts based on different properties. Grouping items with the greatest ease and support for alphabetic grouping, by property value or by date with awesome interactions designed for each type. Custom everything – when all the defaults won’t fit you scenario, create custom sorts with your own comparer, filtering with your filter logic, group items with custom provider. The XamList truly comes packed with features (probably even missed some) and flexibility. On a side note – while you can style nicely, I probably won’t do such a good job, so I’ll rely on the minimalistic defaults and tease you with looks from the Samples Browser (It’s on the market and it’s free!): A Quick Setup Model - I chose to use a very nice OData Service provided by INETA Live that offers a fairly long list of authors/speakers to fit in a list. To use it, of course, in my demo I have a reference to service, which is at http://live.ineta.org/InetaLiveService.svc/ and the following model for the Authors: namespace XamListApp { public class MainViewModel : INotifyPropertyChanged { public MainViewModel() { this.Authors = new DataServiceCollection<LiveAuthor>(); } INETALivePublic context; private DataServiceCollection<LiveAuthor> authors; public DataServiceCollection<LiveAuthor> Authors { get { return authors; } set { authors = value; authors.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(authors_LoadCompleted); NotifyPropertyChanged("Authors"); } } public bool IsDataLoaded { get; private set; } /// <summary> /// Load data. /// </summary> public void LoadData() { context = new INETALivePublic(new Uri("http://live.ineta.org/InetaLiveService.svc/")); Authors = new DataServiceCollection<LiveAuthor>(context); var query = from auths in context.LiveAuthors.Expand("LivePresentations") select auths; Authors.LoadAsync(query); } void authors_LoadCompleted(object sender, LoadCompletedEventArgs e) { if (Authors.Continuation != null) { Authors.LoadNextPartialSetAsync(); } IsDataLoaded = true; } public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (null != handler) { handler(this, new PropertyChangedEventArgs(propertyName)); } } } } namespace XamListApp.INETAService { public partial class LiveAuthor : global::System.ComponentModel.INotifyPropertyChanged { /// <summary> /// Added Image property /// </summary> [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Services.Design", "1.0.0")] public string Image { get { return "/XamListApp;component/speaker.png"; } } } } There is a second (identical in structure) model for the Live Groups feed. As mentioned the list offers a set of standard templates you can use to jump start an app. But you can go even simpler than that – by default the XamList will display the result of the ToString() method for each record and if you data is suitable for this all you need to do is bind the ItemsSource property to it: <ig:XamList x:Name="xamListQuick" ItemsSource="{Binding Authors}"/> Or in our case where the data object is a bit more complex and doesn’t have a proper ToString method, you can easily pick one property to show using the DisplayMemberPath: <ig:XamList x:Name="xamListQuick" ItemsSource="{Binding Authors}" DisplayMemberPath="FirstName"/> Quick Templates Then again so far we got a single name and that doesn’t really tell much. The default templates offer more – a Title Only (the one we have so far), with added Thumbnail and with both thumbnail and description. Notice in the model above I’ve added an additional Image property to the Authors (line 58 and on) so I can demonstrate this: <ig:XamList x:Name="xamListQuick" ItemsSource="{Binding Authors}" ItemLayout="ThumbnailTitleAndDescription"> <ig:XamList.ItemLayoutBindings> <ig:ItemLayoutBinding ItemLayoutProperty="ThumbnailSource" Path="Image"/> <ig:ItemLayoutBinding ItemLayoutProperty="Title" Path="FirstName"/> <ig:ItemLayoutBinding ItemLayoutProperty="Description" Path="LastName"/> </ig:XamList.ItemLayoutBindings> </ig:XamList> And then you can simply activate some of the features like Group speakers by first letter (alphabetic) and allow for them to be sorted with a few extra lines (these go inside the list definition from above): <ig:XamList.Grouping> <ig:AlphabeticGrouping GroupByMemberPath="FirstName" /> </ig:XamList.Grouping> <ig:XamList.SortPresets> <ig:SortPreset DisplayText="First Name" Field="FirstName"/> </ig:XamList.SortPresets> And the end result is both good looking and functional: Another way If those defaults don’t quite fit your tastes you can easily do a super quick template – in my case with authors the image really wasn’t available and not necessary. Then again I have first and last name and I would like to have them in a single row as the title and the Bio as description text: <ig:XamList.ItemsTemplate> <DataTemplate> <StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding FirstName}"/> <TextBlock Text="{Binding LastName}" Margin="8,0,0,0"/> </StackPanel> <TextBlock Text="{Binding Bio}"/> </StackPanel> </DataTemplate> </ig:XamList.ItemsTemplate> Some styling required, but this is the general way you can build the List UI just the way you like it. A slight issue Now that you have an app running you may want to try the built-in search (taping the icon expands a textbox to enter criteria) and get this by accident: And I don’t mean the result (Oh look! It’s our very own Jason Beres!). Take a look at the search criteria – 44?! The things is the search by default will attempt to really find everything you need by searching every property of the list items and as it turns out each LiveAuthor has an ID and Jason’s for example is 44. Assuming you end up in the situation with more properties than it is reasonable to display in the list, you might want to restrict the set of properties to be searched by adding the following to the other settings above: <ig:XamList.ScopePresets> <ig:ScopePreset DisplayText="First Name" Field="FirstName"/> <ig:ScopePreset DisplayText="Last Name" Field="LastName"/> </ig:XamList.ScopePresets> Hierarchy I find the automatic drill-down feature to be one of the most outstanding features of this control. And it’s not so hard too. And as I said – there are templates for a almost everything. Above were showed the built-in ones and the general Items template. Now those wouldn’t be very appropriate to use when you have hierarchy for the sole reason you’d want to display different items when drilling down. If you haven’t already guessed, yes – there are more templates you can set instead. Actually it’s more of a Type Defined templating that anything else. The trick about this type of template definitions is that you can use them for other purposes as well (like defining different layouts for different types of items in your data source – see the sample with books and movies screenshot above). The List has a Global ItemLayouts collection where you can define yours and set a target type for each. In the code below you can see templates for the ‘LiveAuthor’(line 2) and ‘LivePresentation’ (line 53) and they are actually part of one list and keep in mind the presentations are collection too: <ig:XamList.GlobalListItemLayouts> <ig:ListItemLayout TargetTypeName="LiveAuthor"> <ig:ListItemLayout.ListItemTemplate> <ig:ListItemTemplate> <ig:ListItemTemplate.PortraitItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding PresentationCount, Converter={StaticResource countConverter}}" FontSize="22" FontWeight="Bold"></TextBlock> <TextBlock Text="{Binding FirstName}"></TextBlock> <TextBlock Text="{Binding LastName}" Margin="8,0,0,0"></TextBlock> </StackPanel> </DataTemplate> </ig:ListItemTemplate.PortraitItemTemplate> </ig:ListItemTemplate> </ig:ListItemLayout.ListItemTemplate> <ig:ListItemLayout.DetailItemTemplate> <ig:DetailItemTemplate> <ig:DetailItemTemplate.DrillDownSettings> <ig:DrillDownSettings DrillDownTargetPath="LivePresentations" AutomaticDrillDown="False"/> </ig:DetailItemTemplate.DrillDownSettings> <ig:DetailItemTemplate.PortraitItemTemplate> <DataTemplate> <ScrollViewer Height="Auto"> <StackPanel> <Image Height="48" Width="48" Margin="5" HorizontalAlignment="Left" Source="appbar.back.rest.png"> <igBase:Commanding.Command> <ig:XamListCommandSource CommandType="GoBack" EventName="Tap" TargetName="xamList"/> </igBase:Commanding.Command> </Image> <StackPanel Orientation="Horizontal" Margin="0,0,0,5"> <TextBlock Text="{Binding FirstName}" FontFamily="Segoe WP Semibold"></TextBlock> <TextBlock Text="{Binding LastName}" Margin="8,0,0,0" FontFamily="Segoe WP Semibold"></TextBlock> </StackPanel> <TextBlock Text="{Binding Bio}" TextWrapping="Wrap" TextTrimming="WordEllipsis" MaxHeight="350"></TextBlock> <StackPanel Height="48" Orientation="Horizontal"> <igBase:Commanding.Command> <ig:ListItemControlCommandSource CommandType="DrillDown" EventName="Tap"/> </igBase:Commanding.Command> <TextBlock VerticalAlignment="Center"> Presentations </TextBlock> <Image Height="48" Width="48" Source="appbar.next.rest.png"/> </StackPanel> <TextBlock Text="{Binding URL}" TextWrapping="Wrap"></TextBlock> </StackPanel> </ScrollViewer> </DataTemplate> </ig:DetailItemTemplate.PortraitItemTemplate> </ig:DetailItemTemplate> </ig:ListItemLayout.DetailItemTemplate> </ig:ListItemLayout> <ig:ListItemLayout TargetTypeName="LivePresentation" AutomaticDrillDown="False"> <ig:ListItemLayout.ListItemTemplate> <ig:ListItemTemplate> <ig:ListItemTemplate.PortraitItemTemplate> <DataTemplate> <StackPanel Margin="0,0,0,20"> <TextBlock Text="{Binding Title}"></TextBlock> <Line Margin="1"></Line> <TextBlock Text="{Binding Abstract}" TextTrimming="WordEllipsis" Height="60" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}"></TextBlock> </StackPanel> </DataTemplate> </ig:ListItemTemplate.PortraitItemTemplate> </ig:ListItemTemplate> </ig:ListItemLayout.ListItemTemplate> </ig:ListItemLayout> </ig:XamList.GlobalListItemLayouts> Each ListItemLayout in the Global collection has both list (item) and details templates and notice they each have Portrait template defined…that’s because you can specify a different one for the Landscape orientation! Also they have Automatic drill down property (default is true, so you can skip it). The absolute best part is that I have defined two types of collections and yet only one list and I can drill down to the Presentations collection from within each author item thanks to the Drill down Path property. And here’s a small hierarchy flow of what this will do: Commands Guess I missed that one in the listing at the start – simply put the List comes with two default command sources – the List itself which you can see in the above code (lines 27-31) where it is used to create a back button from an Image. This source also provides a set of the most common functionalities for you to use, such as applying a filter, toggling filter and search states and jumping to groups. And then you have the List Item which provides a single, yet critical command – drill down! This is useful if you don’t want or can’t use the automatic drill-down feature and you can use this to add a button that does that inside the list item itself. You can see that in action in the Samples Browser (It’s the “Non Automatic Drilldown”). Custom Grouping So far I’ve showed you an alphabetic grouping in the first example and now if you look at the second it is also grouped. But those are user group names that are not included in the ‘LiveAuthor‘ or their ‘LivePresentations’? This is where all the customization really shines, as I have defined my very own Grouping Provider that makes the connection between Author ID and Groups. However, since authors may have presented in more than one, they will be grouped in the one they have the most or the first if all are equal: public class GroupingProvider : IGroupingProvider { public System.Collections.Generic.List<string> GetAllGroupNames() { var titles = (from grp in App.GroupModel.Groups select grp.Name).ToList(); return titles; } public string GetGroupNameForItem(object item) { var Id = App.ViewModel.Authors.Where(a => a.AuthorID == (int)item).FirstOrDefault().LivePresentations .GroupBy(p => p.GroupID) .Select(p => new { ID = p.Key, num = p.Count() }) .OrderByDescending(p => p.num) .First().ID; var name = App.GroupModel.Groups.Where(x => x.GroupID == Id).FirstOrDefault().Name; return name; } } Note that the GetAllGroupNames method gets called when the user interacts with the group headers which opens up the Group picker (or jumper if you will) menu which is an overlay with all the groups returned by the method and the user can quickly jump to: The grayed-out names are the ones that don’t have items and can’t be navigated to. Here’s a small trick however – if you return ‘null’ in the above method, this menu will only show the names that are actually available. Code Here’s the rest of that list complete with group settings and filter: <ig:XamList x:Name="xamList" ItemsSource="{Binding Authors}" ItemClicked="xamList_ItemClicked" GotFocus="xamList_GotFocus"> <ig:XamList.ScopePresets> <ig:ScopePreset DisplayText="First Name" Field="FirstName"/> <ig:ScopePreset DisplayText="Last Name" Field="LastName"/> </ig:XamList.ScopePresets> <ig:XamList.Grouping> <ig:CustomGrouping GroupingProvider="{StaticResource groupingProvider}" /> </ig:XamList.Grouping> <ig:XamList.FilterSets> <ig:FilterSet Title="Size" DefaultFilterName="All"> <ig:FilterPreset Name="All" DisplayText="All"/> <ig:FilterPreset Name="A" DisplayText="Starst with A" Field="FirstName" Operator="StartsWith" Value="A" /> </ig:FilterSet> </ig:XamList.FilterSets> <!--The global list templates here--> </ig:XamList> Bonus screenshot I mentioned grouping has special interactions for the different types (the custom above is like a value type). Then you have the alphabetic from the first example and taping its headers brings up a very familiar styled menu: Wrapping Up You can tell I was very serious when I said the XamList has quite the feature set – I pretty much skipped some features like filtering (although you can see one filter definition in the Code section above), but remember you can customize that and sorting too? Don’t forget you get search with scope tweaking, amazing grouping with defaults for letters and dates and the best feature ever – the automatic drilldown that can build entire applications with a single list! This control combines a few awesome sides – it can be as simple as it gets with built-in templates and spot-on defaults for all features and then it all can be templated to your liking and customized to your needs. It’s truly a grid-type control that is ready to handle large data and various scenarios. Download the demo project for this blog( two list samples with all of the above described), keep in mind you’ll need Windows Phone SDK and at least trial (it’s free) version of NetAdvantage for Windows Phone. As always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!