Skip to content

Latest commit

 

History

History
329 lines (236 loc) · 12.6 KB

12-ShowPhotosOnMobileApp.md

File metadata and controls

329 lines (236 loc) · 12.6 KB

Showing the photos on a mobile app

Now that our mobile app can download the photos, we need to display them on our app.

A typical way to display such data is through a scrolling list, with the most recent items at the top. Xamarin.Forms provides a ListView that we will use to display such a scrolling list.

ListViews can be bound to a collection of objects, and each item in the collection is used to supply the data for an item in the list. The common way to expose such a list is using an ObservableCollection. This collection type raises an event when the collection changes, such as having new items added. A ListView bound to an ObservableCollection will automatically incorporate the changes to the UI. We will put a ListView on MainPage.xaml and bind it to an ObservableCollection in MainViewModel.

1. Loading the photos into the ObservableCollection

What object should be in the observable collection? Ideally you want a model that represents each individual item, exposing the photo and metadata as properties.

Property Type Description
Caption string The caption for the photo generated by the Computer Vision service
Tags string The tags for the photo generated by the Computer Vision service, concatenated into a single string
Timestamp long A timestamp for when the document was created
Photo ImageSource An image source for the photo, this will be created from the file name
  1. In the Visual Studio Solution Explorer, right-click HappyXamDevs > Models > Add > Class

    • (Mac) On Visual Studio for Mac, right-click HappyXamDevs > Models > Add > New File
  2. In the Add New Item window, name the file PhotoModel.cs

  3. In the PhotoModel.cs editor, enter the following code:

using System.Linq;
using Xamarin.Forms;

namespace HappyXamDevs.Models
{
    public class PhotoModel
    {
        public PhotoModel(PhotoMetadataModel photoMetadata)
        {
            Caption = photoMetadata.Caption;
            Timestamp = photoMetadata.Timestamp;
            Tags = string.Join(" ", photoMetadata.Tags.Select(t => $"#{t}"));
            Photo = ImageSource.FromFile(photoMetadata.FileName);
        }

        public string Caption { get; }

        public ImageSource Photo { get; }

        public string Tags { get; }

        public long Timestamp { get; }
    }
}
  1. In the Visual Studio Solution Explorer, open HappyXamDevs > ViewModels > MainViewModel.cs

  2. In the MainViewModel.cs editor, add the following using statement:

using System.Collections.ObjectModel;
using System.Linq;
using HappyXamDevs.Models;
  1. In the MainViewModel.cs editor, add the following field:
private bool isRefreshing;
  1. In the MainViewModel.cs editor, add the following property:
public bool IsRefreshing
{
    get => isRefreshing;
    set => Set(ref isRefreshing, value);
}

About the Code

IsRefreshing will be used to display the spinning indicator when a pull-to-refresh is triggered

  1. In the MainViewModel.cs editor, add the following properties:
public ObservableCollection<PhotoModel> Photos { get; } = new ObservableCollection<PhotoModel>();
public ICommand RefreshCommand { get; }

About the Code

ObservableCollection<PhotoViewModel> Photos this is an IEnumerable that will contain the photos displayed on the UI

ICommand RefreshCommand this command will be triggered when the user initiates a pull-to-refresh on the ListView

  1. In the MainViewModel.cs editor, add the following method:
private async Task Refresh()
{
    var photos = await azureService.GetAllPhotoMetadata();

    if (!Photos.Any())
    {
        foreach (var photo in photos.OrderByDescending(p => p.Timestamp))
        {
            Photos.Add(new PhotoModel(photo));
        }
    }
    else
    {
        var latest = Photos[0].Timestamp;

        foreach (var photo in photos.Where(p => p.Timestamp > latest).OrderBy(p => p.Timestamp))
        {
            Photos.Insert(0, new PhotoModel(photo));
        }
    }

    IsRefreshing = false;
}

About the Code azureService.GetAllPhotoMetadata(); retrieves the photo meta data from our Azure Function

if (!Photos.Any()) if Photos is empty, add all of the photos to the ObservableCollection from newest to oldest

else otherwise, only add the newest photos to the ObservableCollection

  1. In the MainViewModel.cs editor, update the constructor using this code:
public MainViewModel()
{
    TakePhotoCommand = new Command(async () => await TakePhoto());
    SelectFromLibraryCommand = new Command(async () => await SelectFromLibrary());
    RefreshCommand = new Command(async () => await Refresh());
    azureService = DependencyService.Get<IAzureService>();
}

About the Code

RefreshCommand = new Command(async () => await Refresh()); initializes RefreshCommand, instructing it to run Refresh() when it is triggered

2. Displaying the photos on the page

ListView use a DataTemplate to specify how to display the items in the list.

Xamarin.Forms includes three templates for displaying text & images; we will use the built-in ImageCell template to display the photos. This UI will be improved later in this workshop.

  1. In the Visual Studio Solution Explorer, open HappyXamDevs > MainPage.xaml

  2. In the MainPage.xaml editor, enter the following code:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             Title="Happy Developers"
             xmlns:viewModels="clr-namespace:HappyXamDevs.ViewModels"
             x:Class="HappyXamDevs.MainPage">

    <ContentPage.BindingContext>
        <viewModels:MainViewModel />
    </ContentPage.BindingContext>

    <ContentPage.ToolbarItems>
        <ToolbarItem Order="Primary"
                     Icon="TakePhoto.png"
                     Priority="0"
                     Command="{Binding TakePhotoCommand}" />
        <ToolbarItem Order="Primary"
                     Icon="SelectFromLibrary.png"
                     Priority="1"
                     Command="{Binding SelectFromLibraryCommand}" />
    </ContentPage.ToolbarItems>

    <ListView x:Name="PhotosListView"
              ItemsSource="{Binding Photos}"
              IsRefreshing="{Binding IsRefreshing}"
              RefreshCommand="{Binding RefreshCommand}"
              IsPullToRefreshEnabled="true"
              SelectionMode="None">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ImageCell ImageSource="{Binding Photo}"
                           Text="{Binding Caption}"
                           Detail="{Binding Tags}" />
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

</ContentPage>

About the Code

ItemsSource="{Binding Photos}" binds the ListView.ItemSource to the ObservableCollection, MainViewModel.Photos

RefreshCommand="{Binding RefreshCommand}" binds the ListView.RefreshCommand to ICommand MainViewModel.Refresh. This will trigger `ICommand

<DataTemplate> <ImageCell ... uses Xamarin.Forms' baked-in ImageCell template to display each PhotoModel. ImageSource is bound to PhotoModel.Photo, displaying the image in bold text. Text is bound to PhotoModel.Caption, displaying each photo's caption next to the photo. Detail is bound to PhotoModel.Tags, displaying each photo's tags in a smaller font underneath its caption.

3. Refreshing the ListView when MainPage.xaml appears

When the main page is launched, it checks to see if the user is logged in, and if not shows the login page. After the user logs in, MainPage should automatically load all the photos.

  1. In the Visual Studio Solution Explorer, open HappyXamDevs > MainPage.xaml.cs

  2. In the MainPage.xaml.cs editor, enter the following code:

using System.Linq;
using HappyXamDevs.Services;
using Xamarin.Forms;

namespace HappyXamDevs
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        protected override async void OnAppearing()
        {
            base.OnAppearing();

            var azureService = DependencyService.Get<IAzureService>();

            if (!azureService.IsLoggedIn())
            {
                if(!Navigation.ModalStack.Any())
                    await Navigation.PushModalAsync(new LoginPage(), false);
            }
            else
            {
                PhotosListView.BeginRefresh();
            }
        }
    }
}

About the Code

else { PhotosListView.BeginRefresh(); } When MainPage appears on the screen, if the user is logged in, the ListView will automatically trigger pull-to-refresh

4. Test Updated UI

You should now have everything in place to view the photos that have been uploaded. Run the app on your platform of choice and you will see the photos with their captions and tags.

4a. Test Updated UI, Android

  1. In Visual Studio, right-click on HappyXamDevs.Android > Set as Startup Project

  2. (PC) In Visual Studio, select Debug > Start Debugging

    • (Mac) In Visual Studio for Mac, select Run > Start Debugging
  3. On the Android device, if the LoginPage complete the login flow

  4. On the Android device, on the MainPage, tap the Camera icon

  5. On the Android device, if prompted for permission, tap Allow

  6. On the Android device, ensure the Camera appears

  7. On the Android device, take a happy-looking selfie

  8. On the Android device, trigger a pull-to-refresh

  9. On the Android device, ensure the uploaded photo and its associated metadata appear

    Note: Repeat the pull-to-refresh a few times until the image appears, because the Azure Functions may take a few seconds to complete. If the image still doesn't appear after a minute, there likely is a bug.

4b. Test Updated UI, iOS

  1. In Visual Studio, right-click on HappyXamDevs.iOS > Set as Startup Project

  2. (PC) In Visual Studio, select Debug > Start Debugging

    • (Mac) In Visual Studio for Mac, select Run > Start Debugging
  3. On the iOS device, if the LoginPage complete the login flow

  4. On the iOS device, on the MainPage, tap the Camera icon

  5. On the iOS device, if prompted for permission, tap Allow

  6. On the iOS device, ensure the Camera appears

  7. On the iOS device, take a happy-looking selfie

  8. On the iOS device, trigger a pull-to-refresh

  9. On the iOS device, ensure the uploaded photo and its associated metadata appear

    Note: Repeat the pull-to-refresh a few times until the image appears, because the Azure Functions may take a few seconds to complete. If the image still doesn't appear after a minute, there likely is a bug.

4c. Test Updated UI, UWP

  1. (PC) In Visual Studio, right-click on HappyXamDevs.UWP > Set as Startup Project

    • (Mac) Skip this step
  2. (PC) In Visual Studio, select Debug > Start Debugging

    • (Mac) Skip this step
  3. On the UWP device, if the LoginPage complete the login flow

  4. On the UWP device, on the MainPage, tap the Camera icon

  5. On the UWP device, if prompted for permission, tap Allow

  6. On the UWP device, ensure the Camera appears

  7. On the UWP device, take a happy-looking selfie

  8. On the UWP device, trigger a pull-to-refresh

  9. On the UWP device, ensure the uploaded photo and its associated metadata appear

    Note: Repeat the pull-to-refresh a few times until the image appears, because the Azure Functions may take a few seconds to complete. If the image still doesn't appear after a minute, there likely is a bug.

Next step

Now that you have a basic UI to show photos, the next step is to improve the UI.