Xamarin UI Controls - Case Study: Building the Error Monitoring Client App (Part 2)

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 Xamarin.Forms UI Controls. In this part, we’ll describe how portions of our Error Monitoring 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: To reserve your free copy of our Xamarin Forms UI suite, simply point your browser to: https://www.devexpress.com/xamarin-free/. If you have never seen or used our Xamarin Forms UI controls, please review our YouTube videos and our online documentation for more information.

Building the Report List View

If you’ve used an error monitoring service, you know the importance of reports – the means by which exception information is displayed.

To display exception 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 Error Monitoring 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 Error Monitoring 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 Error Monitoring 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.

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.
No Comments

Please login or register to post comments.