Thursday 14 October 2010

MonoTouch & WindowsPhone7: indexed lists

One interesting aspect of building cross-platform apps is how to adopt the 'standard' UI elements so that each app looks 'native'. A basic iPhone/MonoTouch and WindowsPhone7 application was introduced in a previous post. Each app uses the 'default' list representation.

This post is about making those apps easier to use, adding each platform's default "list index" to help select from long, sorted lists. The images below hardly need captions:
  • on the left is the iPhone app with alphabetic index down the right side of the screen
  • on the right is the WindowsPhone7 app showing the alphabet tiles 'in' the list, and the grid that is displayed when you touch one of those tiles

Implementing the two different solutions highlights the difference in the two platforms (referring of course to C#/MonoTouch on the iPhone, and C#/Silverlight on the WindowsPhone7). Get the complete code from github - the main changes for each platform are highlighted below:

iPhone (MonoTouch) UITableView sections

The following code was added to the UITableViewSource subclass to break up the list into sections, identify the section labels and wire up the alphabetic navigation. The GetCell and RowSelected methods also required a minor change.

List<string> sectionTitles;
Dictionary<int, <restaurant>> sectionElements = new Dictionary<int,restaurant>>();
public TableViewSource (List<restaurant> list, MainViewController controller)
{
   this.list = list;
   mvc = controller;
   sectionTitles = (from r in list
            orderby r.StartsWith
            select r.StartsWith).Distinct().ToList();
   foreach (var restaurant in list)
   {   // group elements together into 'alphabet'
      int sectionNumber = sectionTitles.IndexOf(restaurant.Name[0].ToString());
      if (sectionElements.ContainsKey(sectionNumber))
         sectionElements[sectionNumber].Add(restaurant);
      else
         sectionElements.Add(sectionNumber, new List<restaurant> {restaurant});
   }
}
public override int NumberOfSections (UITableView tableView)
{
   return sectionTitles.Count;
}
public override string TitleForHeader (UITableView tableView, int section)
{
   return sectionTitles[section];
}
public override string[] SectionIndexTitles (UITableView tableView)
{
   return sectionTitles.ToArray();
}
public override int RowsInSection (UITableView tableview, int section)
{
   return sectionElements[section].Count(); //list.Count;
}


Windows Phone 7 'quick jump grid'

Although you'll see this kind of navigation in the 'built-in' applications, it isn't actually part of the default SDK. Thankfully Kevin Marshall has posted a quick jump grid sample which has been integrated into the RestGuide app - just add the relevant Assembly References then update the XAML to use the QuickJumpGrid (don't forget to add the relevant xmlns: declarations)

<cc:QuickJumpGrid DataSource="{Binding}"
      IsAlphaNumeric="True"
      Margin="12,0,0,0"
      SelectionChanged="MainListBox_SelectionChanged"
      x:Name="MainListBox"
      OverlayTileBackground="#8CBF26">
    <cc:QuickJumpGrid.QuickJumpGridSelector>
        <local:RestaurantNameSelector />
    </cc:QuickJumpGrid.QuickJumpGridSelector>
    <cc:QuickJumpGrid.ItemTemplate>
        <DataTemplate>
           <StackPanel Margin="0,0,0,17" Width="432">
               <TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
               <TextBlock Text="{Binding Cuisine}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
           </StackPanel>
        </DataTemplate>
    </cc:QuickJumpGrid.ItemTemplate>
</cc:QuickJumpGrid>

and implement this 'helper' class (which is referenced from the XAML)

namespace RestGuide
{
    public class RestaurantNameSelector : IQuickJumpGridSelector
    {
        public Func<object, IComparable> GetGroupBySelector()
        {
            return (p) => ((Restaurant)p).Name.FirstOrDefault();
        }
        public Func<object, string> GetOrderByKeySelector()
        {
            return (p) => ((Restaurant)p).Name;
        }
        public Func<object, string> GetThenByKeySelector()
        {
            return (p) => (string.Empty);
        }
    }
}

No comments:

Post a Comment

Note: only a member of this blog may post a comment.