ViewModel

Create scrollable tabs in Xamarin.Forms with CollectionView and CarouselView

Create scrollable tabs in Xamarin.Forms with CollectionView and CarouselView

When it comes to navigation patterns in mobile apps, the tabbed interface is one of the most popular options. While Xamarin.Forms has the TabbedPage (and Shell) to fulfill that need, it lacks one essential feature: scrollable tabs. After studying some of the samples floating around the web and some of the packages that provide such functionality, I tried find an easier solution.

The View

Let’s have a look at the View first. Like you may have guessed from the title, we are using a CollectionView for the tabs and a CarouselView for the Content. This combination makes it quite easy to implement tabs that cover a whole page size or smaller ones within a page.

Here’s the XAML:

<Grid x:DataType="{x:Null}" RowSpacing="0">
    <Grid.RowDefinitions>
        <RowDefinition Height="45" />
        <RowDefinition Height="45" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <CollectionView
        x:Name="CustomTabsView"
        Grid.Row="1"
        HorizontalScrollBarVisibility="Never"
        ItemSizingStrategy="MeasureAllItems"
        ItemsSource="{Binding TabVms}"
        ItemsUpdatingScrollMode="KeepItemsInView"
        SelectedItem="{Binding CurrentTabVm, Mode=TwoWay}"
        SelectionMode="Single"
        VerticalScrollBarVisibility="Never">
        <CollectionView.ItemsLayout>
            <LinearItemsLayout Orientation="Horizontal" />
        </CollectionView.ItemsLayout>
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="local:TabViewModel">
                <Grid RowSpacing="0">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                        <RowDefinition Height="3" />
                    </Grid.RowDefinitions>
                    <Label
                        x:Name="TitleLabel"
                        Grid.Row="0"
                        Padding="15,0"
                        FontAttributes="Bold"
                        FontSize="Small"
                        HeightRequest="50"
                        HorizontalTextAlignment="Center"
                        Text="{Binding Title}"
                        TextColor="White"
                        VerticalTextAlignment="Center" />
                    <BoxView
                        x:Name="ActiveIndicator"
                        Grid.Row="1"
                        BackgroundColor="Red"
                        IsVisible="{Binding IsSelected, Mode=TwoWay}" />
                </Grid>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>

    <CarouselView
        Grid.Row="2"
        CurrentItem="{Binding CurrentTabVm, Mode=TwoWay}"
        CurrentItemChanged="CarouselView_CurrentItemChanged"
        HorizontalScrollBarVisibility="Never"
        IsScrollAnimated="True"
        IsSwipeEnabled="True"
        ItemsSource="{Binding TabVms}"
        VerticalScrollBarVisibility="Never">
        <CarouselView.ItemTemplate>
            <DataTemplate x:DataType="local:TabViewModel">
                <Grid Margin="10">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Label
                        Grid.Row="0"
                        Margin="10"
                        LineBreakMode="WordWrap"
                        Text="{Binding Content}"
                        VerticalTextAlignment="Center" />
                </Grid>
            </DataTemplate>
        </CarouselView.ItemTemplate>
    </CarouselView>
</Grid>

Let me break that piece down. First, I wrapped everything in a Grid for this sample. The CollectionView of course should be horizontally scrolling but should not show any scroll bar. The tab item template is not a complex one – it is just a Label and a BoxView below it to help with indication of the selection. You are free to make the tab looking whatever you want because of the CollectionView, however.

Below that, we put a CarouselView. For this sample, I just made a simple one with a Lorem Ipsum Label in it on every item.

The ViewModels

Most of you know that I absolutely love the MVVM pattern. And this sample proves me right once again. We need just need two ViewModels to handle scrolling and synchronizing.

The first ViewModel is the TabViewModel:

Snippet

public class TabViewModel : ObservableObject
{
    private string _title;
    private string _content;
    private bool _isSelected;

    public TabViewModel(string title)
    {
        this.Title = title;
        this.Content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Tempor id eu nisl nunc mi ipsum faucibus vitae aliquet. Turpis egestas integer eget aliquet nibh praesent tristique magna. In fermentum posuere urna nec tincidunt. Vitae congue eu consequat ac felis donec et odio pellentesque. Augue lacus viverra vitae congue. Viverra vitae congue eu consequat. Orci nulla pellentesque dignissim enim sit amet venenatis urna. Et ultrices neque ornare aenean euismod elementum nisi. Id consectetur purus ut faucibus pulvinar. In cursus turpis massa tincidunt. Egestas pretium aenean pharetra magna. Et pharetra pharetra massa massa ultricies mi quis. Nunc sed blandit libero volutpat. Purus viverra accumsan in nisl nisi scelerisque eu ultrices vitae.";
    }

    public string Title { get => _title; set => Set(ref _title, value); }

    public string Content { get => _content; set => Set(ref _content, value); }

    public bool IsSelected { get => _isSelected; set =>Set(ref _isSelected, value); }
}

The TabViewModel in this sample just has the bare minimum, namely the Title, the Content and the IsSelectedFlag to control the Visibility of the Indicator-BoxView. Nothing dramatic so far.

The MainViewModel glues everything together, so let’s have a look:

Snippet

public class MainViewModel : ObservableObject
{
    private TabViewModel _currentTabVm;

    public MainViewModel()
    {

        this.TabVms = new ObservableCollection<TabViewModel>();
        this.TabVms.Add(new TabViewModel("Short Title"));
        this.TabVms.Add(new TabViewModel("A Little Longer Title"));
        this.TabVms.Add(new TabViewModel("An Even Longer Title Than Before"));
        this.TabVms.Add(new TabViewModel("Again Short Title"));
        this.TabVms.Add(new TabViewModel("Mini Title"));
        this.TabVms.Add(new TabViewModel("Different Title"));

        this.CurrentTabVm = this.TabVms.FirstOrDefault();
    }


    public ObservableCollection<TabViewModel> TabVms { get; set; } 

    public TabViewModel CurrentTabVm 
    { 
        get => _currentTabVm;
        set
        {
            Set(ref _currentTabVm, value);
            SetSelection();
        }
    }

    private void SetSelection()
    {
        this.TabVms.ForEach(vm => vm.IsSelected = false);
        this.CurrentTabVm.IsSelected = true;
    }
}

Once again, there is nothing complex in it. We are mocking a collection of TabViewModel and handle the tab selection via Binding. After the current item got selected, we are setting the IsSelected property on it to true to show the Indicator in the CollectionView.

For this sample, I didn’t use a fully blown MVVM framework, so I am setting the BindingContext in the MainPage‘s constructor. The Binding engine in Xamarin.Forms already does almost everything to make this work.

The only thing left is to handle the positioning of the tabs if we are swiping the CarouselView. As this is purely View related, I am using the CurrentItemChanged event in code behind to center the CollectionView‘s selected item:

Snippet

private void CarouselView_CurrentItemChanged(object sender, CurrentItemChangedEventArgs e)
{
    this.CustomTabsView.ScrollTo(e.CurrentItem, null, ScrollToPosition.Center, true);
}

The result of this setup looks like this:

Conclusion

Xamarin.Forms provides a lot of solutions out of the box. Sometimes, however, these are not enough. Luckily, we can combine some of the solutions the framework provides to create fresh solutions within our apps. This post showed one of these. The additional bonus you get with this implementation is the ability to style the tabs in whatever way you want. As always, I hope this post will be helpful for some of you.

Of course, there is also sample for this post on Github.

Until the next post, happy coding, everyone!
Posted by msicc in Dev Stories, Xamarin, 2 comments
Scroll to any item in your Xamarin.Forms CollectionView from your ViewModel

Scroll to any item in your Xamarin.Forms CollectionView from your ViewModel

If you are working with collections in your app, chances are high you are going to want (or need) to scroll to a specific item at some point. CollectionView has the ScrollTo method that allows you to do so. If you are using MVVM in your app however, there is no built-in support to call this method.

My solution

My solution for this challenge consists of following parts:

  • a BindableProperty in an extended CollectionView class to bind the item we want to scroll to
  • a configuration class to control the scrolling behavior
  • a base interface with the configuration and two variants derived from it (one for ungrouped items, one for grouped ones)

Let’s have a look at the ScrollConfiguration class:

public class ScrollToConfiguration
{
    public bool Animated { get; set; } = true;

    public ScrollToPosition ScrollToPosition { get; set; } = ScrollToPosition.Center;
}

These two properties are used to tell our extended CollectionView how the scrolling to the item will behave. The above default values are my preferred ones, feel free to change them in your implementation.

Next, let us have a look at the base interface:

public interface IConfigurableScrollItem
{
    ScrollToConfiguration Config { get; set; }
}

Then we will define two additional interfaces which we are going to use later in our ViewModel:

    public interface IScrollItem : IConfigurableScrollItem
    {
    }

    public interface IGroupScrollItem : IConfigurableScrollItem
    {
        object GroupValue { get; set; }
    }

For a non-grouped CollectionView, we just need to implement IScrollItem. If we have groups, we’ll use IGroupScrollItem to add an object that identifies the group (following the Xamarin.Forms API here).

Extending CollectionView

Let’s connect the dots and implement an extended version of the CollectionView – to do so, create a new class and derive from it. I named mine CollectionViewEx (ingenious, right?).

To wrap things up, we now add a BindableProperty with a PropertyChanged handler to our CollectionViewEx that we can bind against, and which is, most importantly, calling the ScrollTo method of CollectionView.

Here is the full class:

public class CollectionViewEx : CollectionView
{
    public static BindableProperty ScrollToItemWithConfigProperty = BindableProperty.Create(nameof(ScrollToItemWithConfig), typeof(IConfigurableScrollItem), typeof(CollectionViewEx), default(IConfigurableScrollItem), BindingMode.Default, propertyChanged: OnScrollToItemWithConfigPropertyChanged);

    public IConfigurableScrollItem ScrollToItemWithConfig
    {
        get => (IConfigurableScrollItem)GetValue(ScrollToItemWithConfigProperty);
        set => SetValue(ScrollToItemWithConfigProperty, value);
    }

    private static void OnScrollToItemWithConfigPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (newValue == null)
            return;

        if (bindable is CollectionViewEx current)
        {
            if (newValue is IGroupScrollItem scrollToItemWithGroup)
            {
                if (scrollToItemWithGroup.Config == null)
                    scrollToItemWithGroup.Config = new ScrollToConfiguration();

                    current.ScrollTo(scrollToItemWithGroup, scrollToItemWithGroup.GroupValue, scrollToItemWithGroup.Config.ScrollToPosition, scrollToItemWithGroup.Config.Animated);

            }
            else if (newValue is IScrollItem scrollToItem)
            {
                if (scrollToItem.Config == null)
                    scrollToItem.Config = new ScrollToConfiguration();

                    current.ScrollTo(scrollToItem, null, scrollToItem.Config.ScrollToPosition, scrollToItem.Config.Animated);
            }
        }
    }
}

Let’s go through the code. The BindableProperty implementation should be common to most of us (if not, read up the docs). The most important part happens in the PropertyChanged handler.

By allowing the value of the BindableProperty to be null, we can reset the item and scroll to the same item again if necessary. Because IScrollItem as well as IGroupScrollItem derive from IConfigurableScrollItem, we can handle them both in one method. To make sure there is a default ScrollToConfiguration, I am checking the Config property for null – in case it is (because I forgot it), there is at least the default. In the end, I am scrolling to the Item in the CollectionView using the ScrollTo method.

The ViewModel(s) and Binding

Here is one of the (simple) ViewModels from the sample application for this post:

public class ItemViewModel : ViewModelBase, IScrollItem
{
    public ItemViewModel()
    {
        this.Config = new ScrollToConfiguration();
    }

    public string Text { get; set; }

    public int Number { get; set; }

    public ScrollToConfiguration Config { get; set; }
}

Now in the parent ViewModel, we just add another property that we can use to bind against the CollectionViewEx‘s ScrollToItemWithConfig property. The Binding is straight forward:

<controls:CollectionViewEx
    Grid.Row="3"
    Margin="6"
    ItemsSource="{Binding ScrollableItems}"
    ScrollToItemWithConfig="{Binding ScrollToVm}"
    SelectedItem="{Binding SelectedItemVm, Mode=TwoWay}"
    SelectionMode="Single">
    <controls:CollectionViewEx.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Label Margin="5,10" Text="{Binding Text}" />
            </Grid>
        </DataTemplate>
    </controls:CollectionViewEx.ItemTemplate>
</controls:CollectionViewEx>

The result of this whole exercise looks like this:

Conclusion

Even if the CollectionView control in Xamarin.Forms provides a whole bunch of optimized functionalities over ListView, there are some scenarios that require additional work. Luckily, it isn’t that hard to extend the CollectionView. Scrolling to a precise ViewModel is easy with the code above. Of course, I created also a sample Github repo.

As always, I hope this post will be helpful for some of you.

Until the next post, happy coding, everyone!
Posted by msicc in Dev Stories, Xamarin, 1 comment
Xamarin Forms, the MVVMLight Toolkit and I: Command Chaining

Xamarin Forms, the MVVMLight Toolkit and I: Command Chaining

The problem

Sometimes, we want to invoke a method that is available via code for a control. Due to the abstraction of our MVVM application, the ViewModel has no access to all those methods that are available if we would access the control via code. There are several approaches to solve this problem. In one of my recent projects, I needed to invoke a method of a custom control, which should be routed into the platform renderers I wrote for such a custom control. I remembered that I have indeed read quite a few times about command chaining for such cases and tried to implement it. In the beginning, it may sound weird to do this, but the more often I see this technique, the more I like it.

Simple Demo control

For demo purposes, I created this really simple Xamarin.Forms user control:

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XfMvvmLight.Controls.CommandChainingDemoControl">
  <ContentView.Content>
      <StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
            <Label x:Name="LabelFilledFromBehind" Margin="12" FontSize="Large" />   
        </StackLayout>
  </ContentView.Content>
</ContentView>

As you can see, there is just a label without text. We will write the necessary code to fill this label with some text just by invoking a method in the code behind. To be able to do so, we need a BindableProperty (once again) to get our foot into the door of the control:

public static BindableProperty DemoCommandProperty = BindableProperty.Create(nameof(DemoCommand), typeof(ICommand), typeof(CommandChainingDemoControl), null, BindingMode.OneWayToSource);

public ICommand DemoCommand
{
    get => (ICommand)GetValue(DemoCommandProperty);
    set => SetValue(DemoCommandProperty, value);
}

The implementation is pretty straightforward. We have done this already during this series, so you should be familiar if you were following. One thing, however, is different. For this BindableProperty, we are using BindingMode.OneWayToSource. By doing so, we are basically making it a read-only property, which sends its changes only down to the ViewModel (the source). If we would not do this, the ViewModel could change the property, which we do not want here.

Now we have the BindableProperty in place, we need to create an instance of the Command that will be sent down to the ViewModel. We are doing this as soon as the control is instantiated in the constructor:

public CommandChainingDemoControl()
      {
          InitializeComponent();

          this.DemoCommand = new Command(() =>
          {
              FillFromBehind();
          });
      }

      private void FillFromBehind()
      {
          this.LabelFilledFromBehind.Text = "Text was empty, but we used command chaining to show this text inside a control.";
      }

That’s all we need to do in the code behind.

ViewModel

For this demo, I created a new page and a corresponding ViewModel in the demo project. Here is the very basic ViewModel code:

using System.Windows.Input;
using GalaSoft.MvvmLight.Command;

namespace XfMvvmLight.ViewModel
{
    public class CommandChainingDemoViewModel : XfNavViewModelBase
    {
        private ICommand _invokeDemoCommand;
        private RelayCommand _demo1Command;

        public CommandChainingDemoViewModel()
        {
        }

        public ICommand InvokeDemoCommand { get => _invokeDemoCommand; set => Set(ref _invokeDemoCommand, value); }

        public RelayCommand Demo1Command => _demo1Command ?? (_demo1Command = new RelayCommand(() =>
        {
            this.InvokeDemoCommand?.Execute(null);
        }));
    }
}

As you can see, the ViewModel includes two Commands. One is the pure ICommand implementation that gets its value from the OneWayToSource-Binding. We are not using MVVMLight’s RelayCommand here to avoid casting between types, which always led to an exception when I tested the implementation first. The second command is bound to a button in the CommandChainingDemoPage and will be the trigger to execute the InvokeDemoCommand.

Final steps

The final steps are just a few simple ones. We need to connect the  InvokeDemoCommand to the user control we created earlier, while we need to bind the Demo1Commandto the corresponding button in the view. This is the page’s code after doing so:

<?xml version="1.0" encoding="utf-8" ?>
<baseCtrl:XfNavContentPage
    xmlns:baseCtrl="clr-namespace:XfMvvmLight.BaseControls" xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:ctrl="clr-namespace:XfMvvmLight.Controls;assembly=XfMvvmLight"
             x:Class="XfMvvmLight.View.CommandChainingDemoPage" RegisteredPageKey="{Binding CommandChainingDemoPageKey, Source=Locator}">
    <ContentPage.BindingContext>
        <Binding Path="CommandChainingDemoVm" Source="{StaticResource Locator}" />
    </ContentPage.BindingContext>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <ctrl:CommandChainingDemoControl Grid.Row="0" DemoCommand="{Binding InvokeDemoCommand, Mode=OneWayToSource}" Margin="12"></ctrl:CommandChainingDemoControl>

        <Button Text="Execute Command Chaining" Command="{Binding Demo1Command}" Margin="12" Grid.Row="1" />

    </Grid>
</baseCtrl:XfNavContentPage>

One thing to point out is that we are also specifying the OneWayToSource binding here once again. It should work with normal binding, but it I recommend to do like I did, which makes the code easier to understand for others (and of course yourself). That’s all – we have now a working command chain that invokes a method inside the user control from our ViewModel.

Conclusion

Command chaining can be a convenient way to invoke actions on controls that are otherwise not possible due to the abstraction of layers in MVVM. Once you got the concept, they are pretty easy to implement. This technique is also usable outside of Xamarin.Forms, so do not hesitate to use it out there. Just remember the needed steps:

  • create a user control (or a derived one if you need to call a method on framework controls)
  • add a BindableProperty/DependecyProperty and set its default binding mode to OneWayToSource
  • instantiate the BindableProperty/DependecyProperty inside the constructor of the user control
  • pass the method call/code into the Action part of the newly created Command  instance
  • create the commands in the ViewModel
  • connect the Commands to your final view implementation

Like I wrote earlier, I came across this (again) when I was writing a custom Xamarin.Forms control with renderers, where I had to invoke methods inside the renderer from my ViewModel. Other techniques that I saw to solve this is using Messengers (be it the one from MVVMLight or the Xamarin.Forms Messenger implementation) or the good old Boolean switch implementation (uses also a BindableProperty/DependecyProperty). I decided to use the command chaining approach as it is pretty elegant in my eyes and not that complicated to implement.

The series’ sample project is updated and available here on Github. Like always, I hope this post is useful for some of you.

Happy coding, everyone!


all articles of this series

title image credit

Posted by msicc in Android, Dev Stories, iOS, UWP, Xamarin, 7 comments
Xamarin Forms, the MVVMLight Toolkit and I: EventToCommandBehavior

Xamarin Forms, the MVVMLight Toolkit and I: EventToCommandBehavior

Often, we want to/need to know when views throw certain events. However, due to using the MVVM pattern, our application logic is separated from the view. There are several ways to get those events into our ViewModel while keeping it separated from the views. One of those is using an interface, which I showed you already in my blog post about navigation in Xamarin.Forms with MVVMLight.

Another way is the good old EventToCommand approach. Some of you might have used this approach already in WPF and other .NET applications. Xamarin.Forms has them too, this post will show you how to implement it.

Xamarin.Forms Behaviors

In Windows applications like WPF or UWP, we normally use the Interactivity namespace to use behaviors. Xamarin.Forms however has its own implementation, so we need to use the Behavior and Behavior<T> classes. All controls that derive from View are providing this BindableProperty, so we can use Behaviors in a lot of scenarios. Until the new XAML Standard is finally defined, we have to deal with this.

EventToCommandBehavior

Xamarin provides a nearly ready-to-use EventToCommandBehavior implementation and an quite detailed explanation (which is why I won’t go into details on that). The implementation has two part – the BehaviorBase<T>implementation and the EventToCommandBehavior implementation itself.

While we are able to use the BehaviorBase<T> implementation as is, we have to do some minor changes to the EventToCommandBehavior to enable a few more usage scenarios.

The first change we need to make is to derive Xamarin’s EventToCommandBehavior sample from VisualElement instead of View. This way, we can also use the behavior on controls that do not derive from View, especially in Pages. Pages do not derive from View, but they do from VisualElement (like Viewdoes, too). You need to change the Type also on the parameter of the OnAttachedTo and OnDetachingFrom methods in this case (which are the other two changes we need to do).

The rest of the implementation is basically the same like in the Xamarin sample and works quite well.

To show you a simple sample in Action, we are using the Appearing and Disappearing events to attach them via the behavior into our ModalPageViewModelon the ModalPage we integrated before. This way, you won’t need the IViewEventBrokerService I showed you in my post on navigation and modal pages. It is up to you to choose the way you want to go along, both ways are fully respecting the MVVM pattern.

Implementation

The implementation has two parts. As we want to handle the events in a Command, the first step to take is to implement two Commands in the corresponding ViewModel. I am using a base implementation (in my apps and also in this sample), so I am going to implement the Commands there. This way, every derived ViewModel can bind to this Command. Additionally, I am using a Execute...Command method and a CanExecute boolean method, which can both be overriden in derived ViewModels to implement the code to execute. Let’s have a look at the code:

public RelayCommand ViewAppearingCommand => _viewAppearingCommand ?? (_viewAppearingCommand = new RelayCommand(ExecuteViewAppearingCommand, CanExecuteViewAppearingCommand));

public virtual void ExecuteViewAppearingCommand()
{

}

public virtual bool CanExecuteViewAppearingCommand()
{
    return true;
}

public RelayCommand ViewDisappearingCommand => _viewDisappearingCommand ?? (_viewDisappearingCommand = new RelayCommand(ExecuteViewDisappearingCommand, CanExecuteViewDisappearingCommand));

public virtual void ExecuteViewDisappearingCommand()
{

}

public virtual bool CanExecuteViewDisappearingCommand()
{
    return true;
}

The second part is the XAML part, which includes the Binding to the Command properties we just created. The implementation is as easy as these four lines for both events:

    <baseCtrl:XfNavContentPage.Behaviors>
        <behaviors:EventToCommandBehavior EventName="Appearing" Command="{Binding ViewAppearingCommand}"></behaviors:EventToCommandBehavior>
        <behaviors:EventToCommandBehavior EventName="Disappearing" Command="{Binding ViewDisappearingCommand}"></behaviors:EventToCommandBehavior>
    </baseCtrl:XfNavContentPage.Behaviors>

That’s it, if you want to attach the behavior only for individual Pages. If you have a base page implementation like I do however, you can automatically attach the event already there to have it attached to all pages:

private void XfNavContentPage_BindingContextChanged(object sender, EventArgs e)
{
    if (this.BindingContext is XfNavViewModelBase)
    {
        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "Appearing",
            Command = ((XfNavViewModelBase)this.BindingContext).ViewAppearingCommand
        });
 
        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "Disappearing",
            Command = ((XfNavViewModelBase)this.BindingContext).ViewDisappearingCommand
        });
    }
}

I am attaching the behaviors only if the BindingContextdoes derive from my XfNavViewModelBase. The Command can be set directly in this case, without the need to use the SetBinding method.

These few lines are connecting the Event to the Command, the only thing we need to do is to override the base implementations of the “Execute…Command” methods:

public override async void ExecuteViewAppearingCommand()
{
    base.ExecuteViewAppearingCommand();
    await _dialogService.ShowMessageAsync(this.CorrespondingViewKey, $"from overriden {nameof(ExecuteViewAppearingCommand)}");
}
 
public override async void ExecuteViewDisappearingCommand()
{
    base.ExecuteViewDisappearingCommand();
    await _dialogService.ShowMessageAsync(this.CorrespondingViewKey, $"from overriden {nameof(ExecuteViewDisappearingCommand)}");
}

The above overrides are using the IDialogService you will find in the sample application to show a simple message from which overriden Execute...Command method they are created from.

Converting EventArgs to specific types

Xamarin.Forms has only a few events that have usefull EventArgs. At the time of writing this post, I tried to find valid scenarios where we want to get some things of the events to attach also an IValueConverterimplementation to get this data out of them. Fact is, the only one I ever used is the one from the Xamarin sample – which is a converter that gets the selected Item for a ListView. Because Xamarin.Forms Views already provide most of the properties I ever needed, I was able to solve everything else via Binding. To make this post complete, you can have a look into Xamarin’s sample implementation here.

Conclusion

Hooking into events on the view side of our applications can be done in several ways. It is up to you to choose the route you want to go. With this post, I showed you a second way to achieve this.

If you have some more valid scenarios for using the EventToCommandBehaviorwith a Converter that cannot be solved via Binding directly, I would love to hear them. Feel free to leave a comment here or via social networks. Of course, I updated the sample on Github with the code from this post.

As always, I hope this post is helpful for some of you. Until the next post, happy coding!

Posted by msicc in Android, Dev Stories, iOS, UWP, Xamarin, 3 comments
[Updated] Xamarin Forms, the MVVMLight Toolkit and I: navigation and modal pages

[Updated] Xamarin Forms, the MVVMLight Toolkit and I: navigation and modal pages

After showing you the basic MVVMLight Setup I am using as well as how to combine Xamarin.Forms’ DependencyService with our own Dependecy Injection-mechanism, this article is all about Navigation and modal pages in Xamarin.Forms.

Some preliminary words

I always liked how the MVVMLight Toolkit provided the NavigationService for my Windows & Windows Phone applications. That said, I tried to keep the idea of its NavigationService and ported it over to my Xamarin.Forms structure. I also extended it to fit the needs of my XF apps, for example for pushing modal pages (we’ll see that later). So for the base idea, I have to say thanks to Laurent Bugnion for the Windows platform implementation in MVVMLight and for keeping it simple. The later fact allows one to easily extend and adapt the service.

Also, there are tons of really good and also working samples around the web to show navigation in Xamarin.Forms. However, most of them follow another strategy, so I came to the point where decided to write my own implementation. My way may work for most scenarios without making a difference between navigation and showing modal pages. It provides an easy implementation and usage while following the MVVM pattern as far as it can go with Xamarin.Forms.

Components

My implementation has some key components, which I will describe briefly in this post:

  • Interfaces for Navigation and view events
  • Implementation of these interfaces
  • using the implementations in views
  • using the implementations in ViewModels

Interfaces for the win!

If we want to follow the MVVM pattern, we need to keep our Views completely separated from our ViewModels. A common practice to achieve this goal is the usage of interfaces, and so do I. If you have read my previous blog post about Dependency Injection, you know already a bit about the things we have to do. First, we will create an interface for the navigation itself:

public interface IXfNavigationService
{
    void Initialize(NavigationPage navigation);
    void Configure(string pageKey, Type pageType);
    (bool isRegistered, bool isModal) StackContainsNavKey(string pageKey);
    Task GoHomeAsync();
    Task GoBackAsync();
    Task GoBackModalAsync();
    int ModalStackCount { get; }
    string CurrentModalPageKey { get; }
    Task ShowModalPageAsync(string pageKey, bool animated = true);
    Task ShowModalPageAsync(string pageKey, object parameter, bool animated = true);
    int NavigationStackCount { get; }
    string CurrentPageKey { get; }
    Task NavigateToAsync(string pageKey, bool animated = true);
    Task NavigateToAsync(string pageKey, object parameter, bool animated = true);
}

As you can see, it is a pretty big interface that covers nearly all possible navigation scenarios of a Xamarin.Forms app (at least those that I had to deal with already). We are going to hook into a Xamarin.Forms  NavigationPage and configure our page handling cache with the configure method. As all navigation methods in Xamarin Forms are async, all navigational tasks will be implemented async as well. Additionally, we will have some helpful properties that we can use in our application implementation.

In Windows applications, we have some additional events for pages like  Loaded or OnNavigatedTo(and so on). Xamarin.Forms pages have something similar: ViewAppearing and ViewDisappearing. These two events correspond to OnNavigatedTo and OnNavigatedFrom in a Windows application. Sometimes, we need to know when those events happen in our ViewModels or other places behind the scenes to get stuff done. That’s why I created an interface for this purpose:

public interface IViewEventBrokerService
{
    event EventHandler<ViewEventBrokerEventArgs> ViewAppearing;
    event EventHandler<ViewEventBrokerEventArgs> ViewDisAppearing;
    void RaiseViewAppearing(string pageKey, Type pageType, bool isModal);
    void RaiseViewDisAppearing(string pageKey, Type pageType, bool isModal);
}

I also created my own EventArgs to make it easier to pass all data around (as you can see above). Here is the class:

public class ViewEventBrokerEventArgs : EventArgs
{
    public ViewEventBrokerEventArgs(string pageKey, Type pageType, bool isModal)
    {
        PageKey = pageKey;
        PageType = pageType;
        IsModal = isModal;
    }
    public string PageKey { get; private set; }
    public Type PageType { get; private set; }
    public bool IsModal { get; private set; }
}

Now that we have our interfaces defined, we are going the next step and have a look into their implementations.

IXfNavigationService implementation

First it is important to understand what Xamarin.Forms does when we are using a NavigationPage. We are performing a hierarchical navigation here. A must read is this page from the Xamarin documentation.

So you’re back from reading the documentation? Great, then lets have a look what our XfNavigationServiceimplementation does. It builds on top of the Xamarin.Forms NavigationPage, which implements the INavigation interface. Other solutions I saw are pulling down this interface and implement it in their implementation. I prefer to keep it simple and just use the already implemented interface in the Xamarin.Forms NavigationPage, which is mandatory for my implementation. We are pulling it into our implementation with the Initialize(NavigationPage navigationPage) method:

public void Initialize(NavigationPage navigationPage)
{
    _navigationPage = navigationPage;
}

We could also use a constructor injection here. Most of the time it would work, but I had some problems to get the timing right under certain circumstances. That’s why I prefer to manually initialize it to overcome these timing problems  – and it does not really hurt in real life applications to have it a bit more under control.

Before we are going to see the actual implementation, I need to break out of this scope and explain a threading issue.

Locking the current executing thread

Often we are running a lot of code at the same time. This could bring up some issues with ourXfNavigationService. If multiple threads try to change or execute code from our interface implementation, the result may not the one we desired. To prevent this, we would normally use the lock statement to allow only one thread modifying our code. Problem with the lock statement is, that we cannot run asynchronous code within.

As asynchronous code execution removes some of the headache in programming, we need a solution for this. And there is one, utilizing the SemaphoreSlim class of the .NET framework. Basically, the SemaphoreSlim class provides a single application Semaphore implementation. Without going deeper into it, we are replacing the lock statement we would normally use with this. We just need to initialize the SemaphoreSlim class as a private static member:

//using this instead of lock statement
private static SemaphoreSlim _lock = new SemaphoreSlim(1, 1);

This will only allow one thread to access the objects we are passing after it, and before releasing it (which is our responsibility). The code itself is recommended to run in a try..finally  block (you’ll see that below). Pretty much the same thing the lock statement would do, but with the possibility to run asynchronous code.

Back to the implementation

The next step is to allow registering pages with a name and their type from the ViewModelLocator. I am doing the same as in the standard MVVMLight implementation here:

//declare private member for the registered pages cache
private readonly Dictionary<string, Type> _pagesByKey = new Dictionary<string, Type>();


public void Configure(string pageKey, Type pageType) 
{ 
    //synchronous lock 
    _lock.Wait(); 
    try 
    { 
        if (_pagesByKey.ContainsKey(pageKey)) 
        { 
            _pagesByKey[pageKey] = pageType; 
        } 
        else 
        { 
            _pagesByKey.Add(pageKey, pageType); 
        } 
    } 
    finally 
    { 
        _lock.Release(); 
    } 
} 

Once we have our pages configured (we’ll see that later in this post), we are easily able to identify our registered pages for navigation purposes. Sometimes we want to get the current Page, so we need to implement a property for that, as well as for the current page key and the current modal page key:

public Page GetCurrentPage()
{
    return _navigationPage?.CurrentPage;
}


public string CurrentPageKey 
{ 
    get 
    { 
        _lock.Wait(); 
        try 
        { 
            if (_navigationPage?.CurrentPage == null) 
            { 
                return null; 
            } 
  
            var pageType = _navigationPage.CurrentPage.GetType(); 
  
            return _pagesByKey.ContainsValue(pageType) 
                ? _pagesByKey.First(p => p.Value == pageType).Key 
                : null; 
        } 
        finally 
        { 
            _lock.Release(); 
        } 
    } 
} 


public string CurrentModalPageKey 
{ 
    get 
    { 
        _lock.Wait(); 
  
        try 
        { 
            if (ModalStackCount == 1) 
            { 
                return null; 
            } 
  
            //only INavigation holds the ModalStack 
            var pageType = _navigationPage.Navigation.ModalStack.Last().GetType(); 
  
            return _pagesByKey.ContainsValue(pageType) ? _pagesByKey.FirstOrDefault(p => p.Value == pageType).Key 
                : null; 
        } 
        finally 
        { 
            _lock.Release(); 
        } 
    } 
} 

I found myself in situations where I needed to check if I am on a modal page, so lets add a method for that:

public (bool isRegistered, bool isModal) StackContainsNavKey(string pageKey) 
{ 
  
    bool isUsedModal = false; 
    bool isRegistered = false; 
  
    _lock.Wait(); 
    try 
    { 
        isRegistered = _pagesByKey.ContainsKey(pageKey); 
  
  
        if (isRegistered) 
        { 
            var pageType = _pagesByKey.SingleOrDefault(p => p.Key == pageKey).Value; 
  
            var foundInNavStack = _navigationPage.Navigation.NavigationStack.Any(p => p.GetType() == pageType); 
            var foundInModalStack = _navigationPage.Navigation.ModalStack.Any(p => p.GetType() == pageType); 
  
            if (foundInNavStack && !foundInModalStack || !foundInNavStack && !foundInModalStack) 
            { 
                isUsedModal = false; 
            } 
            else if (foundInModalStack && !foundInNavStack) 
            { 
                isUsedModal = true; 
            } 
            else 
            { 
                throw new NotSupportedException("Pages should be used exclusively Modal or for Navigation"); 
            } 
        } 
        else 
        { 
            throw new ArgumentException($"No page with key: {pageKey}. Did you forget to call the Configure method?", nameof(pageKey)); 
        } 
    } 
    finally 
    { 
        _lock.Release(); 
    } 
  
    return (isRegistered, isUsedModal); 
} 

This method checks if a page is registered correctly with its key and if it is presented modal at the moment. It also makes sure that an exception is thrown if a page is used modal AND in a navigation flow, which I think is a good practice to avoid problems when using the service. Finally, let’s add a Task for navigation to another page:

public async Task NavigateToAsync(string pageKey, object parameter, bool animated = true) 
{ 
    await _lock.WaitAsync(); 
  
    try 
    { 
  
        if (_pagesByKey.ContainsKey(pageKey)) 
        { 
            var type = _pagesByKey[pageKey]; 
            ConstructorInfo constructor = null; 
            object[] parameters = null; 
  
            if (parameter == null) 
            { 
                constructor = type.GetTypeInfo() 
                    .DeclaredConstructors 
                    .FirstOrDefault(c => !c.GetParameters().Any()); 
  
                parameters = new object[] 
                { 
                }; 
            } 
            else 
            { 
                constructor = type.GetTypeInfo() 
                    .DeclaredConstructors 
                    .FirstOrDefault( 
                        c => 
                        { 
                            return c.GetParameters().Count() == 1 
                                   && c.GetParameters()[0].ParameterType == parameter.GetType(); 
                        }); 
  
                parameters = new[] { parameter }; 
            } 
  
            if (constructor == null) 
            { 
                throw new InvalidOperationException("No constructor found for page " + pageKey); 
            } 
  
            var page = constructor.Invoke(parameters) as Page; 
            if (_navigationPage != null) 
            { 
                Device.BeginInvokeOnMainThread(async () => 
                { 
                    await _navigationPage.Navigation.PushAsync(page, animated); 
                }); 
            } 
            else 
            { 
                throw new NullReferenceException("there is no navigation page present, please check your page architecture and make sure you have called the Initialize Method before."); 
            } 
        } 
        else 
        { 
            throw new ArgumentException( 
                $"No page with key: {pageKey}. Did you forget to call the Configure method?", 
                nameof(pageKey)); 
        } 
    } 
    finally 
    { 
        _lock.Release(); 
    } 
} 

[Update:] Until the latest Xamarin.Forms release, there was no need to use the Device.BeginInvokeOnMainThreadmethod for the navigation itself. I only checked it recently that the Navigation needs now the call to it to dispatch the navigation to the main UI thread. Otherwise, the navigation will end in a dead thread on Android and iOS (but strangely not for UWP). I updated the code above and the source on Github as well.

I try to avoid passing parameters to pages while navigating, that’s why I added another task without the parameter overload as well:

public async Task NavigateToAsync(string pageKey, bool animated = true)
{
    await NavigateToAsync(pageKey, null, animated);
}

Of course we will run into situations where we want to go back manually, for example in a cancel function. So we have a method for that as well:

public async Task GoBackAsync()
{
    await _navigationPage.Navigation.PopAsync(true);
}

That’s  a lot of functionality already, but until now, we are only able to navigate to other pages and back. We want to support modal pages as well, so we’ll have some more methods to implement. They are following the same syntax as their navigation counterpart, so no surprise here:

public async Task ShowModalPageAsync(string pageKey, object parameter, bool animated = true) 
{ 
    await _lock.WaitAsync(); 
  
    try 
    { 
  
        if (_pagesByKey.ContainsKey(pageKey)) 
        { 
            var type = _pagesByKey[pageKey]; 
            ConstructorInfo constructor = null; 
            object[] parameters = null; 
  
            if (parameter == null) 
            { 
                constructor = type.GetTypeInfo() 
                    .DeclaredConstructors 
                    .FirstOrDefault(c => !c.GetParameters().Any()); 
  
                parameters = new object[] 
                { 
                }; 
            } 
            else 
            { 
                constructor = type.GetTypeInfo() 
                    .DeclaredConstructors 
                    .FirstOrDefault( 
                        c => 
                        { 
                            return c.GetParameters().Count() == 1 
                                   && c.GetParameters()[0].ParameterType == parameter.GetType(); 
                        }); 
  
                parameters = new[] { parameter }; 
            } 
  
  
            var page = constructor.Invoke(parameters) as Page; 
  
            if (_navigationPage != null) 
            { 
                Device.BeginInvokeOnMainThread(async () => 
                { 
                    await _navigationPage.Navigation.PushModalAsync(page, animated); 
                }); 
            } 
            else 
            { 
                throw new NullReferenceException("there is no navigation page present, please check your page architecture and make sure you have called the Initialize Method before."); 
            } 
        } 
        else 
        { 
            throw new ArgumentException($"No page found with key: {pageKey}. Did you forget to call the Configure method?", nameof(pageKey)); 
        } 
    } 
    finally 
    { 
        _lock.Release(); 
    } 
} 

public async Task GoBackModalAsync()
{
    await _navigationPage.Navigation.PopModalAsync(true);
}

The last things that I wanted to note are properties that return the current counts of both the modal and the navigation stack of the navigation page as well as the GoHomeAsync() method to return to the root page. You will see these in the source code on Github.

IViewEventBrokerService implementation

Like I wrote already before, sometimes we need to know when a view is appearing/disappearing. In that case, the IViewEventBrokerService comes in. The implementation is pretty straight forward:

public class ViewEventBrokerService : IViewEventBrokerService
{
    public event EventHandler<ViewEventBrokerEventArgs> ViewAppearing;
    public event EventHandler<ViewEventBrokerEventArgs> ViewDisAppearing;


    public void RaiseViewAppearing(string pageKey, Type pageType, bool isModal)
    {
        ViewAppearing?.Invoke(this, new ViewEventBrokerEventArgs(pageKey, pageType, isModal));
    }

    public void RaiseViewDisAppearing(string pageKey, Type pageType, bool isModal)
    {
        ViewDisAppearing?.Invoke(this, new ViewEventBrokerEventArgs(pageKey, pageType, isModal));
    }
}

We have two events that can be raised with their corresponding Raise… methods. That’s all we have to do in here.

Implementing the Interfaces in Views (Pages)

Well, we have written a lot of code until here, now it is time to actually use it. As I want all my pages to be raise their appearing and disappearing automatically, I created a new base class, derived from Xamarin.Forms’ ContentPage:

public class XfNavContentPage : ContentPage
{
    private IViewEventBrokerService _viewEventBroker;
    private IXfNavigationService _navService;

    public XfNavContentPage()
    {
        _viewEventBroker = SimpleIoc.Default.GetInstance<IViewEventBrokerService>();
        _navService = SimpleIoc.Default.GetInstance<IXfNavigationService>();
    }

    public static BindableProperty RegisteredPageKeyProperty = BindableProperty.Create("RegisteredPageKey", typeof(string), typeof(XfNavContentPage), default(string), BindingMode.Default, propertyChanged: OnRegisteredPageKeyChanged);

    private static void OnRegisteredPageKeyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        //todo, but not needed atm.
    }

    public string RegisteredPageKey
    {
        get { return (string)GetValue(RegisteredPageKeyProperty); }
        set { SetValue(RegisteredPageKeyProperty, value); }
    }

    private(bool isRegistered, bool isModal) _stackState { get; private set; }

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

        if (!string.IsNullOrEmpty(RegisteredPageKey))
        {
            _stackState = _navService.StackContainsNavKey(RegisteredPageKey);

            if (_stackState.isRegistered)
            {
                _viewEventBroker?.RaiseViewAppearing(RegisteredPageKey, GetType(), _stackState.isModal);
            }
        }
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();

        if (!string.IsNullOrEmpty(RegisteredPageKey))
        {
            if (_stackState.isRegistered)
            {
                _viewEventBroker?.RaiseViewDisAppearing(RegisteredPageKey, GetType(), _stackState.isModal);
            }
        }
    }
}

Let me explain what this base class does. It provides a BindableProperty(which is the Xamarin.Forms version of a DependencyProperty) that takes the registered key to provide it as a parameter. You can bind the key from your ViewModel or set it in code behind/XAML directly, all these ways will work. The base page implements our interfaces of the IXfNavigationService and the IViewBrokerServiceinterfaces as members and loads them in its constructor. The _stackStatemember is just an easy way to provide the needed data to the event-raising methods of our IViewEventBrokerservice. If used correctly, every page that derives from this base class will throw the corresponding events, which can be used in other parts of our application then.

Implementing the Interfaces in ViewModels

Like I did for Views, I am also creating a new base class for ViewModels that implement our two interfaces. Here is how it looks like:

public class XfNavViewModelBase : ViewModelBase
{
    protected readonly IXfNavigationService _navService;

    protected readonly IViewEventBrokerService _viewEventBroker;


    public XfNavViewModelBase()
    {
        _navService = SimpleIoc.Default.GetInstance<IXfNavigationService>();
        _viewEventBroker = SimpleIoc.Default.GetInstance<IViewEventBrokerService>();
        _viewEventBroker.ViewAppearing += OnCorrespondingViewAppearing;
        _viewEventBroker.ViewDisAppearing += OnCorrespondingViewDisappearing;
    }


    protected virtual void OnCorrespondingViewAppearing(object sender, ViewEventBrokerEventArgs e)
    {
    }

    protected virtual void OnCorrespondingViewDisappearing(object sender, ViewEventBrokerEventArgs e)
    {
    }

    private string _correspondingViewKey;

    public string CorrespondingViewKey
    {
        get => _correspondingViewKey; set => Set(ref _correspondingViewKey, value);
    }

    public bool IsBoundToModalView()
    {
        if (!string.IsNullOrEmpty(CorrespondingViewKey))
        {
            var checkIfIsModal = _navService.StackContainsNavKey(CorrespondingViewKey);

            if (checkIfIsModal.isRegistered)
            {
                return checkIfIsModal.isModal;
            }
        }
        return false;
    }
}

The XfNavViewModelBase implements both the IXfNavigationService and the IViewEventBrokerService interfaces. It also registers for the events thrown by IViewEventBrokerService. Making them virtual allows us to override them in derived ViewModels, if we need to. It has a string property to set the name of the View we want to use it in as well as a method that checks automatically if we are on a modal page if it gets called.

Using the created navigation setup

Now our navigation setup is ready it is time to have a look into how to use it. First we are going to create two pages, one for being shown modal, and one for being navigated to.

created-pages-snip

Then we’ll need to register and configure our services and pages in the ViewModelLocator. Remember the static RegisterServices() method we created in the first post? This is were we will throw the interface registrations in:

private static void RegisterServices() 
{ 
    //this one gets the correct service implementation from platform implementation 
    var osService = DependencyService.Get<IOsVersionService>(); 
  
    // which can be used to register the service class with MVVMLight's Ioc 
    SimpleIoc.Default.Register<IOsVersionService>(() => osService); 
  
    SimpleIoc.Default.Register<IXfNavigationService>(GetPageInstances); 
    SimpleIoc.Default.Register<IViewEventBrokerService, ViewEventBrokerService>(); 
}

The method contains already our IOsVersionService from my last blog post. We are also using DI here for the IXfNavigationService, as we need to Configure our page keys as well. First, we are adding static strings as page keys to the ViewModelLocator:

public static string ModalPageKey => nameof(ModalPage);
public static string NavigatedPageKey => nameof(NavigatedPage);

I am always using the nameof()-operator for this, because it makes life a bit easier. The second step is the missing piece in the service registration, the GetPageInstances() method that returns an instance of the IXfNavigationService interface implentation and registers our two pages via the Configure() method.

public static XfNavigationService GetPageInstances()
{
    var nav = new XfNavigationService();

    nav.Configure(ModalPageKey, typeof(ModalPage));
    nav.Configure(NavigatedPageKey, typeof(NavigatedPage));

    return nav;
}

This all will never work if we are not using a NavigationPage. So we are going to load MainPage.xaml as a NavigationPagein App.xaml.cs and pass the navigation page to our IXfNavigationService:

public App()
{
    InitializeComponent();

    var rootNavigation = new NavigationPage(new View.MainPage());
    MainPage = rootNavigation;
    SimpleIoc.Default.GetInstance<IXfNavigationService>().Initialize(rootNavigation);
}

Modifying pages

Until now, the pages we created are standard ContentPages, so we need to make them derive from our earlier created base class. First we are adding the namespace of our base class to it:

xmlns:baseCtrl="clr-namespace:XfMvvmLight.BaseControls;assembly=XfMvvmLight"

After that, we are able to use the base class to change ContentPage to baseCtrl:XfNavContentPage. Now open the code behind file for the page and change it to derive from XfNavContentPage as well:

public partial class ModalPage : XfNavContentPage

The sample project has a modal page and a navigated page to demonstrate both ways.

Setting up ViewModels

Normally, we also have a ViewModel for every additional page. That´s why I created a ModalPageViewModel as well as a NavigatedPageViewModel for the sample. They derive from the XfNavViewModelBase, so wie can easyily hook up the view events (if needed):

public class ModalPageViewModel : XfNavViewModelBase
{ 
    public ModalPageViewModel()
    {
        CorrespondingViewKey = ViewModelLocator.ModalPageKey;
    }
 
    protected override void OnCorrespondingViewAppearing(object sender, ViewEventBrokerEventArgs e)
    {
        base.OnCorrespondingViewAppearing(sender, e);
    }
 
    protected override void OnCorrespondingViewDisappearing(object sender, ViewEventBrokerEventArgs e)
    {
        base.OnCorrespondingViewDisappearing(sender, e);
    }
 
 
    private RelayCommand _goBackCommand;
 
    public RelayCommand GoBackCommand => _goBackCommand ?? (_goBackCommand = new RelayCommand(async () =>
    {
        await _navService.GoBackModalAsync();
    })); 
}

Remember the page keys we added earlier to the ViewModelLocator? We are just setting it as CorrespondingViewKey in the constructor here, but you could choose to it the way you prefer as well and can easily be bound to the view now:

<baseCtrl:XfNavContentPage 
   xmlns="http://xamarin.com/schemas/2014/forms"
   xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
   xmlns:baseCtrl="clr-namespace:XfMvvmLight.BaseControls;assembly=XfMvvmLight"
   x:Class="XfMvvmLight.View.ModalPage" 
   RegisteredPageKey="{Binding CorrespondingViewKey}">
<!--code omitted for readability-->
</baseCtrl:XfNavContentPage>

As the sample pages have a button with back function, we have also a GoBackCommand bound to it. Now that’s already all we need to, and if we set a breakpoint in the event handling overrides, it will be hit as soon as the view is about to appear. See the sample working here in action on Android:

navigation-service-xf-mvvmlight

No we have a fully working and MVVM compliant navigation solution in Xamarin.Forms using the MVVMLight Toolkit.  I know there are several other toolkits and helpers floating around, but I like it to have less dependencies.  Another advantage of going this route: I am mostly familiar with the setup as I am already used to it from my Windows and Windows Phone applications. Creating this setup for navigation does not take a whole lot of time (the initial setup took me around 3 hours including conception). One can also pack the interfaces and the implementations in a (portable) class library to have all this work only once.

If you have feedback for me, feel free to leave a comment below. Like always, I hope this article is helpful for some of you. The updated sample application can be found here on my Github account.

Happy coding, everyone!

 

Posted by msicc in Android, Dev Stories, iOS, UWP, Xamarin, 4 comments

Helper class to easily display local toast notifications (Windows Universal app)

Often , we need to display a confirmation that some action in our app has been finished (like some data has been updated etc.). There are several ways of doing this, like displaying a MessageBox or MessageDialog. This however breaks the user interaction, and a lot of users will start complaining on that if your app keeps doing so. There needs to be a better way.

With the Coding4fun Toolkit floating around, you can mimic a toast notification – sadly only on Windows Phone (at least for the moment, but Dave told me he will work on implementing it for Windows, too). Also, Toastinet library is floating around, which is also able to mimic the toast notification behavior (although for Windows Universal app, the implementation is not that intuitive as for Windows  Phone). Both are fantastic libraries that I used in the past, but I wanted a solution that is implemented easily and works with my Universal app. So I did some searching in the Web and the MSDN docs, and found out that is pretty easy to use the system toast notifications on both platforms locally.

There are 8 possible ways to format toast notifications (as you can see here in the toast template catalog). This gives us pretty much options on how a notification can be styled. However, most options just work on Windows 8.1, while Windows Phone 8.1 apps will only show the notification in the way “app logo”  “bold text”  “normal text”. However, the notification system takes care of that, so you can specify some other type on Windows 8.1, while knowing that it gets converted on Windows Phone automatically. This allows us to write a helper class that implements all possible options without any headache.

the code parts for the notification

Let’s have a look at the code parts for the notification. First, you need to add two Namespaces to the class:

using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;

After that, we can start writing our code. Toast notifications are formatted using Xml. Because of this, we need to get a reference to the underlying Xml template for the system toast notification:

ToastTemplateType xmlForToast= ToastTemplateType.ToastImageAndText01; 
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(xmlForToast);

System toast notifications can hold Text (and an Image on Windows 8.1). So we need to declare the elements of the toast notification. We are using the Xml methods of the DOM namespace to get the text elements of the chosen template first:

XmlNodeList toastTextElements = xmlForToast.GetElementsByTagName("text");
toastTextElements[0].AppendChild(xmlForToast.CreateTextNode("text1"));
//additional texts, depending on the template:
//toastTextElements[1].AppendChild(xmlForToast.CreateTextNode("text2"));
//toastTextElements[2].AppendChild(xmlForToast.CreateTextNode("text3"));

This is how the image element is implemented:

XmlNodeList toastImageElement = xmlForToast.GetElementsByTagName("image");
//setting the image source uri:
if (toastImageElement != null) ((XmlElement) toastImageElement[0]).SetAttribute("src", imageSourceUri);
//setting optional alternative text for the image
if (toastImageElement != null)  ((XmlElement) toastImageElement[0]).SetAttribute("alt", imageSourceAlternativeText);

You can attach local or remote images to the toast notification, but remember this works only on Windows, not on Windows Phone.

The next part we are able to set is the duration. The duration options are long (25 seconds) and short (7 seconds). The default is short, which should be ok for most scenarios. Microsoft recommends to use long only when a personal interaction of the user is needed (like in a chat). This is how we do it:

IXmlNode toastRoot = xmlForToast.SelectSingleNode("/toast");
((XmlElement) toastRoot).SetAttribute("duration", "short");

What we are doing here is to get the root element of the template’s Xml and add a new element for the duration. Now that we finally have set all options, we are able to create our toast notification and display it to the user:

ToastNotification notification = new ToastNotification(xmlForToast);
ToastNotificationManager.CreateToastNotifier().Show(notification);

the helper class

That’s all we need to do for our local notification. You might see that always rewriting the same code just makes a lot of work. Because the code for the toast notification can be called nearly everywhere in an app (it does not matter if you are calling it from a ViewModel or code behind), I wrote this helper class that makes it even more easy to use the system toast notification locally:

    public class LocalToastHelper
    {
        public void ShowLocalToast(ToastTemplateType templateType, string toastText01, string toastText02 = null, string toastText03 = null, string imageSourceUri = null, string imageSourceAlternativeText = null, ToastDuration duration = ToastDuration.Short)
        {
            XmlDocument xmlForToast = ToastNotificationManager.GetTemplateContent(templateType);
            XmlNodeList toastTextElements = xmlForToast.GetElementsByTagName("text");

            switch (templateType)
            {
                case ToastTemplateType.ToastText01:
                case ToastTemplateType.ToastImageAndText01:
                    toastTextElements[0].AppendChild(xmlForToast.CreateTextNode(toastText01));
                    break;
                case ToastTemplateType.ToastText02:
                case ToastTemplateType.ToastImageAndText02:
                    toastTextElements[0].AppendChild(xmlForToast.CreateTextNode(toastText01));
                    if (toastText02 != null)
                    {
                        toastTextElements[1].AppendChild(xmlForToast.CreateTextNode(toastText02));
                    }
                    else
                    {
                        throw new ArgumentNullException("toastText02 must not be null when using this template type");
                        
                    }
                    ;
                    break;
                case ToastTemplateType.ToastText03:
                case ToastTemplateType.ToastImageAndText03:
                    toastTextElements[0].AppendChild(xmlForToast.CreateTextNode(toastText01));
                    if (toastText02 != null)
                    {
                        toastTextElements[1].AppendChild(xmlForToast.CreateTextNode(toastText02));
                    }
                    else
                    {
                        throw new ArgumentNullException("toastText02 must not be null when using this template type");
                    }
                    ;
                    break;
                case ToastTemplateType.ToastText04:
                case ToastTemplateType.ToastImageAndText04:
                    toastTextElements[0].AppendChild(xmlForToast.CreateTextNode(toastText01));
                    if (toastText02 != null)
                    {
                        toastTextElements[1].AppendChild(xmlForToast.CreateTextNode(toastText02));
                    }
                    else
                    {
                        throw new ArgumentNullException("toastText02 must not be null when using this template type");
                    }
                    ;
                    if (toastText03 != null)
                    {
                        toastTextElements[2].AppendChild(xmlForToast.CreateTextNode(toastText03));
                    }
                    else
                    {
                        throw new ArgumentNullException("toastText03 must not be null when using this template type");
                    }
                    ;
                    break;
            }

            switch (templateType)
            {
                case ToastTemplateType.ToastImageAndText01:
                case ToastTemplateType.ToastImageAndText02:
                case ToastTemplateType.ToastImageAndText03:
                case ToastTemplateType.ToastImageAndText04:
                    if (!string.IsNullOrEmpty(imageSourceUri))
                    {
                        XmlNodeList toastImageElement = xmlForToast.GetElementsByTagName("image");
                        if (toastImageElement != null)
                            ((XmlElement) toastImageElement[0]).SetAttribute("src", imageSourceUri);
                    }
                    else
                    {
                        throw new ArgumentNullException(
                            "imageSourceUri must not be null when using this template type");
                    }
                    if (!string.IsNullOrEmpty(imageSourceUri) && !string.IsNullOrEmpty(imageSourceAlternativeText))
                    {
                        XmlNodeList toastImageElement = xmlForToast.GetElementsByTagName("image");
                        if (toastImageElement != null)
                            ((XmlElement) toastImageElement[0]).SetAttribute("alt", imageSourceAlternativeText);
                    }
                    break;
                default:
                    break;
            }

            IXmlNode toastRoot = xmlForToast.SelectSingleNode("/toast");
            ((XmlElement) toastRoot).SetAttribute("duration", duration.ToString().ToLowerInvariant());

            ToastNotification notification = new ToastNotification(xmlForToast);
            ToastNotificationManager.CreateToastNotifier().Show(notification);
        }

        public enum ToastDuration
        {
            Short,
            Long
        }
    }

As you can see, you just need to provide the wanted parameters to the ShowLocalToast method, which will do the rest of the work for you.

One word to the second switch statement I am using. The image element needs to be set only when we are using the ToastImageAndTextXX templates. There are three ways to implement the integration: using an if with 4  “or” options, the switch statement I am using or a string comparison with String.Contains. The switch statement is the cleanest option for me, so I decided to go this way. Feel free to use any of the other ways in your implementation.

In my implementation, I added also some possible ArgumentNullExceptions to make it easy to find any usage errors.

For your convenience, I attached the source file. Just swap out the namespace with yours. Download

The usage of the class is pretty simple:

var _toastHelper = new LocalToastHelper();
_toastHelper.ShowLocalToast(ToastTemplateType.ToastText02, "This is text 1", "This is text 2");

audio options

The system toasts have another option that can be set: the toast audio. This way, you can customize the appearance of the toast a bit more. I did not implement it yet, because there are some more options and things to remind, and I haven’t checked them out all together. Once I did, I will add a second post to this one with the new information.

As always, I hope this is helpful for some of you.

Happy coding!

Posted by msicc in Archive, 3 comments