Archive

Archive for the ‘Xamarin’ Category

Xamarin.Forms and Azure Mobile apps on ioProgrammo

08/09/2016 1 comment

I have written a new article for the n°206 of ioProgrammo (September 2016). This time I talk about Xamarin.Forms and how to use it together with Azure Mobile apps in order to create an app that is strictly integrated with Facebook.

ioProgrammo September 2016

ioProgrammo September 2016

Advertisements
Categories: .NET, Azure, C#, General, Xamarin

A simple NavigationService for Xamarin.Forms

11/07/2016 1 comment

If we do a search on the Internet, we’ll find a lot of implementations of a NavigationService for Xamarin.Forms. While all of them are surely valid, there are two things that I don’t like much:

  • if we want to pass arguments to the new page, we need to use the constructor of the page itself (i.e., adding an object parameter to it);
  • they don’t provide a convenient way to clear navigation history, that is often a requirement (i.e., after login/registration).

So, I ended up writing my own implementation. It is very simple and doesn’t cover all the scenarios, but at this moment it completely satifies my need:

public class NavigationService
{
    private Dictionary<string, Type> pages { get; }
        = new Dictionary<string, Type>();

    public Page MainPage => Application.Current.MainPage;

    public void Configure(string key, Type pageType) => pages[key] = pageType;

    public void GoBack() => MainPage.Navigation.PopAsync();

    public void NavigateTo(string pageKey, object parameter = null,
        HistoryBehavior historyBehavior = HistoryBehavior.Default)
    {
        Type pageType;
        if (pages.TryGetValue(pageKey, out pageType))
        {
            var displayPage = (Page)Activator.CreateInstance(pageType);
            displayPage.SetNavigationArgs(parameter);

            if (historyBehavior == HistoryBehavior.ClearHistory)
            {
                MainPage.Navigation.InsertPageBefore(displayPage,
                    MainPage.Navigation.NavigationStack[0]);

                var existingPages = MainPage.Navigation.NavigationStack.ToList();
                for (int i = 1; i < existingPages.Count; i++)
                    MainPage.Navigation.RemovePage(existingPages[i]);
            }
            else
            {
                MainPage.Navigation.PushAsync(displayPage);
            }
        }
        else
        {
            throw new ArgumentException($"No such page: {pageKey}.",
                nameof(pageKey));
        }
    }
}

public enum HistoryBehavior
{
    Default,
    ClearHistory
}

public static class NavigationExtensions
{
    private static ConditionalWeakTable<Page, object> arguments
        = new ConditionalWeakTable<Page, object>();

    public static object GetNavigationArgs(this Page page)
    {
        object argument = null;
        arguments.TryGetValue(page, out argument);

        return argument;
    }

    public static void SetNavigationArgs(this Page page, object args)
        => arguments.Add(page, args);
}

The idea of this NavigationService is to first register all the pages using the Configure method (line 8), just like the MVVM Light approach. The core of the implementation is the NavigateTo method (lines 12-41). It tries to get the page type corresponding the passed key (line 16) and then instantiate it using Activator.CreateInstance.

This is the first difference with other solutions: we create the page using its default parameterless constructor. The actual parameter, if any, is stored in a kind of Dictionary using the SetNavigationArgs extension method at line 19 (its implementation is at lines 62-63).

Then, we check whether we want to clear history after navigation: if this case, we insert the new page before the current one (lines 23-24) and then, at lines 26-28, we remove all the other pages from the stack. Otherwise, at line 32 we just perform a normal PushAsync of the page.

Let’s see how to use it. First of all, we configure the NavigationService in the App class:

public partial class App : Application
{
    public static NavigationService NavigationService { get; }
        = new NavigationService();

    public App()
    {
        InitializeComponent();

        NavigationService.Configure("MainPage", typeof(MainPage));
        NavigationService.Configure("SecondPage", typeof(SecondPage));
        NavigationService.Configure("ThirdPage", typeof(ThirdPage));

        MainPage = new NavigationPage(new MainPage());
    }
}

Then, in MainPage.xaml we write something like this:

<StackLayout Margin="12" HorizontalOptions="Center">
  <Entry x:Name="arguments" Placeholder="Arguments..." />
  <StackLayout Orientation="Horizontal" Spacing="0">
    <Label Text="Clear History" VerticalOptions="Center" Margin="0,0,10,0" />
    <Switch x:Name="clearHistory" VerticalOptions="Center" />
  </StackLayout>
  <Button x:Name="navigate" Text="Go to second page" Clicked="navigate_Clicked" />
</StackLayout>

So we have an Entry that allows to specify arguments to be passed to the target (line 2), a Switch to clear history after navigation (line 5) and a Button to actually go to the second page (line 7). The corresponding code-behind follows:

private void navigate_Clicked(object sender, EventArgs e)
{
    var historyBehavior = clearHistory.IsToggled
        ? HistoryBehavior.ClearHistory : HistoryBehavior.Default;

    App.NavigationService.NavigateTo("SecondPage", arguments.Text, 
        historyBehavior);
}

We simply get the HistoryBehavior based on Switch value and then we pass it along with the target page and the arguments to the NavigateTo method. Finally, in the Second Page we override the OnAppearing method:

protected override void OnAppearing()
{
    var args = this.GetNavigationArgs();
    passedArguments.Text = $"Passed arguments: {args}";

    base.OnAppearing();
}

Using the GetNavigationArgs extension method, at line 3 we retrieve the arguments we have specified with NavigateTo.

Another interesting point about this solution is that it can easily be integrated with an MVVM approach, but we’ll go deeper in this argument in a future post.

You can download the complete example using the link below:
A simple NavigationService for Xamarin.Forms

Categories: C#, Xamarin

Clear history in Xamarin Android with MVVM Light NavigationService

14/03/2016 Comments off

If we’re working with Xamarin, MVVM Light provides quite useful helpers (derived from the Windows implementation) that simplify a lot our work. Among the others, we have a INavigationService class that abstracts the concept of starting a new Activity (Android) rather than activating a certain Controller (iOS), providing a unique approach to page navigation.

This approach is general and the interface is the same on all the platforms. While this helps having a common navigation system, it can leads to platform specific problem. Imagine we are on Android and we need to delete history (for example, after a login). On this platform, navigation history can be deleted only in conjunction with the start of an Activity (i.e., we need to tell Android to delete any existing task before starting the Activity). But if we’re using INavigationService, we have a NavigateTo method to which we can pass only the pageKey and an optional object that will be received by the new activity. We haven’t control on how the new activity is started. So, how can we delete history?

We can tweak the original NavigationService implementation for Android to handle this scenario. First of all, we need to define a NavigationParameter class:

public class NavigationParameter
{
    public ActivityFlags? Flags { get; }

    public object Content { get; }

    public NavigationParameter(ActivityFlags? flags = null)
    {
        Flags = flags;
    }

    public NavigationParameter(object content, ActivityFlags? flags = null)
    {
        Content = content;
        Flags = flags;
    }
}

It allows us to pass custom activation flags rather the a single object to the NavigateTo method. To use this class, a little modification to NavigationService.cs file is requested. Let’s get the original implementation from CodePlex. Then, locate the following code in the NavigateTo(string pageKey, object parameter) method:

if (parameter != null)
{
    lock (_parametersByKey)
    {
        // ...
    }
}

We need to replace it with the following code:

if (parameter != null)
{
    lock (_parametersByKey)
    {
        var guid = Guid.NewGuid().ToString();

        var flags = (parameter as NavigationParameter)?.Flags;
        var actualParameter = (parameter as NavigationParameter)?.Content 
            ?? parameter;

        _parametersByKey.Add(guid, actualParameter);
        intent.PutExtra(ParameterKeyName, guid);

        if (flags.HasValue)
            intent.SetFlags(flags.Value);
    }
}

The core of the change is at lines 7-9. We check if parameter is a NavigationParameter: in this case, we extract the activation flags and the actual parameter; otherwise, we assume we don’t have any activation flag and that the actual parameter is the parameter argument itself. So, we can continue to use the NavigateTo method in the usual way (no breaking changes). Then, at lines 14-15, if flags has value, we pass it to the Intent.SetFlags method.

So, when we need to clear history, we just need to create a NavigationParameter with the correct activation flags, like in the following example:

private NavigationParameter CreateParameter<T>(T parameter, bool clearHistory)
{
    var flags = clearHistory ? (ActivityFlags.NewTask | ActivityFlags.ClearTask) 
        : default(ActivityFlags);
    var args = new NavigationParameter(parameter, flags);

    return args;
}
        
private void NavigateToHomePage<T>(T parameter)
{
    var args = this.CreateParameter(parameter, clearHistory: true);
    navigationService.NavigateTo(Constants.HomePage, args);
}

Note that, as we use NavigationParamenter only to pass activation flags and the new activity receives always the actual parameter, the GetAndRemoveParameter(Intent) method will keep working in the usual way. Moreover, as already said, we can continue to use NavigateTo method as before if we don’t need to clear history.

Categories: C#, MVVM, Xamarin