Friday, March 17, 2017

Build cross-platform mobile apps with Xamarin and Microsoft Visual Studio 2015

Build cross-platform mobile apps with Xamarin and Microsoft Visual Studio 2015

Objective

Today, we will be building a cloud connected Xamarin.Forms application that will display a list of speakers at Xamarin Dev Days and show their details. We will start by building some business logic backend that pulls down json from a restful endpoint and then we will connect it to an Azure Mobile App backend in just a few lines of code.

Scenario

Virtual Machines

  1. DC01
  2. Windows 10

Exercise 1 : Get Started

  1. Get Started
    Go to the Windows 10 Machine.  Open C:\Files\DEVHOL202\HandsOnLab\Start\DevDaysSpeakers.sln.

    Close the Xamarin Mac Agent dialog.
  2. This solution contains 4 projects
    This solution contains 4 projects:
    • DevDaysSpeakers (Portable) - Portable Class Library that will have all shared code (model, views, and view models).
    • DevDaysSpeakers.Droid - Xamarin.Android application
    • DevDaysSpeakers.iOS - Xamarin.iOS application
    • DevDaysSpeakers.UWP - Windows 10 UWP application (can only be run from VS 2015 on Windows 10)
  3. Model
    We will be pulling down information about speakers. Open the DevDaysSpeakers/Model/Speaker.cs file and add the following properties inside of the Speaker class:

    public string Name { get; set; }
    public string Description { get; set; }
    public string Website { get; set; }
    public string Title { get; set; }
    public string Avatar { get; set; }
  4. View Model
    Open ViewModel\SpeakersViewModel.cs.

    This will provide all of the functionality for how our main Xamarin.Forms view will display data. It will consist of a list of speakers and a method that can be called to get the speakers from the server. It will also contain a boolean flag that will indicate if we are getting data in a background task.
  5. Implementing INotifyPropertyChanged
    INotifyPropertyChanged is important for data binding in MVVM Frameworks. This is an interface that, when implemented, lets our view know about changes to the model.

    Update:

    public class SpeakersViewModel

    {

    }
    to

    public class SpeakersViewModel : INotifyPropertyChanged
    {

    }
  6. Expand the light bulb
    Hover over INotifyPropertyChanged. Click the light bulb icon and select Implement Interface, which will add the following line of code:

    public event PropertyChangedEventHandler PropertyChanged;

    Please click on the camera icon on the lower left hand side to view the screenshot.
  7. We will code a helper method
    We will code a helper method named OnPropertyChanged that will raise the PropertyChanged event (see below). We will invoke the helper method whenever a property changes.

    private void OnPropertyChanged([CallerMemberName] string name = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

    Now, we can call OnPropertyChanged(); whenever a property updates. Let's create our first property now.
  8. IsBusy
    We will create a backing field and accessors for a boolean property. This will let our view know that our view model is busy so we don't perform duplicate operations (like allowing the user to refresh the data multiple times).

    First, create the backing field:

    private bool busy;

    Next, create the property:

    public bool IsBusy

    {

        get { return busy; }
        set
        {
            busy = value;

            OnPropertyChanged();
        }

    }

    Notice that we call OnPropertyChanged(); whenever the value changes. The Xamarin.Forms binding infrastructure will subscribe to our PropertyChanged event so the UI will get notified of the change.
  9. ObservableCollection of Speaker
    Declare the Speakers auto-property:

    public ObservableCollection Speakers { get; set; }


    Add the following constructor, which creates a new instance of the ObservableCollection:

    public SpeakersViewModel()

    {

        Speakers = new ObservableCollection();

    }

     
    We will use an ObservableCollection that will be cleared and then loaded with speakers. We will use an ObservableCollection because it has built-in support for CollectionChanged events when we Add or Remove from it. This is very nice so we don't have to call OnPropertyChanged each time.
  10. GetSpeakers Method
    First, create a method called GetSpeakers which is of type async Task (it is a Task because it is using Async methods):

    private async Task GetSpeakers()

    {

    }
    We are now set to create a method named GetSpeakers which will retrieve the speaker data from the internet. We will first implement this with a simply HTTP request, but later update it to grab and sync the data from Azure!
  11. First check if we are already grabbing data
    First check if we are already grabbing data:

    private async Task GetSpeakers()

    {
        if(IsBusy)
            return;
    }
    Notice, that we set IsBusy to true and then false when we start to call to the server and when we finish.
  12. Next we will create some scaffolding
    Next we will create some scaffolding for try/catch/finally blocks:

    private async Task GetSpeakers()
    {
        if (IsBusy)
            return;

        Exception error = null;
        try
        {
            IsBusy = true;

        }
        catch (Exception ex)
        {
            error = ex;
        }
        finally
        {
           IsBusy = false;
        }

    }
  13. Now, we will use HttpClient
    Now, we will use HttpClient to grab the json from the server inside of the try block.

    using(var client = new HttpClient())
    {
        //grab json from server
        var json = await client.GetStringAsync("http://demo4404797.mockable.io/speakers");
    }
  14. Still inside of the using
    Still inside of the using, we will Deserialize the json and turn it into a list of Speakers with Json.NET:

    var items = JsonConvert.DeserializeObject>(json);
  15. Still inside of the using
    Still inside of the using, we will clear the speakers and then load them into the ObservableCollection:

    Speakers.Clear();
    foreach (var item in items)
        Speakers.Add(item);
  16. If anything goes wrong
    If anything goes wrong the catch will save the exception.

    catch (Exception ex)
    {
        Debug.WriteLine("Error: " + ex);
        error = ex;
    }
  17. AFTER the finally block we can pop up an alert
    and AFTER the finally block we can pop up an alert:

    if (error != null)
        await Application.Current.MainPage.DisplayAlert("Error!", error.Message, "OK");
  18. The completed code should look like:
    The completed code should look like:

    Please click on the camera icon on the lower left hand side to view the screenshot.
    Our main method for getting data is now complete!
  19. GetSpeakers Command
    Create a new Command called GetSpeakersCommand:

    public Command GetSpeakersCommand { get; set; }
    Instead of invoking this method directly, we will expose it with a Command. A Command has an interface that knows what method to invoke and has an optional way of describing if the Command is enabled.
  20. Inside of the SpeakersViewModel constructor
    Inside of the SpeakersViewModel constructor, create the GetSpeakersCommand and pass it two methods: one to invoke when the command is executed and another that determines whether the command is enabled. Both methods can be implemented as lambda expressions as shown below:

    GetSpeakersCommand = new Command(
                    async () => await GetSpeakers(),
                    () => !IsBusy);
  21. The only modification that we will have to make
    The only modification that we will have to make is when we set the IsBusy property, as we will want to re-evaluate the enabled function that we created. In the set of IsBusy simply invoke the ChangeCanExecute method on the GetSpeakersCommand as shown below:

    set
    {
        busy = value;
        OnPropertyChanged();

        //Update the can execute
        GetSpeakersCommand.ChangeCanExecute();
    }

     
Click Continue to advance to the next exercise.

Exercise 2 : The User Interface

It is now finally time to build out our first Xamarin.Forms user interface in the View/SpeakersPage.xaml
  1. SpeakersPage.xaml
    SpeakersPage.xaml
    For the first page we will add a few vertically-stacked controls to the page. We can use a StackLayout to do this. Between the ContentPage tags add the following:


    This will be the container where all of the child controls will go. Notice that we specified the children should have no space in between them.
  2. Next, inside the StackLayout
    Next, inside the StackLayout, let's add a Button that has a binding to the GetSpeakersCommand that we created (see below). The command takes the place of a clicked handler and will be executed whenever the user taps the button.

  3. Under the button we can display a loading bar
    Under the button we can display a loading bar when we are gathering data from the server. We can use an ActivityIndicator to do this and bind to the IsBusy property we created:

  4. We will use a ListView
    We will use a ListView that binds to the Speakers collection to display all of the items. We can use a special property called x:Name="" to name any control:

                  ItemsSource="{Binding Speakers}">
           

  5. Replace <!--Add ItemTemplate Here--> with
    Replace with:


       
                                Detail="{Binding Title}"
                        ImageSource="{Binding Avatar}"/>
       


    Xamarin.Forms will automatically download, cache, and display the image from the server.
    We still need to describe what each item looks like, and to do so, we can use an ItemTemplate that has a DataTemplate with a specific View inside of it. Xamarin.Forms contains a few default Cells that we can use, and we will use the ImageCell that has an image and two rows of text.
  6. Validate App.cs
    Open the App.cs file and you will see the entry point for the application, which is the constructor for App(). It simply creates the SpeakersPage, and then wraps it in a navigation page to get a nice title bar.
  7. Run the App!
    Note: This lab runs in a VM and therefore you can only run the UWP version of the app. You would need to be connected to a macOS device with Xamarin installed to run and debug the iOS app. To run the Android app, you could use an Android emulator.
  8. Ensure that you have the SQLite extension
    Ensure that you have the SQLite extension installed for UWP apps:

    Go to Tools->Extensions & Updates
  9. Under Online search for SQLite
    Under Online search for SQLite and ensure that you have SQlite for Univeral Windows Platform installed (current version 3.14.1). You will need to Download and Install the extension and then restart Visual Studio.
  10. Remove the SQLite
    Remove the SQLite for Universal Windows Platform from the References in the UWP app.
  11. Add a new Reference
    Add a new Reference and under Universal Windows -> Extensions you will see SQlite for Universal Windows Platform. Add that in and you will be good to go.

    Run the app.
  12. Click Sync Speakers to retrieve a list of speakers
    Click Sync Speakers to retrieve a list of speakers. Close the app.

    Please click on the camera icon on the lower left hand side to view the screenshot.
  13. Details
    Now, let's do some navigation and display some Details. Let's open up the code-behind for SpeakersPage.xaml called SpeakersPage.xaml.cs.
  14. ItemSelected Event
    In the code-behind you will find the setup for the SpeakersViewModel. Under BindingContext = vm;, let's add an event to the ListViewSpeakers to get notified when an item is selected:

    ListViewSpeakers.ItemSelected += ListViewSpeakers_ItemSelected;
  15. Implement this method
    Implement this method so it navigates to the DetailsPage:

    privateasyncvoid ListViewSpeakers_ItemSelected(object sender, SelectedItemChangedEventArgs
    e)
    {
        var speaker = e.SelectedItem as Speaker;
        if (speaker == null)
            return;

        await Navigation.PushAsync(new DetailsPage(speaker));

        ListViewSpeakers.SelectedItem = null;
    }

    In the above code we check to see if the selected item is not null and then use the built in Navigation API to push a new page and then deselect the item.
  16. DetailsPage.xaml
    Let's now fill in the details page. Open DetailsPage.xaml. Similar to the SpeakersPage, we will use a StackLayout, but we will wrap it in a ScrollView in case we have long text.

     
       
        
       
   
 
  • Now, let's add controls and bindings
    Now, let's add controls and bindings for the properties in the Speaker object. Add the following XAML to the StackLayout.





  • Add two buttons below the XAML above
    Add two buttons below the XAML above. Give them names so we can add clicked handlers to them in the code behind:




     
  • Text to Speech
    If we open up DetailsPage.xaml.cs we can now add a few more click handlers. Let's start with ButtonSpeak, where we will use the Text To Speech Plugin to read back the speaker's description.

    In the constructor, add a clicked handler below the BindingContext:

    ButtonSpeak.Clicked += ButtonSpeak_Clicked;


     
  • Then we can add the clicked handler
    Then we can add the clicked handler and call the cross-platform API for text to speech:

    privatevoid ButtonSpeak_Clicked(object sender, EventArgs e)
    {
        CrossTextToSpeech.Current.Speak(this.speaker.Description);
    }
  • Open Website
    Xamarin.Forms itself has some nice APIs built right in for cross platform functionality, such as opening a URL in the default browser.

    Let's add another clicked handler, but this time for ButtonWebsite:

    ButtonWebsite.Clicked += ButtonWebsite_Clicked;
  • Then, we can use Device class
    Then, we can use the Device class to call the OpenUri method:

    privatevoid ButtonWebsite_Clicked(object sender, EventArgs e)
    {
        if (speaker.Website.StartsWith("http"))
            Device.OpenUri(new Uri(speaker.Website));
    }
  • Compile & Run
    Now, we should be all set to compile and run just like before!

    Click Sync Speakers. Then select a speaker to see their details.

    Click Speak to hear about the speaker and then click Go to Website to learn more about him or her.

    Please click on the camera icon on the lower left hand side to view the screenshot.
  • Click Continue to advance to the next exercise.

    Exercise 3 : Challenge 1: Connect to Azure Mobile Apps

    Of course being able grab data from a RESTful end point is great, but what about a full back end? This is where Azure Mobile Apps comes in. Let's upgrade our application to use an Azure Mobile Apps back end.

    Head to http://portal.azure.com and register for an account.
    1. Once you are in the portal
      Once you are in the portal select the + New button and search for mobile apps. Select Mobile Apps Quickstart

      Please click on the camera icon on the lower left hand side to view the screenshot.
    2. The Quickstart blade will open, select Create
      The Quickstart blade will open, select Create.
    3. This will open a settings blade with 4 settings
      This will open a settings blade with 4 settings:

      App name

      This is a unique name for the app that you will need when you configure the back end in your app. You will need to choose a globally-unique name; for example, you could try something like yourlastnamespeakers.

      Subscription Select a subscription or create a pay-as-you-go account (this service will not cost you anything)

      Resource Group Select Create new and call it DevDaysSpeakers

      A resource group is a group of related services that can be easily deleted later.

      App Service plan/Location Click this field and select Create New, give it a unique name, select a location (typically you would choose a location close to your customers), and then select the F1 Free tier:
    4. Finally check Pin to dashboard and click create
      Finally check Pin to dashboard and click create:

      This will take about 3-5 minutes to setup, so let's head back to the code!
    5. Update App.cs
      We will be using the Azure App Service Helpers library that we saw earlier in the presentations to add an Azure back end to our mobile app in just four lines of code.

      In the DevDaysSpeakers/App.cs file let's add a static property above the constructor for the Azure Client:

      publicstatic IEasyMobileServiceClient AzureClient { get; set; }
    6. In the constructor
      In the constructor, simply add the following lines of code to create the client and register the table:

      AzureClient = EasyMobileServiceClient.Create();
      AzureClient.Initialize("https://YOUR-APP-NAME-HERE.azurewebsites.net");
      AzureClient.RegisterTable();
      AzureClient.FinalizeSchema();

      Be sure to udpate YOUR-APP-NAME-HERE with the app name you just specified.
    7. Update SpeakersViewModel.cs
      Back in the ViewModel, we can add another private property to get a reference for the table. Above the constructor add:

      ITableDataStore table;
    8. Inside of the constructor
      Inside of the constructor use the static AzureClient to get the table:

      table = App.AzureClient.Table();
    9. Update async Task GetSpeakers()
      Now, instead of using the HttpClient to get a string, let's query the Table:

      Change the try block of code to:

       try
      {
          IsBusy = true;

          var items = await table.GetItemsAsync();

          Speakers.Clear();
          foreach (var item in items)
              Speakers.Add(item);
      }
    10. Now, we have implemented all of the code
      Now, we have implemented all of the code we need in our app! Amazing isn't it! That's it! App Service Helpers will automatically handle all communication with your Azure back end for you, do online/offline synchronization so your app works even when it's not connected, and even perform automatic conflict resolution. Just 7 lines of code!

      Let's head back to the Azure Portal and populate the database.
    11. When the Quickstart finishes
      When the Quickstart finishes you should see the following screen, or can go to it by tapping the pin on the dashboard:

      Under Features select Easy Tables.

      It will have created a TodoItem, which you should see, but we can create a new table and upload a default set of data by selecting Add from CSV from the menu.
    12. Click the icon
      Click the icon to the right of Select a file and open C:\Files\IDL2030\HandsOnLab\Speaker.csv.

      Select the file and it will add a new table name and find the fields that we have listed. Then hit Start Upload.

      Now you can re-run your application and get data from Azure!
    Click Continue to advance to the next exercise.

    Exercise 4 : Challenge 2: Edit Speaker Details

    In this challenge we will make the speaker's Title editable.
    1. Open DetailsPage.xaml
      Open DetailsPage.xaml and change the Label that is displaying the Title from:



      to an Entry with a OneWay data binding (this means when we enter text it will not change the actual data), and a Name to expose it in the code behind.

                   TextColor="Purple"
                   x:Name="EntryTitle"/>

       
    2. Let's add a save Button
      Let's add a save Button under the Go To Website button.

    3. Update SpeakersViewModel
      Open SpeakersViewModel and add a new method called UpdateSpeaker(Speaker speaker), that will update the speaker, sync, and refresh the list:

       publicasync Task UpdateSpeaker(Speaker speaker)
      {
          await table.UpdateAsync(speaker);
          await table.Sync();
          await GetSpeakers();
      }
    4. Update DetailsPage.xaml.cs
      Let's update the constructor to pass in the SpeakersViewModel for the DetailsPage:

      Before:

      Speaker speaker;
      public DetailsPage(Speaker item)
      {
          InitializeComponent();
          this.speaker = item;
          ...
      }
    5. After:
      After:

      Speaker speaker;
      SpeakersViewModel vm;
      public DetailsPage(Speaker item, SpeakersViewModel viewModel)
      {
          InitializeComponent();
          this.speaker = item;
          this.vm = viewModel;
          ...
      }
    6. Under the other clicked handlers
      Under the other clicked handlers we will add another clicked handler for ButtonSave.

      ButtonSave.Clicked += ButtonSave_Clicked;

       
    7. When the button is clicked
      When the button is clicked, we will update the speaker, and call save and then navigate back:

      privateasyncvoid ButtonSave_Clicked(object sender, EventArgs e)
      {
          speaker.Title = EntryTitle.Text;
          await vm.UpdateSpeaker(speaker);
          await Navigation.PopAsync();
      }
    8. Finally
      Finally, we will need to pass in the ViewModel when we navigate in the SpeakersPage.xaml.cs in the ListViewSpeakers_ItemSelected method:

      //Pass in view model now.
      await Navigation.PushAsync(new DetailsPage(speaker, vm));

      There you have it!

       
    Congratulations!

    Click Continue to close and finalize this lab.

    No comments: