You only need a OneTime binding

When creating a binding in Xamarin.Forms, the default is that you create an OneWay binding (except for input elements that have TwoWay bindings), this means that it updates the view from the ViewModel whenever the value in the ViewModel changes. But often you don't need a OneWay binding, you only need a OneTime binding, but you use OneWay just because it is the default. A OneTime binding only reads the value once, if the property changes after that, the UI is not updated. That means that the UI doesn't need to listen to changes in the ViewModel, and that can affect performance in a good way.

If you are familiar with UWP development and compiled bindings with x:Bind you may know that there is OneTime default and the reason for that is that it gives you a more performant app. So why not use it with Xamarin.Formas as well.

To use OneTime binding one tip from the Jason Smith, one of the founders of Xamarin.Forms, according to this tweet:

In this post, I will show some scenarios where you can use OneTime binding. But I recommend you to use it whenever possible.

When using CollectionView
You maybe will change the data source for a CollectionView (read below for a static data example), so you still need a TwoWay binding there, and often you will set the value of the property after that you have fetched data from a data source. But inside your DataTemplate, you can often use OneTime bindings, and if you have some elements that need to listen for changes, you can use OneTime bindings just for those elements.

When using Commands
When using commands in ViewModels, you often write to them like below, and you never change the value of the property.

In this case, there is no need to use the default OneWay binding because you know that the value of the property will never change.

When having static data
Sometimes you need to bind an element to a collection of static data, for example when you are using a CarouselView or a CollectionView where you specify the data in a ViewModel. The reason that you want to do that is that you can't add items to those elements directly (you can specify ItemSource in XAML if you want).

The property in the ViewModel:

A CarouselView that binds to the property:

Building a styleable “Content Button” using PancakeView

Sometimes you need a more flexible button than the one that is shipped with Xamarin.Forms. That was the case for me with the app I am working with right now. I wanted to be able to have more flexible content and I also wanted to style it more than I was able to when I used the default Button. So I decided to create my own Button control. I decided to use PancakeView by Steven Thewissen as the base class for my control. PancakeView is a great library if you want to have views and be more flexible when we are talking about corner radius, shadows, and gradients.

To make a button controls of PancakeView you need to add code to handle when a user taps the control. I added bindable properties for Command and CommandParameter as Xamarin.Forms.Button has, so that the user can bind a command to it.

In the constructor of the control, I am adding some default styling, that could be overridden by setting the properties in the view that you are using the control in. I also add a TapGestureRecognizer and an event handler for it in the constructor.

I wanted some feedback for the user when the button is tapped. So I added code to scale the control down and up again when a user tapped the control, to be able to easily set how much it should scale I added a property with the name AnimationScale.

In the event handler for the TapGestureRecognizer I do the animations and execute the Command if it is set. I execute the command before I animate the scale back to its original value. This because I think the delay is too long to wait for both animations. But it is nice to animate the value back, in case you not navigating when the users are pressing the button. But here you can write whatever feedback that fit your needs.

Then you can use it like this in yout views:

TinyMvvm 2.4.1

Today, I released a new version of TinyMvvm, 2.4.1.

The release contains this:

  • Added ShellViewBase to optimize how ViewModels are created. Before the ViewModel was created during OnAppearing when using Shell navigation. It also ran Initialize during OnAppearing. But there are cases where you want it to run earlier, so I added a new class that you can use to achieve that. You can also pass trueto the constructor of ViewBase to achieve the same thing.

    In XAML and ShellViewBase:

    <mvvm:ShellViewBase
    xmlns:mvvm="clr-namespace:TinyMvvm.Forms;assembly=TinyMvvm.Forms"
    xmlns:vm="clr-namespace:SampleApp.ViewModels"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    x:Class="SampleApp.Views.MainView" 
    x:TypeArguments="vm:MainViewModel">
    
    </mvvm:ViewBase>

    With a parameter to the constructor of ViewBase:

    public partial class MainView
    {
    public MainView() : base(true)
    }
    <mvvm:ViewBase
    xmlns:mvvm="clr-namespace:TinyMvvm.Forms;assembly=TinyMvvm.Forms"
    xmlns:vm="clr-namespace:SampleApp.ViewModels"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    x:Class="SampleApp.Views.MainView" 
    x:TypeArguments="vm:MainViewModel">
    
    </mvvm:ViewBase>
  • Removed the need of TinyMvvm.Initialize(); It now marked as obsolete, so it will not break your code. The big win by removing the need for it is that it is easier to get started with TinyMvvm.

  • Refactored so that ViewModelBase implements the new IViewModelBase interface. The ViewBase is refactored so IViewModelBase is used instead of ViewModelBase. This is a request from a developer that uses TinyMvvm and it makes the library more flexible.

Read more

If you want to read more about TinyMvvm, I recommend the Get Started Tutorial, and also these blog posts:

Contribute

The full source code and documentation can be found on GitHub. Feedback and contributions are very welcome.

Building a Rating Control using Xamarin.Forms Shapes

In the release of Xamarin.Forms 4.7, Microsoft introduced a Shapes, an API to draw shapes without having to use an external library like SkiaSharp.

In this blog post I will show how you can use Xamarin.Forms and Shapes to create a rating control without using an external library. The complete code for this control can be found here.



Make sure that you are using Xamarin.Forms 4.7 or newer.
Add the experimental flag in your App.xaml.cs file.

 public App()
 {
    InitializeComponent();
    Device.SetFlags(new string[] { "Shapes_Experimental" });
 }

The rating control will be based on StackLayout, so you will create a new class that has StackLayout as its base class.

 public class RatingControl : StackLayout
 {
        public RatingControl()
        {
            Orientation = StackOrientation.Horizontal;
        }
 }

To draw the starts for the control, you need a couple of points, I used Sketch to draw a star and saved it as an svg-file. After that I opened the svg-file in Visual Studio Code to extract the points. In the RatingControl I store the points as a lists:

 private readonly List originalFullStarPoints = new List()
  {
            new Point(96,1.12977573),
            new Point(66.9427701,60.0061542),
            new Point(1.96882894,69.4474205),
            new Point(48.9844145,115.27629),
            new Point(37.8855403,179.987692),
            new Point(96,149.435112),
            new Point(154.11446,179.987692),
            new Point(143.015586,115.27629),
            new Point(190.031171,69.4474205),
            new Point(125.05723,60.0061542),
            new Point(96,1.12977573),
};

 private readonly List originalHalfStarPoints = new List()
 {
            new Point(96,1.12977573),
            new Point(66.9427701,60.0061542),
            new Point(1.96882894,69.4474205),
            new Point(48.9844145,115.27629),
            new Point(37.8855403,179.987692),
            new Point(96,149.435112),
            new Point(96,1.12977573)
};

The control will have a couple of properties that can be used to customize the look of it. Everytime one of these properties changed, it should trigger a redraw of the control:

 private void Draw()
 {
    //We will insert code here later
 }

 private void Set(ref T field, T newValue)
 {
            if (!EqualityComparer.Default.Equals(field, newValue))
            {
                field = newValue;
                Draw();
            }
 }

 public double Rating
 {
            get => (double)GetValue(RatingProperty);
            set => SetValue(RatingProperty, value);
}

 private int max = 5;
 public int Max
 {
            get => max;
            set => Set(ref max, value);
 }

 private Color fillColor = Color.Yellow;
 public Color FillColor
 {
            get => fillColor;
            set => Set(ref fillColor, value);
 }

 private Color strokeColor = Color.Black;
 public Color StrokeColor
 {
            get => strokeColor;
            set => Set(ref strokeColor, value);
 }

 private double strokeThickness = 0;
 public double StrokeThickness
 {
            get => strokeThickness;
            set => Set(ref strokeThickness, value);
 }

 private double size = 50;
 public double Size
 {
            get => size;
            set => Set(ref size, value);
 }

The Rating property should be a BindableProperty so you are able to bind to it:

public static BindableProperty RatingProperty = BindableProperty.Create(nameof(Rating), typeof(double), typeof(RatingControl), 0.0, BindingMode.OneWay, propertyChanged: (bindable, newValue, oldValue) =>
{
             var control = (RatingControl)bindable;

             if (newValue != oldValue)
             {
                 control.Draw();
             }
});

 public double Rating
 {
            get => (double)GetValue(RatingProperty);
            set => SetValue(RatingProperty, value); 
 }

Above you added the original points from the svg file, but because you are able to set size of the stars you need to recalculate them before you draw them and store them in a PointsCollection that you will use in the draw method. Here is how to calculate the new size:

 private readonly PointCollection fullStarPoints = new PointCollection();
 private readonly PointCollection halfStarPoints = new PointCollection();

 private void CalculatePoints(PointCollection calculated, List original)
  {
            calculated.Clear();

            foreach (var point in original)
            {
                var x = point.X * ratio;
                var y = point.Y * ratio;

                var p = new Point(x, y);

                calculated.Add(p);
            }
 }

To draw all different types of stars in this control you need three methods to draw stas, one for a full star, one for a half star, and one for an empty star. Both the full star and the empty star will use the same points and the half start will use points from both. You will use the Polygon object to draw the stars:

private Polygon GetFullStar()
{
            var fullStar = new Polygon()
            {
                Points = fullStarPoints,
                Fill = FillColor,
                StrokeThickness = StrokeThickness,
                Stroke = StrokeColor
            };

            return fullStar;
}

private Polygon GetEmptyStar()
{
            var emptyStar = new Polygon()
            {
                Points = fullStarPoints,
                StrokeThickness = StrokeThickness,
                Stroke = StrokeColor
            };

            return emptyStar;
 }

The half star will be a drawn with two Polygons, therefore you will add them to a Grid so you can have them in layers:

private Grid GetHalfStar()
 {
            var grid = new Grid();

            var halfStar = new Polygon()
            {
                Points = halfStarPoints,
                Fill = fillColor,
                Stroke = Color.Transparent,
                StrokeThickness = 0,
            };

            var emptyStar = new Polygon()
            {
                Points = fullStarPoints,
                StrokeThickness = StrokeThickness,
                Stroke = StrokeColor
            };

            grid.Children.Add(halfStar);
            grid.Children.Add(emptyStar);

            return grid;
 }

The last thing to do is to implement the Draw method, in it you will first calculate the ratio between the new size and the size that I original star was in (in this case, 200). After that you need to use the method you created above to calculate the points that will be used to draw the Polygons. And the last things you do is to call the draw methods to draw the stars:

private double ratio;

private void Draw()
{
            Children.Clear();

            var newRatio = Size / 200;

            if (newRatio != ratio)
            {
                ratio = newRatio;

                CalculatePoints(fullStarPoints, originalFullStarPoints);
                CalculatePoints(halfStarPoints, originalHalfStarPoints);
            }

            for (var i = 1; i <= Max; i++)
            {
                if (Rating >= i)
                {
                    Children.Add(GetFullStar());
                }
                else if(Rating > i - 1)
                {
                    Children.Add(GetHalfStar());
                }
                else
                {
                    Children.Add(GetEmptyStar());
                }
            }
}



The complete code for the control can be found here, https://gist.github.com/dhindrik/4aaa48d5f332222b3ca674cfd1447c6c.

Xamarin.Forms Projects – Second Edition

The second edition of Xamarin.Forms Projects is now published. You can buy it from Packt or Amazon. It will also be available in other book stores.

More Information

An example-driven guide to help you build native cross-platform mobile apps using Xamarin, .NET Core 3, and Visual Studio 2019.

Set up Xamarin.Forms for building native apps with code-sharing capabilities

Understand the core aspects of developing a mobile app such as its layout, UX, and rendering

Use custom renderers to gain platform-specific access

Discover how to create custom layouts for your apps with Xamarin.Forms Shell

Use Azure SignalR for implementing serverless services in your Xamarin apps

Create an augmented reality (AR) game for Android and iOS using ARCore and ARKit, respectively

Build and train machine learning models using CoreML, TensorFlow, and Azure Cognitive Services

Xamarin.Forms is a lightweight cross-platform development toolkit for building apps with a rich user interface. Improved and updated to cover the latest features of Xamarin.Forms, this second edition covers CollectionView and Shell, along with interesting concepts such as augmented reality and machine learning.

Starting with an introduction to Xamarin and how it works, this book shares tips for choosing the type of development environment you should strive for when planning cross-platform mobile apps. You’ll build your first Xamarin.Forms app and learn how to use Shell to implement the app architecture. The book gradually increases the level of complexity of the projects, guiding you through creating apps ranging from a location tracker and weather map to an AR game and face recognition. As you advance, the book will take you through modern mobile development frameworks such as SQLite, .NET Core Mono, ARKit, and ARCore. You’ll be able to customize your apps for both Android and iOS platforms to achieve native-like performance and speed. The book is filled with engaging examples, so you can grasp essential concepts by writing code instead of reading through endless theory.

By the end of this book, you’ll be ready to develop your own native apps with Xamarin.Forms and its associated technologies such as .NET Core, Visual Studio 2019, and C#.

Develop mobile apps, AR games, and chatbots of varying complexity with the help of real-world examples

Explore the important features of Xamarin.Forms 4 such as Shell, CollectionView, and CarouselView

Get to grips with advanced concepts such as AR and VR and machine learning for mobile development

TinyIoC for TinyMvvm

There was an issue created for TinyMvvm regarding to use TinyIoC as the IoC-container. I had created an implementation of IResolver for Autofac. But now I decided to create one for TinyIoC too.

The implementation for it can be installed via the NuGet package, TinyMvvm.TinyIoC, https://www.nuget.org/packages/TinyMvvm.TinyIoC.

The code for setting up the container and the Resolver for the Sample project will look like this if you want to use TinyIoC:

TinyIoCContainer.Current.Register<INavigationHelper>(navigationHelper);

foreach (var type in appAssembly.ExportedTypes.Where(x => x.IsSubclassOf(typeof(ViewModelBase))))
{
    TinyIoCContainer.Current.Register(type);
}

foreach (var type in appAssembly.ExportedTypes.Where(x => x.IsSubclassOf(typeof(Page))))
{
    TinyIoCContainer.Current.Register(type);
}

Resolver.SetResolver(new TinyIoCResolver());

Snippets for TinyMvvm

This blog post is a part of the Xamarin Month, a great initiative by Luis Matos. The theme for this Xamarin Month is code snippets. Here is my contribution to the Xamarin Month.

TinyMvvm is a library that I created to make me more productive when I developing apps with Xamarin.Forms.

Snippets

All snippets are available in the repository for TinyMvvm.

TinyCommand (tmcmd)

TinyMvvm has its own implementation of ICommand. You can use Xamarin.Forms Command, but if you use the TinyCommand you don't have to add references to Xamarin.Forms in your ViewModels.

Shortcut: tmcmd

private ICommand? command;
public ICommand Command => command ??= new TinyCommand(async() => {

}

TinyCommand<T> (tmcmdt)

TinyCommand<T> is a generic TinyCommand that is able to handle a parameter.

Shortcut: tmcmdt

private ICommand? command;
public ICommand Command => command ??= new TinyCommand<object>(async(parameter) => {

}

TinyMvvm property (tmprop)

I often use PropertyChanged.Fody so I don't need this snippet, but if you don't use it, this snippet can be very useful.

Shortcut: tmprop

private string propertyName;
public string PropertyName
{
    get => propertyName;
    set => Set(ref propertyName, value);
}

Override Initialize (tminit)

In the ViewModel you can override Initialize if you have ViewModelBase as your base class. It will run when the ViewModel is created.

Shortcut: tminit

public async override Task Initialize()
{
    await base.Initialize();
}

Override OnAppearing (tmapp)

In the ViewModel you can override OnAppearing if you have ViewModelBase as your base class. It will be triggered by when the View is appearing.

Shortcut: tmapp

public async override Task OnAppearing()
{
    await base.OnAppearing();
}

Override OnDisappearing (tmdis)

In the ViewModel you can override OnDisappearing if you have ViewModelBase as your base class. It will be triggered by when the View is disappearing.

Shortcut: tmdis

public async override Task OnDisappearing()
{
    await base.OnDisappearing();
}

IsBusy (IsBusy)

In the ViewModelBase there is a property with the name IsBusy. I use to set it to true when I loading data so I can bind an ActivityIndicator to it. There is also a property with the name IsNotBusy that always will have the opposite value of IsBusy. This snippet can be used to surround your code with IsBusy.

Shortcut: IsBusy

IsBusy = true;
//Your code
IsBusu = false;

Import snippets

For Visual Studio on Windows, snippets are imported with the Code Snippets Manager that you will find in the Tools menu. Read more here, https://docs.microsoft.com/en-us/visualstudio/ide/walkthrough-creating-a-code-snippet?view=vs-2019#import-a-code-snippet

For Visual Studio for Mac, add them in the ~/Library/VisualStudio/8.0/Snippets folder.

Read more

If you want to read more about TinyMvvm, I recommend the Get Started Tutorial, and also these blog posts:

Contribute

The full source code and documentation for TinyMvvm can be found on GitHub. Feedback and contributions are very welcome.

Get started with TinyMvvm

This is a tutorial that will guide you through how to get started building an app using Xamarin.Forms and TinyMvvm.

  1. Create a new project based on the template for a blank Xamarin.Forms app. In this example, the name of the project will be SampleApp. The template will generate one project for shared code and one project per target platform.

  2. Install the following NuGet packages into all projects:

    • TinyMvvm.Forms
    • TinyMvvm.Autofac
  3. Create a new ContentPage XAML in the SampleApp project and give it the name AppShell. And add the following content to it.

    <Shell xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:views="clr-namespace:SampleApp.Views" x:Class="SampleApp.AppShell">
        <TabBar>
            <ShellContent Title="Home" Route="home" >
                <views:MainView />
             </ShellContent>
            <ShellContent Title="About" Route="about">
                <views:AboutView />
             </ShellContent>
        </TabBar>
    </Shell>
  4. Remove the base class in the AppShell.xaml.cs so it will look like this:

    public partial class AppShell
    {
        public AppShell()
        {
            InitializeComponent();
        }
    }
  5. Navigate to App.xaml.cs and set MainPage to AppShell. You can also delete the MainPage.xaml- and MainPage.xaml.cs files.

    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
    
            MainPage = new AppShell();
        }
    }
  6. On the rows before you set MainPage to AppShell write the initialization code for TinyMvvm:

    • Use ShellNavigationHelper and register all views in the current Assembly. This register all views so we can use the class name as the key if we use the classic (non-Shell) navigation or we can use ViewModelNavigation that is powered by the Shell navigation.
    • The sample is using Autofac as it's IoC container, but you can use whatever container you want. The only thing you need to do to use another is to create an implementation of IResolver that uses it. Register all classes that is a subType of Page (Xamarin.Forms) and ViewModelBase (TinyMvvm).
    • Register the container to the Resolver, the Resolver is used internally by TinyMvvm, but you can also use it in your code.
    • The last thing to do is to call the *Initialize method for TinyMvvm.

      public App()
      {
          InitializeComponent();
      
          var navigationHelper = new ShellNavigationHelper();
      
          var currentAssembly = Assembly.GetExecutingAssembly();
          navigationHelper.RegisterViewsInAssembly(currentAssembly);
      
          var containerBuilder = new ContainerBuilder();
      
          containerBuilder.RegisterInstance<INavigationHelper>(navigationHelper);
      
          var appAssembly = typeof(App).GetTypeInfo().Assembly;
          containerBuilder.RegisterAssemblyTypes(appAssembly)
                 .Where(x => x.IsSubclassOf(typeof(Page)));
      
          containerBuilder.RegisterAssemblyTypes(appAssembly)
                 .Where(x => x.IsSubclassOf(typeof(ViewModelBase)));
      
          var container = containerBuilder.Build();
      
          Resolver.SetResolver(new AutofacResolver(container));
      
          TinyMvvm.Forms.TinyMvvm.Initialize();
      
          MainPage = new AppShell();
      }
  7. Create a new folder called ViewModels in the SampleApp project.

  8. Create two classes in the ViewModels folder, MainViewModel and AboutViewModel.

  9. Add ViewModelBase as the base class for MainViewModel and AboutViewModel:

    public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
        }
    }
    public class AboutViewModel : ViewModelBase
    {
        public AboutViewModel()
        {
        }
    }
  10. Edit MainView.xaml and AboutView.xaml to have ViewBase as it's base class. To set BindingContext to the the ViewModel, use the x:TypeArguments for the View.

    <mvvm:ViewBase
    xmlns:mvvm="clr-namespace:TinyMvvm.Forms;assembly=TinyMvvm.Forms"
    xmlns:vm="clr-namespace:SampleApp.ViewModels"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    x:Class="SampleApp.Views.MainView" 
    x:TypeArguments="vm:MainViewModel">
    
    </mvvm:ViewBase>
    <mvvm:ViewBase
    xmlns:mvvm="clr-namespace:TinyMvvm.Forms;assembly=TinyMvvm.Forms"
    xmlns:vm="clr-namespace:SampleApp.ViewModels"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    x:Class="SampleApp.Views.AboutView" 
    x:TypeArguments="vm:AboutViewModel">
    
    </mvvm:ViewBase>
  11. Remove the base class from MainView.xaml.cs and AboutView.xaml.cs. Because is it a partial class you don't have to specify it both in the XAML-file and in the code-behind file.

  12. Create a new View, DetailsView, and a new ViewModel, DetailsViewModel as in the same style as above.

  13. In MainViewModel, add the following below. The override of Initialize is running when BindingContext for the view has been set. You can also override OnAppearing to run code when the view will appear and OnDisappearing to run code when the view will disappear. IsBusy can be used to bind an ActivityIndicator to. For Commands, use TinyCommand instead of Xamarin.Forms.Command to keep the ViewModel clean from Xamarin.Forms references. To navigate, use the Navigation property from ViewModelBase. Here you can use the name of a ViewModel as a part of a URL to navigate to a route. With TinyMvvm you can also specify a parameter to pass to the target ViewModel.

    public class MainViewModel : ViewModelBase
    {
        private ObservableCollection<string> names;
        public ObservableCollection<string> Names
        {
            get => names;
            set => Set(ref names, value);
        }
    
        public async override Task Initialize()
        {
            IsBusy = true;            
            await base.Initialize();
    
            Names = new ObservableCollection<string>(new List<string>()
            {
                "Daniel",
                "Ella",
                "Willner"
            });
    
            IsBusy = false;
        }
    
        public override Task OnAppearing()
        {
            return base.OnAppearing();
        }
    
        public override Task OnDisappearing()
        {
            return base.OnDisappearing();
        }
    
        private ICommand details;
        private ICommand Details => details ??= new TinyCommand<string>(async(name) =>
        {
            await Navigation.NavigateToAsync($"{nameof(DetailsViewModel)}?name={name}", DateTimeOffset.Now);
        });
    }
  14. To MainView.xaml add the following code to show the bind a CollectionView to the data in the ViewModel:

     <Grid>
        <ActivityIndicator HorizontalOptions="Center" VerticalOptions="Center" IsRunning="False" IsVisible="{Binding IsBusy}" />
        <CollectionView x:Name="Names" ItemsSource="{Binding Names}" IsVisible="{Binding IsNotBusy}">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <ContentView Padding=" 10">
                        <ContentView.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding Source={x:Reference Names}, Path=BindingContext.Details}" CommandParameter="{Binding}" />
                        </ContentView.GestureRecognizers>
                        <Label Text="{Binding }" />
                    </ContentView>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </Grid>
  15. Go to DetailsViewModel.cs and add the code below to receive the parameters sent to it. QueryParameters will be a Dictionary<string, string> and contains the query parameters specified in the navigation URL. If you also specified a parameter you can access it via the NavigationParameter property.

    
    public class DetailsViewModel : ViewModelBase
    {
        public async override Task Initialize()
        {
            await base.Initialize();
    
            Name = QueryParameters["name"];
            var dateParameter = (DateTimeOffset)NavigationParameter;
    
            Date = dateParameter.ToString();
        }
    
        private string name;
        public string Name
        {
            get => name;
            set => Set(ref name, value);
        }
    
        private string date;
        public string Date
        {
            get => date;
            set => Set(ref date, value);
        }
    }
  16. In the DetailsView.cs add the following code to show the data passed from MainViewModel:

    <mvvm:ViewBase
        xmlns:mvvm="clr-namespace:TinyMvvm.Forms;assembly=TinyMvvm.Forms"
        xmlns:vm="clr-namespace:SampleApp.ViewModels"
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        x:Class="SampleApp.Views.DetailsView" x:TypeArguments="vm:DetailsViewModel">
        <StackLayout Padding="10">
            <Label Text="{Binding Name}" />
            <Label Text="{Binding Date}" />
        </StackLayout>
    </mvvm:ViewBase>
  17. Navigate to AboutViewModel.cs and add the following code to use with a button to navigate to the MainView using the route specified in the Shell.

    public class AboutViewModel : ViewModelBase
    {
        private ICommand home;
        public ICommand Home => home ?? new TinyCommand(async () =>
        {
            await Navigation.NavigateToAsync("//home");
        });
    }
  18. Go to AboutView.xaml and create a Button to use with the Command in AboutViewModel.

    <mvvm:ViewBase
        xmlns:mvvm="clr-namespace:TinyMvvm.Forms;assembly=TinyMvvm.Forms"
        xmlns:vm="clr-namespace:SampleApp.ViewModels"
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        x:Class="SampleApp.Views.AboutView" x:TypeArguments="vm:AboutViewModel">
     <Grid>
         <Button Text="Go home!" Command="{Binding Home}" />
     </Grid>
    </mvvm:ViewBase>

The complete code for this sample can be found here: https://github.com/TinyStuff/TinyMvvm/tree/master/src/Samples