Category Archives: Xamarin Forms

OnIdiom and OnPlatform

When developing a Xamarin Forms cross platform application, we’ll have a shared library where (hopefully) the bulk of our code will go, be it C# or XAML.

However we may still need to make platform specific changes to our XAML, whether it’s images, margins, padding etc. Along with the possible differences per platform we also have the added complexity of the idiom, i.e. desktop, phone or tablet (for example) which might require differences, for example maybe we display more controls on a tablet compared to the phone.

OnPlatform

We’re still likely to need to handle different platforms within this shared code, for example in Xamarin Forms TabbedPage we used OnPlatform like this

<ContentPage.Icon>
   <OnPlatform x:TypeArguments="FileImageSource">
      <On Platform="iOS" Value="history.png"/>
   </OnPlatform>
</ContentPage.Icon>

The above simply declares that on iOS the ContentPage Icon is used and is located in the file history.png.

We can handle more than just images, obviously we might look to handle different margins, fonts etc. depending upon the platform being used.

We can declare values for multiple platforms using comma separated values in the Platform attribute, for example

<OnPlatform x:TypeArguments="FileImageSource">
   <On Platform="iOS, Android" Value="history.png"/>
   <On Platform="UWP" Value="uwphistory.png"/>
</OnPlatform>

OnPlatform’s Platform attribute currently supports iOS, Android, UWP, macOS, GTK, Tizen and WPF.

Ofcourse we can simply use Device.RuntimePatform in code-behind to find out what platform the application is running on if preferred.

OnIdiom

Working in much the same was as OnPlatform. OnIdiom is used for handling different XAML or code based upon the current device is classed as a Unsupported, Phone, Tablet, Desktop, TV or Watch.

In XAML we might change the StackLayout orientation, for example

<StackLayout.Orientation>
   <OnIdiom x:TypeArguments="StackOrientation">
      <OnIdiom.Phone>Vertical</OnIdiom.Phone>
      <OnIdiom.Tablet>Horizontal</OnIdiom.Tablet>
   </OnIdiom>
</StackLayout.Orientation>

We can use the Idiom within code-behind by checking the state of Device.Idiom.

Handling orientation in Xamarin Forms

Many years ago I wrote some Windows CE/Pocket PC code. One of the problems was handling different orientation in the same UI, so for example when the device is switched from Portrait to Landscape – in some cases we can use the same layout in both orientations other times and probably in more cases, this isn’t so simple.

Xamarin Forms does not raise orientation events or the likes, instead we will need to override a Pages OnSizeAllocated method, for example

protected override void OnSizeAllocated(double width, double height)
{
   base.OnSizeAllocated(width, height);

   if (width < height)
   {
       // portrait orientation
   }
   else if (height < width)
   {
       // landscape orientation
   }
   else
   {
      // square layout
   }
}

Note: There is also a Device Orientation plugin/nuget package for Xamarin Forms which raises events when orientation changes.

Now we can handle changes to our layout in one of few ways.

We might be to reparent controls, changing layout orientation and/or changes to rows/columns and so on, all in code. This is fairly efficient in terms of reusing the existing controls but is obviously less useful from a design perspective.

We might be able to create the layout in such a way that we can just change orientation of StackLayout’s etc. Hence changing a few properties to reflect the new orientation. This might be more difficult to setup successfully on complex pages.

An alternative method, which is a little wasteful in turns of control creation, but gives us a good design story, is two design two ContentView’s, one for Portrait and one for Landscape. Obviously we’ll need to bind the controls to the same view model properties etc. However, with this solution we can more rapidly get our UI up and running.

<controls:OrientationView>
   <controls:OrientationView.Landscape>
      <views:LandscapeView />
   </controls:OrientationView.Landscape>
   <controls:OrientationView.Portrait>
      <views:PortraitView />
   </controls:OrientationView.Portrait>
</controls:OrientationView>

The OrientationView, might look like this

public class OrientationView : ContentView
{
   public View Landscape { get; set; }
   public View Portrait { get; set; }
   public View Square { get; set; }

   private Page _parentPage;

   protected override void OnParentSet()
   {
      base.OnParentSet();

      _parentPage = this.GetParentPage();
      if (_parentPage != null)
      {
         _parentPage.SizeChanged += PageOnSizeChanged;
      }
   }

   private void PageOnSizeChanged(object sender, EventArgs eventArgs)
   {
      if (_parentPage.Width < _parentPage.Height)
      {
         Content = Portrait ?? Landscape ?? Square;
      }
      else if (_parentPage.Height < _parentPage.Width)
      {
         Content = Landscape ?? Portrait ?? Square;
      }
      else
      {
         Content = Square ?? Portrait ?? Landscape;
      }
   }
}

Here’s the GetParentPage extension method

public static class ViewExtensions
{
   public static Page GetParentPage(this VisualElement element)
   {
      if (element != null)
      {
         var parent = element.Parent;
         while (parent != null)
         {
            if (parent is Page parentPage)
            {
               return parentPage;
            }
            parent = parent.Parent;
         }
      }
      return null;
   }
}

This allows us to basically design totally different UI’s for portrait and landscape, maybe adding extra controls in landscape and removing in portrait. The obvious downside is the duplication of controls.

Creating a Custom Layout for Xamarin Forms

First off, there’s an excellent post on this subject at Creating a Custom Layout. My intention with this post is to just go through my experience creating a layout to organise controls into a grid of squares, i.e. the intention was to layout buttons in a 3×3 matrix for a simple TicTacToe game board.

Getting Started

Your class needs to be derived from Layout or Layout and override the OnMeasure method and the LayoutChildren method.

My class will be named Squared grid and it will require the number of rows and columns that we want to display our View in, so for example here’s the XAML I want to use (excluding namespace and view bindings)

<SquareGrid Rows="3" Columns="3">
   <Button />
   <Button />
   <Button />
   <Button />
   <Button />
   <Button />
   <Button />
   <Button />
   <Button />
</SquareGrid>

So my expectation would be that we will have three rows and three columns of equal width/height and display buttons in each “cell” of the grid.

Note: I’m not going to handle situations where the number of controls doesn’t match the expected rows/columns etc.

Here’s the code (excluding OnMeasure and LayoutChildren for now)

public class SquareGrid : Layout<View>
{
   public static readonly BindableProperty RowsProperty =
      BindableProperty.Create("Rows",
         typeof(int),
         typeof(SquareGrid),
         0,
         propertyChanged: (bindable, oldValue, newValue) =>
            ((SquareGrid)bindable).NativeSizeChanged(),
         validateValue: Validate);

   public static readonly BindableProperty ColumnsProperty = 
      BindableProperty.Create("Columns", 
         typeof(int), 
         typeof(SquareGrid),
         0,
         propertyChanged: (bindable, oldValue, newValue) => 
            ((SquareGrid)bindable).NativeSizeChanged(),
            validateValue: Validate);

   public int Rows
   {
      get => (int)GetValue(RowsProperty);
      set => SetValue(RowsProperty, value);
   }

   public int Columns
   {
      get => (int)GetValue(ColumnsProperty);
      set => SetValue(ColumnsProperty, value);
   }

   private static bool Validate(BindableObject bindable, object value)
   {
      return (int)value >= 0;
   }
}

If you’re used to WPF then, whilst the name BindableProperty don’t exist in WPF (they’re DependencyProperty instead) you’ll probably understand what’s going on, for others reading this – basically we’ve created two properties that are available in XAML, Rows and Columns. We’ve created BindableProperty static’s to handle the storage, binding etc. to these properties. The default values for both Rows and Columns is set to 0 and in both cases, if there’s a change to the property then we call the NativeSizeChanged to alert the layout of changes and also ensure, using validation methods, that the Validate method to check the supplied Rows and Columns properties are within certain bounds (in this case that they’re greater than or equal to 0).

Children

The controls/views (from the example above, the Button objects) become the Children of the layout. In some (maybe most cases) we will want to ask the children for their measurements, i.e. their minimum size requirements and hence would use code like this

foreach (var child in Children)
{
   var childSizeRequest = child.Measure(widthConstraint, heightConstraint);
   // handle any childSizeRequest
}

In our SquareLayout we’re actually going to ignore what the control/view requires and instead force it into the available space, but this gives you an example of how you might handle measurements from the children during the next section, the OnMeasure method.

OnMeasure

Here’s the method signature

protected override SizeRequest OnMeasure(
   double widthConstraint, double heightConstraint)
{
}

The OnMeasure method may be called, depending upon where our SquareGrid is placed and depending upon constraints of any outer layout. For example, if the SquareGrid is within a Grid.Row and that Row height is “*” then OnMeasure is not called. OnMeasure is called when the outer layout is asking “how much space do you require?”. In the case of “*”, we can think of it more like “this is how much space you’ve got”.

In cases where OnMeasure is called, the widthConstraint or heightConstaint might be set to infinity. For example, if the SquareGrid is within a StackLayout, the StackLayout, in portrait orientation, will have not constrain height, hence heightConstraint will by set to infinity. Likewise with a landscape orientation the widthConstraint will be set to infinity. Therefore, when you are calculating the SizeRequest to return from OnMeasure, you will need to handle infinity situations.

This SquareLayout will ignore the child controls measurement requirements and instead will take all the available width or height to create a square of layout space. Hence in a scenario where this layout in within a GridLayout with “Auto” sizing, the SquareGrid will just say it requires an equal width and height based upon the minimum of the two.

Here’s the code

protected override SizeRequest OnMeasure(
   double widthConstraint, 
   double heightConstraint)
{
   var w = double.IsInfinity(widthConstraint) ? 
      double.MaxValue : widthConstraint;
   var h = double.IsInfinity(heightConstraint) ? 
      double.MaxValue : heightConstraint;

   var square = Math.Min(w, h);
   return new SizeRequest(new Size(square, square));
}

LayoutChildren

So OnMeasure is called when the parent wants to ask how much space do you require, and the LayoutChildren (as the name suggests) is when the Layout control is asked to layout it’s children given the x, y, width and height as the bounding rectangle where it should layout it’s children. Here’s a simple example of the code I’m using

protected override void LayoutChildren(
   double x, double y, 
   double width, double height)
{
   var square = Math.Min(width / Columns, height / Rows);

   var startX = x + (width - square * Columns) / 2;
   var startY = y;

   var rect = new Rectangle(startX, startY, square, square);
   var c = 0;
   foreach (var child in Children)
   {
      LayoutChildIntoBoundingRegion(child, rect);

      if (child.IsVisible)
      {
         rect.X += square;
         if (++c >= Columns)
         {
            rect.Y += rect.Height;
            rect.X = startX;
            c = 0;
         }
      }
   }
}

Notice we use the LayoutChildIntoBoundingRegion which ultimately calls the child.Layout but applies margins etc. for us.

Xamarin Forms TabbedPage

The TabbedPage is, as you’d probably expect, a page that hosts ContentPage and NavigationPage elements and displays “tabs” for selecting between those pages.

You cannot have a ContentPage hosting a tab control within it without using a third party control or writing your own.

Let’s create a TabbedPage based application

Let’s take look at implementing a TabbedPage based UI in Visual Studio 2017 (the Visual Studio for Mac project template creates a tabbed page application by default).

  • In Visual Studio 2017, File | New | Project
  • In the search box, type mobile (or select Cross-Platform | Mobile App (Xamarin.Forms))
  • Give your project a name, mine’s TabbedPageExample
  • Select Blank App, I’m aiming to write for all three listed platforms and so keep all three checked, I then select .NET Standard (or Shared Project if you prefer)
  • Build the project just to get NuGet packages updated etc.

We’ve got our project created and by default on Visual Studio 2017 you’ll have created a MainPage.xaml which is a ContentPage, we’re going to change this to a TabbedPage

  • Open the MainPage.xaml and change ContentPage to TabbedPage
  • Before we leave MainPage.xaml remove the StackLayout and everything within it, we won’t be needing this
  • Open the MainPage.xaml.cs and change ContentPage to TabbedPage here also
  • Right mouse click on the shared project (the one not suffixed with a platform name) and select Add | New Item. Select The Xamarin.Forms item on the left, then Content Page (note: not the Content Page (C#) as we’re going to just writing XAML here). Do this two more times as these will form our three tab pages. Mine are named ProjectsPage, HistoryPage and AboutPage.
  • Back in MainPage.xaml, add the following, within the TabbedPage element (i.e. where StackLayout used to reside)
    <local:ProjectsPage />
    <local:HistoryPage />
    <local:AboutPage />
    

    Note: For those not used to XAML etc. the local text is an xmlns (namespace), automatically added to our XAML files.

  • Now in each XAML file (ProjectsPage.xaml, History.xaml etc.) just change the Label to use the display then name as the page (just so we can see the page changes when we click on a tab). Also add a Title=”” to each ContentPage as an attribute and inside the “” put the tab name, i.e. Projects, History, About

If you build and run this now (I’m using the Visual Studio Emulator for Android to view things on Windows), you should see – on Android and UWP three tabs with the labels all at the top of the screen, if you run this against an iOS emulator/simulator the tabs will be at the bottom of the screen and the text is very small – this is because Android and UWP, by default, show just text, iOS be default shows text and an image, so the small text is that size to accommodate the image.

Platform specific XAML

We’re trying to keep all our shared code, whether these are services, other libraries or UI, in the shared project. Xamarin.Forms allows us to conditionally add XAML based upon the platform, i.e.

<ContentPage.Icon>
   <OnPlatform x:TypeArguments="FileImageSource">
      <On Platform="iOS" Value="history.png"/>
   </OnPlatform>
</ContentPage.Icon>

For each ContentPage we add the above as a child to the ContentPage element. the x:TypeArguments refers to the type of objects for the On Platform Value, i.e. these are Image file locations in this case.

As this adds platforms specific conditions to the XAML, we would then create the various .png images, not within the shared code but instead within the TabbedPageExample.iOS project, into the Resources folder and marked as BundleResource.

Each image file should come in three sizes, so let’s suppose we create an file about.png (for the About tab). This should be 30 x 30 pixels in size. Then the next file should be about@2x.png and should be 60 x 60 pixels, finally the last file should be about@3x.png and should be 90 x 90 pixels in size.

Once you’ve created a group of three image’s per tab (as outlined) and ensured their Build Action is BundleResource and got the XAML correctly references each of the smaller images, then you should find that when you run the app. on iOS, text and an image is displayed on each tab. Whilst Android and UWP is solely text. By only including the images in the iOS project you’re not wasting space on the other projects that do not use them.

Code

Code is available on github.

Xamarin Forms (>= 3.0) supports CSS

In my previous post Styles in Xamarin Forms I demonstrated creating a simple styles using XAML Style objects. However, as of version 3.0 of Xamarin Forms, you can also use CSS (Cascading Style Sheet) files/syntax to define your styles.

XAML Style is quite a verbose way of defining styles but if you’re used to WPF or UWP it’s probably something you’re quite happy with. If, however, you come from a web background you’ll probably be used to CSS files instead. Now you can use similar syntax to define your styles for Xamarin Forms (3.0 or above).

If you create a standard mobile application in Visual Studio you’ll get a simple ContentPage based application – as per the previous post we’re going to start off by simply defining a Lime green background for our ContentPage’s.

Now simply add a CSS file (mine’s named Styles.css) to your solution and set it’s Build Action to Embedded resource and place the following into the file

^ContentPage {
     background-color: Lime;
}

Note: the CSS is case insensitive, but I’ve stuck to using the case associated with the controls/page here.

Notice the use of the ^ followed by the base type. If you read my previous post Styles in Xamarin Forms, you’ll notice that this is equivalent to an implicit style. i.e. it’s applied to anything of the type ContentPage.

To use this style we need to add the following to our ContentPage XAML

<ContentPage.Resources>
   <StyleSheet Source="Styles.css" />
</ContentPage.Resources>

Note: you do NOT need to add any namespace to use the StyleSheet (or at least I didn’t at the time of writing). if Visual Studio or Resharper suggests you need to add a namespace, just ignore it.

Now if you run your application you’ll be presented with the Lime background ContentPage.

If you’re used to CSS you’ll know that you can also declare an id (the equivalent of an explicit style) to your CSS. Let’s change our CSS to this following

#ContentStyle {
     background-color: Lime;
 }

Now we can assign this to either the StyleId or it will fallback to using x:Name (if no StyleId is set) on our control. For example

StyleId="ContentStyle"

or if you prefer you can simply use the x:Name (remember no StyleId should exist)

x:Name="ContentStyle"

We can also declare our CSS using CLASS selectors such as

.ContentStyle {
     background-color: Lime;
}

Now we would use the StyleClass attribute on our ContentPage instead of StyleId or x:Name, i.e.

StyleClass="ContentStyle"

Finally, we can also declare CSS for child elements, so for example CSS for the Label which is a child of a StackLayout would look like this

StackLayout Label {
    background-color: Lime;
}

See Selector reference for more on selector syntax, which also demonstrated setting style for direct child objects etc.

References

Styling Xamarin.Forms apps using Cascading Style Sheets (CSS)
Styling Xamarin.Forms Apps with CSS

Styles in Xamarin Forms

Like WPF and UWP, Xamarin Forms supports the ability to “style” your application controls.

We can explicitly apply styles by creating a style with a key and explicitly assigning that style to a control. Alternatively we can implicitly apply styles be declaring the type (only) that the style applies to.

Let’s just do something very simple and make our ContentPage a rather bright Lime colour.

Explicit Style

After creating a mobile application within Visual Studio (which will default to using a ContentPage) simply add the following to the App.xaml, within the Application.Resources element.

<ResourceDictionary>
   <Style x:Key="ContentStyle" TargetType="ContentPage" >
      <Setter Property="BackgroundColor" Value="Lime" />
   </Style>
</ResourceDictionary>

In this example, we’ve declared the x:Key as ContentStyle. This is an explicit style in that, to use it, we need to set the Style property on the ContentPage that we want to apply this style to.

Open the MainPage.xaml (or any other ContentPage xaml files you have) and add the following as an attribute to the ContentPage

Style="{StaticResource ContentStyle}"

If you run the application you’ll be presented with a ContentPage with a bright Lime background colour.

If you’ve come from using WPF or UWP you’ll know you can also declare styles to the resource of your page’s so they’re scoped solely to that page. The App.xaml is globally scoped.

Implicit Style

Now, if all our ContentPage objects were to use this style, we might prefer to not have to assign the style each time. Instead we can change our App.xaml Style to look like this

<Style TargetType="ContentPage" ApplyToDerivedTypes="True">
   <Setter Property="BackgroundColor" Value="Lime" />
</Style>

Notice, we’ve removed the x:Key and added the ApplyToDerivedTypes. Now remove the Style attribute from your, previously created, ContentPage and run the application again and you’ll see the Style is automatically applied to all ContentPage objects.

Note: You can use TargetType=”{x:Type ContentPage}”, this more verbose way of declaring the TargetType is not required.

Using the Xamarin Forms ListView

The ListView (as probably expected) allows us to display a list of data items, just like WPF/XAML we can alter the list item (or ViewCell) to display a custom view of our data, including images, buttons etc.

Bind to the ItemsSource property

For some unknown reason, I continually forget that the property used to bind our list of items to on a ListView is ItemsSource, not BindingContext, so just putting this note to myself Bind to the ItemsSource.

Okay back to the post…

Basic use of the ListView

In the case where we have a list of items, such as strings, then we can simply use

<ListView ItemsSource="{Binding Items}" />

assuming we have a view model which exposes an Items property which is a collection (i.e. ObservableCollection or the likes).

Displaying more complex items

As you’d probably expect, if you’re trying to display a non-primitive type, you can override the class ToString method to return a string representation of the data, for example if we’re trying to display for the type SlightlyLessBasicItemViewModel

public class SlightlyLessBasicItemViewModel
{
   public string Key { get; set; }
   public string Value { get; set; }

   public override string ToString()
   {
      return $"{Key} : {Value}";
   }
}

However, it’s also likely that we’ll want to formatting, images. buttons etc. into a list view item at which point we need to create our own item template.

If we assume we have a view model with an Items collection of SlightlyLessBasicItemViewModel we might like to display our row/cell with the key in bold and the value italic, for example we would use something like

<ListView ItemsSource="{Binding Items}">
   <ListView.ItemTemplate>
      <DataTemplate>
         <ViewCell>
            <StackLayout Orientation="Horizontal" VerticalOptions="Center">
               <Label Text="{Binding Key}" FontAttributes="Bold" />
               <Label Text="{Binding Value}" FontAttributes="Italic" />
             </StackLayout>
          </ViewCell>
       </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Grouping

The ListView supports grouping, i.e. displaying something slightly different to indicate that the items belong to a group. Let’s create the following

public class GroupViewModel : 
   ObservableCollection<SlightlyLessBasicItemViewModel>
{
   public string GroupName { get; set; }
}

Now our main view model will return an Items collection of GroupViewModel types, each GroupViewModel will act as a separate header (and bind to the GroupName for the group heading) and the items will then be displayed beneath each grouping name in the ListView.

All we need to do to our XAML is add IsGroupingEnabled=”True” and then tell the List view to display the GroupName using GroupDisplayBinding=”{Binding GroupName}”. For completeness, here’s the full XAML for this

<ListView ItemsSource="{Binding Items}" 
      IsGroupingEnabled="True"
      GroupDisplayBinding="{Binding GroupName}">
   <ListView.ItemTemplate>
      <DataTemplate>
         <ViewCell>
            <StackLayout Orientation="Horizontal" VerticalOptions="Center">
               <Label Text="{Binding Key}" FontAttributes="Bold" />
               <Label Text="{Binding Value}" FontAttributes="Italic" />
            </StackLayout>
         </ViewCell>
      </DataTemplate>
   </ListView.ItemTemplate>
</ListView>

We can also add the GroupShortNameBinding binding to add to the jump list for jumping to specific parts of our list.

Ofcourse it’s also likely we’re want to override the cell that displays the group. To do this we override the GroupHeaderTemplate (we no longer need the GroupDisplayBinding if we’re taking over the displaying of it). So for example here’s out new group header template displayed in bright green

<ListView ItemsSource="{Binding Items}" 
      IsGroupingEnabled="True">
   <ListView.ItemTemplate>
      <DataTemplate>
         <ViewCell>
            <StackLayout Orientation="Horizontal" VerticalOptions="Center">
               <Label Text="{Binding Key}" FontAttributes="Bold" />
               <Label Text="{Binding Value}" FontAttributes="Italic" />
            </StackLayout>
         </ViewCell>
      </DataTemplate>
   </ListView.ItemTemplate>
   <ListView.GroupHeaderTemplate>
      <DataTemplate>
         <ViewCell>
            <StackLayout Orientation="Horizontal" VerticalOptions="Center">
               <Label Text="{Binding GroupName}" TextColor="Chartreuse"/>
            </StackLayout>
         </ViewCell>
      </DataTemplate>
   </ListView.GroupHeaderTemplate>
</ListView>

Headers and Footers

Along with groupings we can define headers (displayed as you’d expect at the top of the list view) and footers (yes, these are displayed at the end of the list view). Again, like groupings, we can create/use the default look and feel or override it, so here’s an example with the default look and feel (as our ListView XAML’s getting rather large I’ll show an abridge version to just show the changes here, source for this post will be available on my GitHub repos).

Note: I’m not going to change the view model (the GroupingViewModel) used in this example but instead hard code the header and footer, ofcourse you can data bind to these from your view model just as we’ve done for groups etc.

<ListView ItemsSource="{Binding Items}" 
   IsGroupingEnabled="True" 
   Header="---Header---"
   Footer="---Footer---">
</ListView>

Now let’s override our templates to customise the display of our header and footer. Remove the text only from the Header and Footer attributes. We’ll need the attributes still to tell the ListView to display a header and footer. Then we added our HeaderTemplate and FooterTemplate, for example

<ListView ItemsSource="{Binding Items}" 
   IsGroupingEnabled="True" 
   Header="" Footer="">
<!-- other content -->
   <ListView.GroupHeaderTemplate>
      <DataTemplate>
         <ViewCell>
            <StackLayout Orientation="Horizontal" VerticalOptions="Center">
               <Label Text="{Binding GroupName}" TextColor="Chartreuse"/>
            </StackLayout>
         </ViewCell>
      </DataTemplate>
   </ListView.GroupHeaderTemplate>
   <ListView.HeaderTemplate>
      <DataTemplate>
         <StackLayout Orientation="Horizontal">
            <Label Text="---" TextColor="Crimson" />
            <Label Text="Header" TextColor="DarkOrange"/>
            <Label Text="---" TextColor="Crimson" />
         </StackLayout>
      </DataTemplate>
   </ListView.HeaderTemplate>
   <ListView.FooterTemplate>
      <DataTemplate>
         <StackLayout Orientation="Horizontal">
            <Label Text="---" TextColor="Orange" />
            <Label Text="Footer" TextColor="Crimson"/>
            <Label Text="---" TextColor="Orange" />
         </StackLayout>
      </DataTemplate>
   </ListView.FooterTemplate>
</ListView>

Different (or uneven) row heights

In some situations we might want the ListView to display rows with different heights. By default all rows will have the same height, we simply need to set HasUnevenRows=”True” in the ListView and then, assuming our ItemTemplate expands to fit the data it’s given, we’ll get rows of differing sizes.

This example is slightly contrived to save creating another view model, so we’ll make the changes to the ItemTemplate to support larger labels and also to expand so when the label wraps the row expands. Here’s the changes to the ListView

<ListView ItemsSource="{Binding Items}" 
     IsGroupingEnabled="True" Header="" Footer="" 
     HasUnevenRows="True">
   <ListView.ItemTemplate>
      <DataTemplate>
         <ViewCell>
            <StackLayout Orientation="Horizontal" 
               VerticalOptions="StartAndExpand">
               <Label Text="{Binding Key}" FontAttributes="Bold" FontSize="24"/>
               <Label Text="{Binding Value}" FontAttributes="Italic" FontSize="24"/>
            </StackLayout>
         </ViewCell>
      </DataTemplate>
   </ListView.ItemTemplate>
<!-- other content -->
</ListView>

Pull to refresh

A paradigm used in mobile list views is the idea of pulling the list view to refresh it, for example maybe the list view displays a list of email messages, instead of automatically updating the list the user might see an indicator which says more messages are pending, then the user pulls the list to refresh/update the view.

To add such functionality we need to add the IsPullToRefreshEnabled=”True” to the ListView itself and then we’ll need our view model to supply a RefreshCommand and also a boolean property to tell the UI when the refresh has completed.

So let’s go back to a very basic list view and view model. Here’s my view model

public class PullToRefereshViewModel : ViewModel
{
   public PullToRefereshViewModel()
   {
      RefreshCommand = new ActionCommand(() =>
      {
         Items.Add(DateTime.Now.ToString());
         IsRefreshing = false;
      }); 
   }

   [CreateInstance]
   public ExtendedObservableCollection<string> Items
      => GetProperty<ExtendedObservableCollection<string>>();

   public ICommand RefreshCommand { get; }

   public bool IsRefreshing
   {
      get => GetProperty<bool>();
      set => SetProperty(value);
   }
}

Note: I’m using my Presentations.Core view model code which hopefully is fairly obvious.

Here’s the list view

<ListView ItemsSource="{Binding Items}" 
   IsPullToRefreshEnabled="True" 
   RefreshCommand="{Binding RefreshCommand}"
   IsRefreshing="{Binding IsRefreshing}"/>

When the user pulls the ListView down, it (assuming IsPullToRefreshEnabled is true and the supplied RefreshCommand CanExecute is true) will set IsRefreshing to true and display a activity/busy indicator. It will then call the ICommand assigned to RefreshCommand. However after the command completes the IsRefreshing will need to be set to false to hide the activity/busy indicator. It sort of feels like this should happen automatically when the command finishes, but seems it’s down to the view model to reset this flag.

Note: It’s important that the IsRefreshing is set to false or you’ll be stuck the busy indicator being displayed. Therefore, it’s probably best to put the code to reset IsRefreshing into a finally block if there’s chances of exceptions in the refresh command code.

Context Menu/Actions

We can add a context menu/action to the cell of a listview, for example a Delete option when the cell is tapped the option to delete the selected cell should appear.

We’ll extend the “Pull to Refresh” example by adding an ItemTemplate and changing the view model a little. Here’s the ItemTemplate

<ListView.ItemTemplate>
   <DataTemplate>
      <ViewCell>
         <ViewCell.ContextActions>
            <MenuItem Text="Delete" 
               Command="{Binding DeleteCommand}" 
               IsDestructive="True" />
          </ViewCell.ContextActions>
          <StackLayout>
             <Label Text="{Binding Value}" VerticalOptions="Center" />
          </StackLayout>
      </ViewCell>
   </DataTemplate>
</ListView.ItemTemplate>

Note: the use of IsDestructive=”True” has significance on iOS devices, only a single action can have this set to true.

In this example we need to move away from using a collection of strings for the ItemSource and have a class with a DeleteCommand on it, hence in the example above the cell label binds to a Value property on this new class. Here’s one possible way of implementing the view model for this along with the replacement for the PullToRefreshViewModel

public class ItemViewModel : ViewModel
{
   private readonly ContextMenuViewModel _parent;
   
   public ItemViewModel(ContextMenuViewModel parent, string value)
   {
      _parent = parent;
      Value = value;
      DeleteCommand = new ActionCommand<ItemViewModel>(s =>
      {
         _parent.Items.Remove(this);
      });
   }

   public ICommand DeleteCommand { get; }

   public string Value
   {
      get => GetProperty<string>();
      set => SetProperty(value);
   }

   public class ContextMenuViewModel : ViewModel
   {
      public ContextMenuViewModel()
      {
         RefreshCommand = new ActionCommand(() =>
         {
            Items.Add(new ItemViewModel(this, DateTime.Now.ToString()));
            IsRefreshing = false;
         });
      }

      [CreateInstance]
      public ExtendedObservableCollection<ItemViewModel> Items
         => GetProperty<ExtendedObservableCollection<ItemViewModel>>();

      public ICommand RefreshCommand { get; }

      public bool IsRefreshing
      {
         get => GetProperty<bool>();
         set => SetProperty(value);
      }
}

Note: there are several ways we might want this sort of code to work, this is just one example.

Code for the samples

Code for the samples listed, can be found at GitHub https://github.com/putridparrot/blog-projects/tree/master/ListViewExamples

Monitoring your application using App Center

Microsoft’s App Center (https://appcenter.ms/apps) allows us to do several things, including build and distribution of our application(s), but what I’m interested in for this post, is the capabilities which allow us to monitor some usage information, tracing events and viewing exceptions.

Within App Center you need to create an application and tell App Center what OS and Platform is being used and it will respond by supplying you with information on how to “embed” App Center code into your application.

Once we’ve created an application in App Center we’ll be supplied with a key (currently you need a key per OS application).

Let’s now create a simply test application using Xamarin.Forms, although you can create a standard UWP application if you prefer.

Note: at this time WPF does not seem to be fully supported.

Creating our Xamarin.Forms application

  • In Visual Studio, select Mobile App (Xamarin.Forms) project, this will allow us to create a UWP, IOS and Android application (ensure all three platforms are selected).
  • Now add the two NuGet packages, Microsoft.AppCenter.Analytics and Microsoft.AppCenter.Crashes to each platform specific project.

Inside the shared project, edit the App.xaml.cs adding the following using clauses

using Microsoft.AppCenter;
using Microsoft.AppCenter.Analytics;

and within the OnStart, add the following code

AppCenter.Start(
   "android=ANDROID_KEY;" +
   "uwp=UWP_KEY;" +
   "ios=IOS_KEY",
   typeof(Analytics), 
   typeof(Crashes));

For each platform you need to create a new application key within App Center. If you are only intending to create a single platform version of your application, for example a UWP application, you can use the following instead

AppCenter.Start(UWP_KEY,
   typeof(Analytics), 
   typeof(Crashes));

If we now run our application, App Center will start to log each time the application is run along with information regarding the OS being used, but what if we want to start to track certain usage, for example to find out if a feature is actually being used.

Exceptions and crash reporting

Crash/Exception reporting is enabled by default but obviously if you’re running inside the Debugger in Visual Studio, this may intercept such exceptions before they get sent to the App Center, so if your using a mobile device, you’ll need to actually deploy the application to test any exception handling.

However in instances where we want to both handle an exception (probably most cases) and log/track the exception we use code like the following

try
{
   // do something
}
catch(Exception e) 
{
   Crashes.TrackError(e);
}

We can also supply a dictionary with additional information to TrackError, for example

try
{
   // do something
}
catch(Exception e) 
{
   Crashes.TrackError(e, new Dictionary<string, string>
   {
      { "WithFile", filename }
   });
}

Tracking events

To track events, simple add the following line (obviously replacing the text with whatever you want to see in App Center when an event occurs).

Analytics.TrackEvent("New Feature Used");

we can also supply more information to the event tracking, by supplying a dictionary to the TrackEvent method, for example

Analytics.TrackEvent("New Feature Used", new Dictionary<string, string>
{
   { "WithFile", filename }
});

Turning Analytics on/off

Obviously you might want to have a configuration option which allows a user to opt-in/out of analytics, simply use

Analytics.SetEnabledAsync(false);

Offline behaviour

Obviously all this is great if the application/device in connected to the internet but in situations where the application or device are offline, messages will be stored locally. The FAQ states that up to 300 logs can be stored offline and once a internet connection is restored the messages will be sent to the App Center.

View/ViewModel resolver

In my first post on using Prism and Xamarin Forms Prism and Xamarin Forms I stated that you needed to follow the convention of views being located in a .Views. namespace and view models being located in a .ViewModels. namespace.

In fact this is not quite true. What happens is, if the ViewModelLocator.AutowireViewModel in the view is set to true, only then does Prism attempt to wire up a view model to a view and in fact the first thing it does is to look at whether the programmer has actually registered a view to a view model factory method. Only if this returns null (i.e. no factory has been supplied for a view) does it attempt the convention based approach.

Registering your View/ViewModel mapping

We can see how to register our view models with the following code snippet (this should be in your App.cs)

protected override void ConfigureViewModelLocator()
{
   base.ConfigureViewModelLocator();

   ViewModelLocationProvider.Register("XamarinTest.Views.MainPage", () => new MainPageViewModel());
}

As you can see in the above code, you need to ensure the full namespace and type name for the view, and yes it’s a magic string which is not great.

Convention based approach

Okay, so assuming no view/view model mapping has been registered for the view, we’ll now look at the convention based approach used in prism. So by default the convention based approach expects the views to be in a Views namespace and the view models to be in a ViewModels namespace, but this can be changed to suit your needs.

For example, some prefer to keep views and view models for a specific set of functionality together within the same folder or namespace as it saves jumping around a project look for each part of the specific functionality. Or maybe you find other was to layout your view/view model files which you prefer. You can assign your on Func to the ViewModelLocationProvider’s static method SetDefaultViewTypeToViewModelTypeResolver to achieve you own view model mapping.

Let’s take a look

// override this method in your App.cs file 
protected override void ConfigureViewModelLocator()
{
   base.ConfigureViewModelLocator();

   ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(AlternateResolver);
}

Next let’s write a simple implementation of a resolver which looks for matching ViewModels by simply expecting them to be in the same namespace etc. as the view

// add this method to your App.cs
private static Type AlternateResolver(Type type)
{
   var viewName = type.FullName;
   if (String.IsNullOrEmpty(viewName))
      return null;

   var viewModel = viewName + "ViewModel";

   return Type.GetType(viewModel);
}

Note: I had intended to create an assembly lookup method that would search the assembly for matching types but limitation in what we get as part of the PCL and/or Xamarin appears to have scuppered that idea, so in the above code we simply append ViewModel to the view name. Obviously if you name your views with the View suffix, such as MainPageView, then this code is rather simplistic and will expect the view model to be named MainPageViewViewModel, I’ll leave it to the reader to amend the method as required.

Prism and Xamarin Forms

Here’s the steps for getting Prism and Xamarin Forms up and running together. For this I’m using Visual Studio 2015. Refer to a previous post on how to get a Xamarin Forms application up and running in Visual Studio 2015 if you need to.

Note: I’m using the Unity container hence I’ll be demonstrating the code using Prism.Unity.Forms, there is support for alternate IoC containers such as NInject, Mef and Autofac. Prism.Unity.Forms will automatically include Prism.Core and Prism.Forms.

  • Create a Cross-Platform, Blank App (Xamarin.Forms Portable)
  • Using NuGet, install Prism.Unity.Forms (at the time of writing – you’ll need to click on the Include prerelease option), I’ve installed v6.2.0-pe5
  • For each of the projects you’ve created you’ll now need to go to each References section and add the Prism.Unity.Forms package from NuGet
  • Prism has a convention based approach to loading view model’s, so let’s create the two folders, Views and ViewModels off of the Portable project – in fact it’s the namespace that matters, so the expectation is for views to be in the Views namespace and likewise the view models should be in the ViewModels namespace.
  • Now add a MainPage.xaml file to the Views folder, so in the Portable project right mouse click, select Add | New Item and then from Cross-Platform select Forms Xaml Page, I’ve named mine MainPage
  • In the MainPage.xaml file you’ll see the Label is bound to a MainText property, so let’s now create a MainPageViewModel.cs file in the ViewModels folder to act as our view model for this page (the default convention is for the view model for a view to be named after the view with the suffix ViewModel, i.e. MyView by default maps to MyViewModel).
  • To allow Prism to auto wire up view models we need to add the following to the XAML file (to the ContentPage)
    xmlns:mvvm="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
    mvvm:ViewModelLocator.AutowireViewModel="True"
    
  • Derive the view model from BindableBase and implement the following code to simply get things up and running
    public class MainPageViewModel : BindableBase
    {
       public string MainText { get; } = "Hello Prism World";
    }
    

    You’ll need to add using Prism.Mvvm; to resolve the BindableBase object.

  • Remove all the code from the App constructor as the MainPage will be supplied by Prism going forward.
  • Derive your App from PrismApplication instead of Application and implement the abstract methods as below
    public class App : PrismApplication
    {
       // add these methods
       protected override void OnInitialized()
       {
          NavigationService.NavigateAsync("MainPage");
       }
    
       protected override void RegisterTypes()
       {
          Container.RegisterTypeForNavigation<MainPage>();
       }
    }
    

    You’ll need to add using Prism.Unity; to resolve PrismApplication.

    OnInitialized will automatically navigate to the “MainPage” view, the magic string maps to the type name MainPage as I’m sure you can see. But in the RegisterTypeForNavigation we can change this string mapping if preferred.