Xamarin.Forms UI Controls - Building the Logify Client App (Part 2)

Mobile Team Blog
21 August 2019

This blog post is the second in our Xamarin.Forms UI development series. We continue to document how we developed a custom crash reporting mobile client by leveraging DevExpress Logify error monitoring service and DevExpress Xamarin.Forms UI Controls. In this part, we’ll describe how portions of our Logify client app were built using our Xamarin controls and hopefully share a few UI-related tips with you for your next Xamarin.Forms mobile project.

If you have yet to read our first post, you can do so here. If you have questions about Xamarin, feel free to comment below.

NOTE: At present, DevExpress Xamarin.Forms UI controls ship as part of our Universal Subscription. We expect to release a Xamarin-only product subscription in our v19.2 release cycle.

Building Logify’s Report List View

If you’ve used Logify or are familiar with its web UX, you know the importance of reports – the means by which exception information is displayed within the Logify web app.

To display the same data/information within our mobile client, we chose to use our Xamarin Data Grid control as our primary UX element. Our Xamarin Grid uses a list of typed objects for its data source.

Our Logify client must obtain the appropriate report(s) from the Logify service and prepare data for display within the Xamarin Grid.

Using Logify’s HTTP API

Our Logify mobile client obtains reports from Logify server via the following API:

GET https://logifyRestApiEndpoint/reports?subscriptionId=SubscriptionId&filter=Filter&count=pageReportCount&page=pageNumber

The server response is in json and includes a list of crash reports:

“[
  ...,
  {
    "DateTime": "2018-04-19T20:55:02.063Z",
    "ApplicationName": "Hotel Booking System",
    "ReportsListInfo": "Specified argument was out of range of valid values",
    ...
  },
  ...
]”

Load Server Data within the Client

For our mobile app, exception/crash reports are represented by a Report class:

using System;
using System.Collections.Generic;

namespace Logify.Models {
    public class Report {
        public DateTime DateTime { get; set; }
        public string ApplicationName { get; set; }
        public string ReportListInfo { get; set; }
    }
}

To interact with the Logify service, we need to create a ReportDataProvider class that loads the necessary data and prepares the object collection for our Xamarin.Forms Data Grid. This class includes a Load method that sends requests to Logify – It parses json it receives into our Report object collection.

public class ReportsDataProvider {
    public ObservableCollection<Report> Reports { get; private set; }
    readonly HttpClient httpClient = new HttpClient();
    int lastPageNumber = 0;

    public ReportsDataProvider() {
        this.Reports = new ObservableCollection<Report>();
    }

    public void Load(Action finalAction = null) => Task.Run(async () => {
        var items;
        try {
            string requestString = string.Format("https://logifyRestApiEndpoint/reports?subscriptionId={0}&filter={1}&count={2}&page={3}", "", "", 20, lastPageNumber);
            var jsonString = await httpClient.GetStringAsync(requestString);
            items = JsonConvert.DeserializeObject<IEnumerable<Report>>(jsonString);
        } catch (Exception e) {}
        Device.BeginInvokeOnMainThread(() => {
            AddToReports(items);
            finalAction?.Invoke();
        });
        lastPageNumber++;
    });

    void AddToReports(IEnumerable<Report> items) {
        foreach (var report in items) {
            Reports.Add(report);
        }
    }
    ...
}

Creating the Report List View

To proceed, we must create the UI used for our exception/crash report list. We will discuss custom grid appearance settings in a separate blog post. For now, we’ll only use a simple structure – a grid with a single text column (one that displays ReportListInfo):

<ContentPage>
    <dxg:DataGridView>
        <dxg:DataGridView.Columns>
            <dxg:TextColumn FieldName="ReportsListInfo" Caption="Reports"/>
        </dxg:DataGridView.Columns>
    </dxg:DataGridView>
</ContentPage>

Preparing the DataSource

To prepare the view model, we need to add the ReportViewModel class. It contains the data provider we created earlier along with the Reports property. This property returns the element collection and is used as a data source for our Xamarin Grid.

using System.Collections.ObjectModel;
using Logify.Services;

namespace Logify.ViewModels {
    public class ReportViewModel : NotificationObject {
        ReportDataProvider dataProvider;
        public ObservableCollection<Report> Reports => dataProvider?.Reports;

        public ReportViewModel() {
            dataProvider = new ReportDataProvider();
            dataProvider.Load();
        }
    }
}

Binding to Data

To proceed, we need to assign an instance of ReportViewModel to the BindingContext of the view:

<ContentPage.BindingContext>
    <vm:ReportViewModel/>
</ContentPage.BindingContext>

Test the view and check the first few reports.

Implementing the LoadMore Method

The LoadMore Data Provider method allows us to employ “lazy” loading. Lazy data loading improves application speed because data is loaded in small/incremental batches.

LoadMore is the perfect option for those who are primarily concerned with app execution speed. Common use cases for this UX includes apps that display news feed, timelines or direct message dialogs (social networks, messengers, etc.). The bottom-line is this: With LoadMore, you can improve loading speed via the Grid’s infinite scroll UI.

To use LoadMore and partially load data, we must create a LoadMoreCommand in the view model. Each time the command executes, a new data batch is loaded. The IsRefreshing property sends a notification to the grid each time a new batch loads within the UX.

using System.Collections.ObjectModel;
using System.Windows.Input;
using Logify.Services;
using Xamarin.Forms;

namespace Logify.ViewModels {
    public class ReportsViewModel : NotificationObject {
        ReportsDataProvider dataProvider;
        bool isRefreshing = false;
        public ICommand LoadMoreCommand { get; private set; }

        public bool IsRefreshing {
            get => isRefreshing;
            set => SetProperty(ref isRefreshing, value);
        }

        public ReportsViewModel() {
            dataProvider = new ReportDataProvider();
            LoadMoreCommand = new Command(ExecuteLoadMoreCommand, () => !IsRefreshing);
            ExecuteLoadMoreCommand();
        }

        void ExecuteLoadMoreCommand() {
            dataProvider.Load(() => {
                IsRefreshing = false;
            });
        }
        ...
    }
}

To load data initially, we call a command that uses the Load method.

To enable the LoadMore function for the grid, we set the IsLoadMoreEnabled to ‘true’ and bind the LoadMoreCommand and IsRefreshing properties.

<dxg:DataGridView ItemsSource="{Binding Reports}"
    IsLoadMoreEnabled="True"
    LoadMoreCommand="{Binding LoadMoreCommand}"
    IsRefreshing="{Binding IsRefreshing, Mode=TwoWay}">
    ...
</dxg:DataGridView>

Image of our current reports module prior to Data Grid customizations:

With that, we’ll wrap up this post – in our next tutorial, we’ll customize our Xamarin Grid and improve the overall usability of the Logify client.

Should you have any questions or would you like to share your Xamarin experiences with our team, do not hesitate to contact us at info@devexpress.com or leave your comments below.

3 comment(s)
Jose Javier Columbie
Jose Javier Columbie

Hello Grigoriy, 

Are you guys planning on releasing the source code of this app?

Great work btw

26 August, 2019
Grigoriy V (DevExpress)
Grigoriy V (DevExpress)

@Jose

Thanks for your feedback.

Yes, we plan to make this application's source code available in our public GitHub repository after we release v19.2 of our Xamarin.Forms UI components.

28 August, 2019
Jose Javier Columbie
Jose Javier Columbie
Awesome Grigoriy. I look forward to it
28 August, 2019

Please login or register to post comments.