iOS 11 Large Titles in Xamarin.Forms apps

In iOS 11 Apple introduce large tiles in their design language. You can read more about it on the Xamarin blog, https://blog.xamarin.com/go-large-ios-11/, this post will focus on how to implement that in Xamarin.Forms.



Note: There is a pull request for Xamarin Forms about iOS11 fixes created by Rui Marinho, so this will probably be in Xamarin.Forms out of the box soon, https://github.com/xamarin/Xamarin.Forms/pull/1238

The way to do it is to create a custom page renderer, but I don’t want it to affect every page, I want to control it on each page. While I don’t want to create a new base page I decided to use attached properties.

So the first thing I do is to create a property to attach.

public class LargeTitle
{
        public static readonly BindableProperty ShowLargeTitleProperty =
            BindableProperty.CreateAttached("ShowLargeTitle", typeof(bool), typeof(LargeTitle), false);
 
        public static bool GetShowLargeTitle(BindableObject view)
        {
            return (bool)view.GetValue(ShowLargeTitleProperty);
        }
 
        public static void SetShowLargeTitle(BindableObject view, bool value)
        {
            view.SetValue(ShowLargeTitleProperty, value);
        }
}

After that, I can use it in the XAML. I just have to import the namespace.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"            
             xmlns:ap="clr-namespace:Demo.Client"
             x:Class="Demo.Client.Views.MainView" Title="This is a large title"
             ap:LargeTitle.ShowLargeTitle="True">
    <ContentPage.Content>
        <Grid Padding="10">
            <Button Text="Get started" HorizontalOptions="Fill" VerticalOptions="Center" Command="{Binding GetStarted}" />
        </Grid>
    </ContentPage.Content>
</ContentPage

The last thing is to create the custom renderer for iOS. I will override the ViewWillAppear method to set the PreferLargeTitle method to the value of the ShowLargeTitle property that I set the value for in the XAML. And I will also override the ViewDidDisappear to restore the value to false, to ensure that it not will be large on pages I don’t want it to be large on.

public class CustomPageRenderer : PageRenderer
    {
        public override void ViewWillAppear(bool animated)
        {
            base.ViewWillAppear(animated);
 
            var showLargetTitle = LargeTitle.GetShowLargeTitle(Element);
 
            if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0) && NavigationController != null && NavigationController.NavigationBar != null)
            {
                NavigationController.NavigationBar.PrefersLargeTitles = showLargetTitle; 
            }
        }
 
        public override void ViewDidDisappear(bool animated)
        {
            base.ViewDidDisappear(animated);
 
            if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0) && NavigationController != null && NavigationController.NavigationBar != null)
            {
                NavigationController.NavigationBar.PrefersLargeTitles = false;
            }
        }
    }

TinyNavigationHelper for Xamarin.Forms

TinyNavigationHelper is a helper that I created together with Johan Karlsson for a project where I don’t want to install a separate MVVM framework, but I still wanted to have an abstraction for the Navigation so that I could handle navigation in my ViewModels without having a reference to Xamarin.Forms in the ViewModels. The reason for that is that I want to have my ViewModels clean from platform specific libraries so I can use them for other platforms as well, for example, if I want to extend my application with support for WPF.

You will find the source code on GitHub and you’re more than welcome to contribute and give feedback.
https://github.com/dhindrik/TinyNavigationHelper. You will also find the complete documentation on GitHub.

Installation
The easiest way to install it is via NuGet:
Install-Package TinyNavigationHelper.Forms

And for projects that not reference Xamarin.Forms:
Install-Package TinyNavigationHelper.Abstraction

Configure TinyNavigationHelper
There are three ways to configure the navigation, the two first is to register the type of the view together with a key that you define. The third way is to register all views in an assembly. What the method will do that it searching throw the assembly for classes that inherit from Page.

// Option 1: Register single views
var navigationHelper = new FormsNavigationHelper(this);
navigationHelper.RegisterView("MainView");
 
// Option 2: Register single views
var navigationHelper = new FormsNavigationHelper(this);
navigationHelper.RegisterView("MainView", typeof(MainView));
 
// Option 3: Register all views (pages) that is inherited from Page
// The class name will be the key. To use this, you need to add using System.Reflection;
var asm = typeof(App).GetTypeInfo().Assembly;
navigationHelper.RegisterViewsInAssembly(asm);

If you want to use it with dependency injection you can register FormsNavigationHelper with the INavigationHelper interface.

Use TinyNavigationHelper
You can always get the current instance of TinyNavigationHelper via the static Current property on from the NavigationHelper class.

var navigationHelper = NavigationHelper.Current;

To navigate to a view, use the NavigateToAsync method.

await navigationHelper.NavigateToAsync("MainView");
 
//With parameter
await navigationHelper.NavigateToAsync("MainView", "Parameter");

The parameter will be sent to the constructor of the view.

public class MainView
{
     public MainView(object parameter)
     {
          var data = parameter as string;
     }
}

To go back, use the BackAsync method.

await navigationHelper.BackAsync();

You can also open a modal with the OpenModalAsync method.

await navigationHelper.OpenModalAsync("MainView");
 
//open a modal with an own navigation page
await navigationHelper.OpenModalAsync("MainView", true);
 
//with parameter
await navigationHelper.OpenModalAsync("MainView", "parameter", true);

You will close the modal with the CloseModalAsync method.

await navigationHelper.CloseModalAsync();

If you want to reset the navgation stack, you can use the SetRootView method.

//without parameter and navigation stack
navigationHelper.SetRootView("MainView");
 
//with parameter, but without navigation stack
navigationHelper.SetRootView("MainView", "parameter");
 
//without parameter, but with navigation stack
navigationHelper.SetRootView("MainView", true);
 
//with parameter and navigation stack
navigationHelper.SetRootView("MainView", "parameter", true);

Xamarin.Forms – MVVM BaseView and BaseViewModel

In this post, I will describe how I have created base classes for my views and ViewModels in my latest projects. It will probably break some of the MVVM rules, but I think this is a productive way to work. Please leave a comment about your thoughts about in a comment below.

First of all, I’m creating the base ViewModel. I’m adding three virtual methods, Init, OnAppearing and OnDisappering. I’m making the methods virtual so I can override them in the view models if I want.

My idea is that Init() will run after the ViewModel has been created. OnAppearing when a view appearing on the screen and disappearing when the view is disappearing. To place them in the base class for the view models makes it possible to call them from a base class for my views so I don’t have to write code for calling them in every view I’m creating.

If I using an MVVM framework (I’m used to using MvvmLight) my base ViewModel will inherit the base ViewModel from the framework I’m using. But this example will show how it will be without any framework.

public abstract class BaseViewModel : INotifyPropertyChanged
{
     public event PropertyChangedEventArgs PropertyChanged;
 
     public async virtual Task Init()
     {
     }
 
     public async virtual Task OnAppearing()
     {
     }
 
     public async virtual Task OnDisappearing()
     {
     }
}

My base view will be generic to define what ViewModel I will use for the current view. I will also say the generic type has to inherit from BaseViewModel so it not can be used with other types of object. This also makes it possible for me to call the methods I created in the BaseViewModel. In the constructor, I will use my IoC to create an instance of the ViewModel.

public abstract class BaseView<T> : ContentPage where T : BaseViewModel
{
 
     private BaseViewModel _viewModel;
 
     public BaseView()
     {
         //IoCHelper is an own defined class for resolving types that are defined in an IoC container
         _viewModel = IoCHelper.Resolve<T>();
 
         BindingContext = _viewModel;
 
         viewModel.Init();       
     }
 
     public async override void OnAppearing()
     {
           base.OnAppearing();
 
          await _viewModel.OnAppearing();
     }
 
     public async override void OnDisappearing()
     {
          base.OnDisappering();
 
          await _viewModel.OnDisappering();
     }
}

To define what ViewModel that should be used for the view in XAML you can do that by using x:TypeArguments like in the example below. This means that you don’t have to write any code in the code-behind. Don’t forget to remove that the view inherits from ContentPage in the code-behind of the view.

<local:BaseView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MyApp.Views;"
             xmlns:vm="clr-namespace:MyAPp.ViewModels;"
             x:TypeArguments="vm:MainViewModel"
             x:Class="MyApp.Views.MainView">

Add custom tiles to map in Xamarin Forms

If we want to use other maps than the platforms default in our apps we need to provide tiles to the map view. To do that we need to create a custom renderer per platform.

iOS
In iOS we need to create an url template that contains {x}, {y} and {z}. Those be replaced with values from the map engine.

protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
    if(e.NewElement != null)
    {
        var map = (MKMapView)Control;
 
        var urlTemplate = "https://urltomaptiles/{x}/{y}/{z}";
        var tileOverlay = new MKTileOverlay(urlTemplate);
 
        map.OverlayRenderer = OverlayRenderer;
 
        map.AddOverlay(tileOverlay);
    }
}
private MKOverlayRenderer RenderOverlay(MKMapView mapView, IMKOverlay overlay)
{
    var tileOverlay = overlay as MKTileOverlay;
 
    if(tileOverlay != null)
    {
         return new MKTileOverlayRenderer(tileOverlay);
    }
 
    return new MKOverlayRenderer(overlay);
}

If we are getting tiles from a service that not supporting the url format with x-,y- and z value we can customize the url. To do that we need to subclass MKTileOverlay and override the URLForTilePath method. In that method we will write the code that created the url. I recommend to create a helper class for that so we can reuse it on the other platforms.

public class CustomTileOverlay : MKTileOverlay
{
     public override void LoadTileAtPath(MKTileOverlayPath path, MKTileOverlayLoadTileCompletionHandler result)
     {
         base.LoadTileAtPath(path, result);
     }
 
     public override NSUrl URLForTilePath(MKTileOverlayPath path)
     {
         //Here we write the code for creating the url.
         var url = MapHelper.CreateTileUrl((int)path.X, (int)path.Y, (int)path.Z);
 
         return new NSUrl(url);
     }
}

Instead of creating a MKTileOverlay we will create a CustomTileOverlay and add it to the map.

map.AddOverlay(new CustomTileOverlay());

Android
Except to subclass MapRenderer we also need to implement the IOnMapReadyCallback interface. The method OnMapReady will handle when the GoogleMap object is ready so we can work with it. But first we need to request the GoogleMap object in the override of the OnElementChanged method.

public class ExtendedMapRenderer : MapRenderer, IOnMapReadyCallback
{
    protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
    {
        base.OnElementChanged(e);
 
        if(e.NewElement != null)
        {
            ((MapView)Control).GetMapAsync(this);    
        }
    }
 
    public void OnMapReady(GoogleMap googleMap)
    { 
        var options = new TileOverlayOptions();
        options.InvokeTileProvider(new CustomTileProvider());
        googleMap.AddTileOverlay(options);
    }
}

In Android we always need to create an own tile provider.

public class CustomTileProvider : UrlTileProvider
{
    public CustomTileProvider() : base(256,256) {}
 
    public override URL GetTileUrl(int x, int y, int zoom)
    {
        //Here we write the code for creating the url.
        var url = MapHelper.CreateTileUrl(x, y, zoom);
 
        return new URL(url);
    }
}

UWP (Windows 10)
As in iOS, UWP using an url that contains {x},{y} and {z} that will be replaced by the map engine.

protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
    base.OnElementChanged(e);
 
    if (e.NewElement != null)
    {
        map = Control as MapControl;
 
        HttpMapTileDataSource dataSource = new HttpMapTileDataSource("https://urltomaptiles/{x}/{y}/{z}");      
        MapTileSource tileSource = new MapTileSource(dataSource);
        map.TileSources.Add(tileSource);
 
     }
}

If we want to modify the url we using the UriRequested event on HttpMapTileDataSource.

HttpMapTileDataSource dataSource = new HttpMapTileDataSource();
dataSource.UriRequested += DataSource_UriRequested;

The code for modifying the url is placed in the event handler.

private void DataSource_UriRequested(HttpMapTileDataSource sender, MapTileUriRequestedEventArgs args)
{
    var deferral = args.Request.GetDeferral();
 
    //Here we write the code for creating the url.
    var url = MapHelper.CreateTileUrl(args.X, args.Y, args.ZoomLevel);
    args.Request.Uri = new Uri(url);
 
    deferral.Complete();
}

Use behaviors to handle states in Xamarin.Forms

I often see code that for example binds colors and texts for a button when they want to show different color and/or text when app is in different states. For example a button for login/logout the XAML could look like below.

<Button Color="{Binding ButtonColor}" Text="{Binding ButtonText} />

It will work but I don’t want to define the UI in view models.

My solution is to create behaviors for the controls. The behaviors should be so general as possible to increase the reusability. In this case it will be a LoginButtonBehavior.

public class LoginButtonBehavior : Behavior<CircleImage>
    {
        public static readonly BindableProperty IsLoggedInProperty =
        BindableProperty.Create("IsLoggedIn", typeof(bool), typeof(LoginButtonBehavior),
            null, propertyChanged: (bindable, oldValue, newValue) => {
                var behavior = (LoginButtonBehavior)bindable;
                behavior.Update();
            });
 
        public bool IsLoggedIn
        {
            get
            {
                return (bool)GetValue(IsLoggedInProperty);
            }
            set
            {
                SetValue(IsLoggedInProperty, value);
            }
        }
 
 
        public Button Control { get; set; }
 
        protected override void OnAttachedTo(Button bindable)
        {
            base.OnAttachedTo(bindable);
 
            Control = bindable;
 
           bindable.BindingContextChanged += (sender, _) => this.BindingContext = ((BindableObject)sender).BindingContext;
        }
 
        public void Update()
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                 if(IsLoggedIn)
                 {
                       Control.BackgroundColor = Color.Red;
                       Control.Text = "Logout";
                 }
                 else
                 {
                       Control.BackgroundColor = Color.Green;
                       Control.Text = "Login";
                 }
            });
        }
    }

When we have created the behavior we will attach it to the button.

<Button>
     <Button.Behaviors>
          <behaviors:LoginButtonBehavior IsLoggedIn="{Binding IsLoggedIn}" />
     </Button.Behaviors>
</Button>

You can read more about behaviors at Xamarin’s developer portal, https://developer.xamarin.com/guides/xamarin-forms/working-with/behaviors/

MvvmLight navigation in Xamarin.Forms

MvvmLight is a very popular Mvvm framework created by Laurent Bugnion. I like to use MvvmLight but when developing mobile apps I miss the possibility to create modals, so I created an extension to the navigation service in MvvmLight, read more about it here.

We can also use MvvmLight when we developing apps with Xamarin.Forms but there are no navigation service implementation for it. Because of that I have added that to my MvvmLight Navigation Service Extension. The code and complete samples can be found on GitHub.

The first step to use it is to install the nuget package.

install-package MvvmLightNavigationServiceExtension.Forms

Next step is to configure the navigation service. This is pretty much the same as if we use MvvmLight in other project. The important part here is the Initialize method that require a Xamarin.Forms navigation object. We will get the navigation object from a NavigationPage that we create before we starting to configure MvvmLight. In this sample and the in the sample code on GitHub I will use Autofac as IoC container. All the code below should be in the constructor of the App class in your Xamarin.Forms project.

navigationPage = new NavigationPage();
 
var navigationService = new NavigationService();
navigationService.Initialize(navigationPage.Navigation);
 
navigationService.Configure("Main", typeof(MainView));
navigationService.Configure("About", typeof(AboutView));
 
var builder = new ContainerBuilder();
builder.RegisterInstance<INavigationService>(navigationService);
 
var container = builder.Build();
 
ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(container));
 
navigationPage.PushAsync(new MainView());
 
MainPage = navigationPage;

To use it in a ViewModel we need to resolve the INavigationService interface.

var navigation = ServiceLocator.Current.GetInstance<INavigationService>();
navigation.NavigateTo("About");

We can also have a parameter with the NavigateTo method, but then the view we navigating to need to have a constructor with one parameter.

var navigation = ServiceLocator.Current.GetInstance<INavigationService>();
navigation.NavigateTo("About", myParameter);
public MySecondView(object parameter)
{
     //Do something with the parameter.
}

If we want to use OpenModal method from the MvvmLight Navigation Extension we had to add a using to it.

using MvvmLightNavigationExtension;
var navigation = ServiceLocator.Current.GetInstance<INavigationService>();
navigation.OpenModal("About");

If you have feedback, feel free to contact me.

How to solve Xamarin.Forms android build error after updated to Forms 2.0+

Exception while loading assemblies: System.IO.FileNotFoundException: Could not load assembly 'Microsoft.Windows.Design.Extensibility, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. Perhaps it doesn't exist in the Mono for Android profile?

If you updated the nuget packages for Xamarin Forms to 2.0+ and the android project stops to build there are a simple solution. Just delete the references which ends with .Design. In my project it was: Xamarin.Forms.Core.Design, Xamarin.Forms.Xaml.Design and Xamarin.Android.Support.Design. I have read about a solution to add the assemblies Microsoft.Windows.Design.Extensibility and System.Xaml. I don’t know if it will work, I think to delete the .Design references is a better solution so I never tried to add those assemblies.

Xamarin.Forms and large images in android apps

Are you getting out of memory exception when your running your Android app? It’s common that the reason is that a large image is loaded. If it’s the case, take a look at this article at the Xamarin developer portal, http://developer.android.com/training/displaying-bitmaps/load-bitmap.html.

But how to implement it when you’re building your apps with Xamarin.Forms? In this post I will show one solution how to implement it. We will do it with an custom view and a custom renderer.

First we will create the new view that will inherit from the standard Image view, I will name it LargeImage. While we doesn’t want the default behavior we need to create our own source property of type string, I name it ImageSource. While we just want to change the behavior for the Android app and not for Windows and iOS we will set the base Source property in the property changed handler of the ImageSource property if the code not is running on Android.

public class LargeImage : Image
{
        public static readonly BindableProperty ImageSourceProperty =
        BindableProperty.Create("ImageSource", typeof(string), typeof(LargeImage), default(string), propertyChanged: (bindable, oldValue, newValue) => 
        {
            if (Device.OS != TargetPlatform.Android)
            {
                var image = (LargeImage)bindable;
 
                var baseImage = (Image)bindable;
                baseImage.Source = image.ImageSource; 
            }
        });
 
        public string ImageSource
        {
            get { return GetValue(ImageSourceProperty) as string; }
            set { SetValue(ImageSourceProperty, value); }
        }
}

Next step is to create a renderer for our new view. While we need the default Image behavior except for handling the source the renderer will inherit from ImageRenderer.

This renderer will only work for images in the Drawable folder, so if you have other type of image sources you need to modify the code.

In the renderer we need to handle the ImageSource property, we will do that in the OnPropertyChanged method. While we doesn’t want to run the code before the image has width and height we added a if-statement that check if width and height is greater than zero. But we just want it to run once because of that width and height is greater than zero, because of that i have added a flag that I named _isDecoded. If ImageSource changed the code will run because that e.PropertyName will be ImageSource.

[assembly: ExportRenderer(typeof(LargeImage), typeof(LargeImageRenderer))]
namespace SampleApp.Droid.Renderers
{
    public class LargeImageRenderer : ImageRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
        {
            base.OnElementChanged(e);
        }
 
        private bool _isDecoded;
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
 
            var largeImage = (LargeImage)Element;
 
            if ((Element.Width > 0 && Element.Height > 0 && !_isDecoded) || (e.PropertyName == "ImageSource" && largeImage.ImageSource != null)) 
            {
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.InJustDecodeBounds = true;
 
                //Get the resource id for the image
                var field = typeof(Resource.Drawable).GetField(largeImage.ImageSource.Split('.').First());
                var value = (int)field.GetRawConstantValue();
 
                BitmapFactory.DecodeResource(Context.Resources, value,options);
 
                //The with and height of the elements (LargeImage) will be used to decode the image
                var width = (int)Element.Width;
                var height = (int)Element.Height;
                options.InSampleSize = CalculateInSampleSize(options, width, height);
 
                options.InJustDecodeBounds = false;
                var bitmap = BitmapFactory.DecodeResource(Context.Resources, value, options);
 
                //Set the bitmap to the native control
                Control.SetImageBitmap(bitmap);
 
                _isDecoded = true;
            }
 
        }
        public int CalculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
        {
            // Raw height and width of image
            float height = options.OutHeight;
            float width = options.OutWidth;
            double inSampleSize = 1D;
 
            if (height > reqHeight || width > reqWidth)
            {
                int halfHeight = (int)(height / 2);
                int halfWidth = (int)(width / 2);
 
                // Calculate a inSampleSize that is a power of 2 - the decoder will use a value that is a power of two anyway.
                while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth)
                {
                    inSampleSize *= 2;
                }
            }
 
            return (int)inSampleSize;
        }
    }
}

MvvmLight and Xamarin.Android

Last week I wrote a blog post about Xamarin.iOS and MvvmLight. Now it’s time for the second post about MvvmLight, this time about how to use it with Xamarin.Android.

Because I put the ViewModels in a separate project I can use the same ViewModels for both Android and iOS.

First we install the NuGet package for MvvmLight to the Android project.

Install-package MvvmLightLibs

The ViewModels that we will use is the same as in the iOS app and it will look like this.

public class MainViewModel : ViewModelBase
    {
        private string _name;
 
        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                RaisePropertyChanged("Name");
            }
        }
 
        public RelayCommand Send
        {
            get
            {
                return new RelayCommand(() =>
                {
                        var nav = ServiceLocator.Current.GetInstance<INavigationService>();
                        nav.NavigateTo(Views.Hello.ToString(), Name);                    
                });
            }
        }
    }

Navigation

MvvmLight has a INavigationService interface that uses for navigation and each platform will have their own implementation. For Android we will do the configuration in MainActivity. Important is to check if navigation already has been initialized. The code will just run once.

if(!_isInitialized)
{
      var nav = new NavigationService();            
      nav.Configure(Core.Views.Main.ToString(), typeof(MainActivity));
      nav.Configure(Core.Views.Hello.ToString(), typeof(HelloActivity));
 
     _isInitialized = true;
}

In my example I using Autofac for IoC, we can also use the IoC container that is in the MvvmLight package. When we have created the NavigationService we had to register it in the IoC container as a INavigationService.

var builder = new ContainerBuilder();
builder.RegisterInstance<INavigationService>(nav);
 
builder.RegisterType<MainViewModel>();
builder.RegisterType<HelloViewModel>();
 
var container = builder.Build();
 
var serviceLocator = new AutofacServiceLocator(container);
 
ServiceLocator.SetLocatorProvider(() => serviceLocator);

To navigate we will resolve the INavigationService interface and use the NavigateTo method.

var nav = ServiceLocator.Current.GetInstance<INavigationService>();
nav.NavigateTo(Views.Hello.ToString(), "Navigation paramter");

To retrieve the parameter we are using the GetAndRemoveParameter in the NavigationService class. Note that this is an Android specific method so we have to cast the INavigationService to NavigationService.

var nav = (NavigationService)ServiceLocator.Current.GetInstance<INavigationService>();
var param = nav.GetAndRemoveParameter<string>(Intent);
 
ViewModel = ServiceLocator.Current.GetInstance<HelloViewModel>();
ViewModel.Name = param;

Databindings

When using MVVM we want to use data bindings. In Android we have to create the bindnings in code. MvvmLight will help us with that. In the class for the Activity we hade to add a using to the MvvmLight helpers.

using GalaSoft.MvvmLight.Helpers;

The activity also has to inherit from ActivityBase (GalaSoft.MvvmLight.Views.ActivityBase).

public class HelloActivity : ActivityBase
{

The MvvmLight helper namespace will contains the extension methods SetBinding and SetCommand.

The fields that vi are creating bindings to need to be declared as public in the Activity.

public EditText Text { get; private set; }
 
protected override void OnCreate(Bundle bundle)
{
     var button = FindViewById<Button>(Resource.Id.send);
     Text = FindViewById <EditText>(Resource.Id.name);
 
     this.SetBinding(() => ViewModel.Name,() => Text.Text, BindingMode.TwoWay);
     button.SetCommand("Click", ViewModel.Send);
}

The SetCommand method’s first argument will be which event that will execute the command.

I create the ViewModel in the OnCreate method using the ServiceLocator, I prefer to create it with the ServiceLocator directly instead of wrapping it in a ViewModelLocator which is a common way to do it when using MvvmLight.

The complete code for this sample is on GitHub, https://github.com/dhindrik/XamarinMvvmLightSample

Use MvvmLight in Xamarin.iOS

I think MVVM is a good design pattern to separate UI and business logic. MvvmLight is one of the most popular frameworks for MVVM. In this blog post I will show how you can use MvvmLight when we’re building a iOS app with Xamarin.

The fist step is to create a view model. I always have my view models in a PCL (Portable Class Library) so I can share the view models with other platforms, Android and Windows for example.

When I have created a new project I will install MvvmLight to it. The easiest way to do that is to install the NuGet package for MvvmLight. The package should be added to both the PCL project and the iOS project.

Install-package MvvmLightLibs

When we are working with MVVM all ViewModels need to implement the INotifyPropertyChanged interface. INotifyPropertyChanged has one event handler, PropertyChanged that we have to implement. When using MvvmLight this is done in a base class called ViewModelBase.

In my example the viewmodel

public class MainViewModel : ViewModelBase
{
        private string _name;
 
        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                RaisePropertyChanged("Name");
            }
        }
 
        public RelayCommand Send
        {
            get
            {
                return new RelayCommand(() =>
                {
                    var nav = ServiceLocator.Current.GetInstance<INavigationService>();
                    nav.NavigateTo(Views.Hello.ToString(), Name);  
                });
            }
        }
}

Navigation

MvvmLight has a INavigationService interface that uses for navigation and each platform will have their own implementation. On iOS you need to create a NavigationService and configure it in the AppDelegate class.

var nav = new NavigationService();
nav.Initialize((UINavigationController)Window.RootViewController);
nav.Configure(Views.Main.ToString(), "MainView");
nav.Configure(Views.Hello.ToString(), "HelloViewController");

In my example I using Autofac for IoC, we can also use the IoC container that is in the MvvmLight package. When we have created the NavigationService we had to register it in the IoC container as a INavigationService.

var builder = new ContainerBuilder();
builder.RegisterInstance<INavigationService>(nav);
 
builder.RegisterType<MainViewModel>();
builder.RegisterType<HelloViewModel>();
 
var container = builder.Build();
 
var serviceLocator = new AutofacServiceLocator(container);
 
ServiceLocator.SetLocatorProvider(() => serviceLocator);

If you’re using storyboards you have to use the Configure(string key, string storyboardId) method. The key is just a key that we set to the view, we will use that key when we will navigate to the view. (See the code fore the ViewModel above. StoryboardId is a property that we’re setting in the storyboard designer.

Properties

Data bindings

When using MVVM we want to use data bindings. In iOS we have to create the bindnings in code. MvvmLight will help us with that. In the class for the UIViewController we hade to add a using to the MvvmLight helpers.

using GalaSoft.MvvmLight.Helpers;

The MvvmLight helper namespace will contains the extension methods SetBinding and SetCommand.

When binding to a UITextField we can set which event who will trigger an update of the our ViewModel with the method UpdateSourceTrigger. WhenSourceChange is used to set which property in the ViewModel who will be updated.

The SetCommand method’s first argument will be which event that will execute the command.

public override void ViewDidLoad()
{
            base.ViewDidLoad();
 
           //Creates a binding to a UILabel
            this.SetBinding(() => ViewModel.Name, () => MyLabel.Text);
 
           //Creates a binding to a UITextField
            this.SetBinding(() => Name.Text).
                UpdateSourceTrigger("EditingChanged").
                WhenSourceChanges(() => ViewModel.Name = MyTextField.Text);
 
            //Binding a command to the Button.
            MyButton.SetCommand("TouchUpInside", ViewModel.Send);
}

The complete code for this sample is on GitHub, https://github.com/dhindrik/XamarinMvvmLightSample

How to succeed with Xamarin.Forms

Xamarin.Forms makes it possible to write UI for all the major mobile platforms, iOS, Android and Windows Phone with one shared code base. Many developers think that Xamarin.Forms isn’t is good enough to create apps that will be published and they think Xamarin.Forms is more a tool for prototyping.

If you think Xamarin.Forms is a “magic” product that will fix everything, you will properly not succeed with Xamarin.Forms. For me Xamarin.Forms is a framework that helps me build apps for multiple platforms. If you look at Xamarin.Forms in that way you will increase your chances to success with Xamarin.Forms.

Xamarin.Forms is delivered with a lot of controls that uses the Xamarin.Forms framework to render native platform specific controls. A Xamarin.Forms control is in many way just a description of what the control can do. From that description is native controls rendered.

The power of Xamarin.Forms is that you can use the framework to create you own renderers if you want to renderer a control different then the standard renderer. You can also create your own controls by using custom renderers.

I guess one of the most common issues with Xamarin.Forms i ListView performance. It’s not surprising, you maybe have 5 or 6 controls for each cell (row). If cells are reused you might have 8 cells. It means that forms need to create at least 40 renderer objects. And if any of the controls is a StackLayout or a RelativeLayout where it’s render has do to a lot of calculations where to place controls I guess you will realize that it will use a lot of memory.

So if you instead create your own custom cell that has it own renderer, there will be only one renderer for each row or if you write a renderer for the whole list you will only have one renderer. Can you realize how much memory you will save on that? If not you will see it when you are scrolling in your ListView,

The biggest problem when using Xamarin.Forms is that the developers don’t know how Xamarin.Forms works and they don’t know much about the target platforms. If you want to create a excellent app with Xamarin.Forms you still need to have knowledge about the target platforms.

Now you maybe want to ask me why you should use Xamarin.Forms? The answer is that even if you have to write platform specific code for some views there are still much you can use of the controls that is delivered with Xamarin.Forms out of the box and the powerful Xamarin.Forms framework makes it possible to write platform specific code when what you get out of the box with Xamarin.Forms not is enough.

My recommendation is to so do as much as possible with what you get out of the box with forms and don’t care about performance and if it doesn’t look perfect from the beginning. When you have created your app and built all the business logic, then you can start to look at how to make the app perfect. Than you can start write platform specific code to get better performance and a better look of the app.

Workaround if you have a lots of errors for your Xamarin projects in Visual Studio 2015

If you are running Xamarin projects in Visual Studio 2015 you maybe have a lot of error related to assemblies that not is referenced.

A lot of error in the error list.

A lot of error in the error list.

Even if there are errors the project will build successfully. Microsoft says this is a Xamarin bug and that the have reported it to Xamarin.

Until it’s fixed there are a workaround. On the top of the error list there are a filter where you can select source for the error list. The default source is Build + IntelliSense, if you select to just show  errors from build the errors will be gone from the list.

You can select source for the error list.

You can select source for the error list.

With "Build Only" the errors is gone.

With “Build Only” the errors is gone.

TechDays 2015

Den 20 oktober börjar TechDays med förkonferens, jag och Johan Karlsson kommer att dela med oss av våra kunskapar gällande cross-platformutveckling.

Att bygga cross-platformlösningar är alltid en stor utmaning då det i grunden är olika operativsystem, programmeringsspråk och utvecklingsmiljöer.
Xamarin löser en stor del av cross platform problematiken genom att man kan utveckla appar för iOS, OSX och Android med C#. iOS och Android kan även byggas i Visual Studio. Under dagen kommer vi gå genom hur man bygger arkitektur och gränssnitt för en optimal native upplevelse på varje plattform, det vill säga iOS, OSX, Android och Windows. Vi kommer också att visa hur man kan testa mobila applikationer på ett effektivt sätt med hjälp av Xamarin TestCloud.

Läse mer här, http://tdswe.kistamassan.se/Program-2015/Sessioner/Pre-Conf-Cross-Platform-pa-ratt-satt

iOS range slider for Xamarin and Xamarin.Forms

In this post I will show you how to build a range slider for Xamarin.iOS and how to write a custom renderer if you want to use it with Xamarin.Forms. If you want to use the range slider control you can write the code yourself or use the flipper forms control library, https://github.com/johankson/flipper, that can be installed from NuGet, https://www.nuget.org/packages/Flipper.Forms/. On GitHub you will also find a sample app that using the control.


iOS Range Slider

The control will be built up of six UIViews, one for the slider background, two for the indicators, two transparent views to increase the touch area for the indicators and one for the range between the indicators. The first step is to create a new class that inherits from UIView. In the constructor of the new class we are creating the views and adding them to the view. On the both indicators we’re adding a UIPanGestureRecognizer so we can detect when the users dragging the indicator.

public class RangeSlider : UIView
{
    private UIView _background, _leftIndicator, _rightIndicator, _range, _leftTouchArea, _rightTouchArena;
        private UIPanGestureRecognizer _leftIndicatorGesture, _rightIndicatorGesture;
 
     public RangeSlider()
     {
            _background = new UIView();
            _background.BackgroundColor = UIColor.LightGray;
 
            _range = new UIView();
            _range.BackgroundColor = UIColor.Blue;
 
            _leftIndicator = CreateIndicator();
            _leftIndicatorGesture = new UIPanGestureRecognizer(OnPan);
 
            _rightIndicator = CreateIndicator();
            _rightIndicatorGesture = new UIPanGestureRecognizer(OnPan);
 
            _leftTouchArea = new UIView();
            _leftTouchArea.BackgroundColor = UIColor.Clear;
            _leftTouchArea.AddGestureRecognizer(_leftIndicatorGesture);
 
            _rightTouchArena = new UIView();
            _rightTouchArena.BackgroundColor = UIColor.Clear;
            _rightTouchArena.AddGestureRecognizer(_rightIndicatorGesture);
 
            AddSubview(_background);
            AddSubview(_range);
            AddSubview(_leftIndicator);
            AddSubview(_rightIndicator);
            AddSubview(_leftTouchArea);
            AddSubview(_rightTouchArena);     
     }
 
     private UIView CreateIndicator()
     {
            var indicator = new UIView()
            {
                   BackgroundColor = UIColor.Gray
            };
 
            indicator.Layer.CornerRadius = 10;
            indicator.Layer.MasksToBounds = true;
 
            return indicator;
      }
}

Then we need to layout our views, we will do that in an override of the LayoutSubviews method. We also need to check if the views already is layouted so the layout not will be restored to the start layout if the method runs again.

private bool _layouted;
public override void LayoutSubviews()
{
      base.LayoutSubviews();
 
      if (!_layouted)
      {
            _background.Frame = new RectangleF(0, 19, (float)Frame.Width-20, 2);
            _range.Frame = new RectangleF(0, 19, (float)Frame.Width-20, 2);
            _leftIndicator.Frame = new RectangleF(0, 10, 20, 20);
            _rightIndicator.Frame = new RectangleF((float)Frame.Width - 40, 10, 20, 20);
 
            _leftTouchArea.Frame = new RectangleF(0, 0, 40, 40);
            _rightTouchArena.Frame = new RectangleF((float)Frame.Width - 60, 0, 40, 40);
 
            _layouted = true;
       }
}

In the OnPan method we want to update the position of the indicators if state of the gesture recognizer is began or changed. For this range slider we want the indicator to move in steps. To do this we need to move the indicator to next step if we have started to slide from the previous step. For that we need to know the step length in pixels and the cumulative manipulation and the delta manipulation. To calculate cumulative manipulation we need to save the position of the indicator when we starting the manipulation.

While you moving the indicator to next step when it has passed the previous you will have to check if the cumulative manipulation has passed the current step before you moving the indicator to next step.

 private void OnPan(UIPanGestureRecognizer recognizer)
        {
            if (recognizer.State == UIGestureRecognizerState.Began || recognizer.State == UIGestureRecognizerState.Changed)
            {
                var stepLength = _background.Frame.Width / ((MaxValue - MinValue) / Step);
 
                var touchPoint = recognizer.LocationInView(this);
 
                UIView indicator = null;
                UIView touchArea = null;
 
                //Is this a slide to left or right?
                if (recognizer == _leftIndicatorGesture)
                {
                    indicator = _leftIndicator;
                    touchArea = _leftTouchArea;
                }
                else if (recognizer == _rightIndicatorGesture)
                {
                    indicator = _rightIndicator;
                    touchArea = _rightTouchArena;
                }
 
                //save the start position for use when calculating cumulative manipulation
                if (recognizer.State == UIGestureRecognizerState.Began)
                {
                    _startX = (float)indicator.Center.X;
                }
 
 
                var cumulativeManipulation = touchPoint.X - _startX;
                var deltaManipulation = touchPoint.X - indicator.Center.X;
 
                //Check if the cumulative manipulation is has passed the last step
                if (deltaManipulation > 0 && cumulativeManipulation / stepLength > _lastStep ||
                    deltaManipulation < 0 && cumulativeManipulation / stepLength < _lastStep)
                {
                    if (deltaManipulation > 0)
                    {
                        _lastStep++;
                    }
                    else
                    {
                        _lastStep--;
                    }
 
                    //Calculate the new position of the indicator
                    var numberOfSteps = Math.Ceiling(deltaManipulation / stepLength);
                    var newPosition = new CGPoint(indicator.Center.X + stepLength * numberOfSteps, indicator.Center.Y);
 
                    var pixelStep = (MaxValue - MinValue) / Frame.Width;
 
                    if (touchPoint.X >= 0 && touchPoint.X <= _background.Frame.Width-10)
                    {
 
 
                        if (recognizer == _leftIndicatorGesture)
                        {
 
                            var newLeftValue = Round(MinValue + (pixelStep * newPosition.X));
 
                            if (newLeftValue >= RightValue)
                            {
                                return;
                            }
                        }
                        else if (recognizer == _rightIndicatorGesture)
                        {
                            var newRightValue = Round(MinValue + (pixelStep * newPosition.X));
 
                            if (newRightValue <= LeftValue)
                            {
                                return;
                            }
                        }
 
 
                        if (recognizer == _leftIndicatorGesture)
                        {
                            indicator.Center = newPosition;
                            touchArea.Center = newPosition;
                            var width = _rightIndicator.Center.X - _leftIndicator.Center.X;
                            _range.Frame = new CoreGraphics.CGRect(newPosition.X, _range.Frame.Y, width, _range.Frame.Height);
                        }
                        else if (recognizer == _rightIndicatorGesture)
                        {
                            indicator.Center = newPosition;
                            touchArea.Center = newPosition;
                            var width = _rightIndicator.Center.X - _leftIndicator.Center.X;
                            _range.Frame = new CoreGraphics.CGRect(_range.Frame.X, _range.Frame.Y, width, _range.Frame.Height);
                        }
 
 
 
                        LeftValue = Round(MinValue + (pixelStep * _leftIndicator.Center.X));
                        RightValue = Round(MinValue + (pixelStep * _rightIndicator.Center.X));
 
                        if (ValueChanging != null)
                        {
                            ValueChanging(this, new EventArgs());
                        }
                    }
                }
            }
            else if (recognizer.State == UIGestureRecognizerState.Ended)
            {
                if (ValueChanged != null)
                {
                    ValueChanged(this, new EventArgs());
                }
 
                _lastStep = 0;
            }
        }

I have added to events to the slider, ValueChanging and ValueChanged. ValueChanging occurs during the manipulation of the range slider and ValueChanged when the manipulation is finished. This because you maybe want to update labels with the values of the range slider during manipulation and update data based on the range slider when the manipulation is completed.

We also want to make it possible to set start values for the indicators. To do that we are creating a method called UpdateValue. We call the method from LayoutSubviews and from the setters of LeftValue and RightValue. It is important that the code not is running after that we have started using the slider. Therefore we have surrounded the call to the method with an if-statment. The code will run if a initialized variable is false, we will set it to true in the OnPan method.

private void UpdateValue(UIView indicator, UIView touchArea, double value)
{
            var percent = value / (MaxValue - MinValue);
 
            var position = (double)(_background.Frame.Width * percent);
 
            if (!double.IsNaN(position))
            {
                indicator.Center = new CGPoint(position, indicator.Center.Y);
                touchArea.Center = new CGPoint(position, indicator.Center.Y);
 
                var width = _rightIndicator.Center.X - _leftIndicator.Center.X;
                _range.Frame = new CoreGraphics.CGRect(_leftIndicator.Center.X, _range.Frame.Y, width, _range.Frame.Height);
 
                if (ValueChanged != null)
                {
                    ValueChanged(this, new EventArgs()); 
                }
            }
}

Xamarin.Forms
If you want to use the control with Xamarin.Forms you need to create a Xamarin.Forms control and a custom renderer. The Xamarin.Forms control will inherit from View and it will contains all the properties of the range slider, no logic at all.

 public class RangeSliderRenderer : ViewRenderer<Flipper.Controls.RangeSlider, iOS.Controls.RangeSlider>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<RangeSlider> e)
        {
            base.OnElementChanged(e);
 
            if (e.NewElement != null)
            {
                var slider = new Controls.RangeSlider();
                slider.Frame = new CGRect((float)Frame.X, (float)Frame.Y, (float)Frame.Width, 200);
 
                slider.ValueChanging += slider_ValueChanging;
                slider.ValueChanged += slider_ValueChanged;              
 
                SetNativeControl(slider); 
            }
 
        }
 
        void slider_ValueChanged(object sender, EventArgs e)
        {
           if(Element.Command != null && Element.Command.CanExecute(null))
           {
               Element.Command.Execute(null);
           }
 
           Element.NotifyValueChanged();
        }
 
        void slider_ValueChanging(object sender, EventArgs e)
        {
            Element.LeftValue = (float)Control.LeftValue;
            Element.RightValue = (float)Control.RightValue;
        }

The complete code can be found at GitHub, https://github.com/johankson/flipper

Xamarin.Forms and iOS segemented control

A common control in many iOS apps is the segmented control. Xamarin.Forms has no support for the segemented control out of the box. This blog post will show you how to create a control in Xamarin.Forms that is mapped to UISegmentedControl in iOS.

In my sample I have named the control, FilterControl because is often used as a filter control and if you write a implementation of the control on the other platforms it will maybe not look lite the segmented control.

To make the control usefull I need to be able to bind items to it, bind to selected index, have an event that is thrown when selected index is changed and be able to change the color.

The first I will do is to create a class in the shared project that inherits from View.

If the properties will be bindable you have to create a BindableProperty as shown in the code below.

To set the choises (items) of the control we’re creating a List of strings.

public static readonly BindableProperty ItemsProperty =
			BindableProperty.Create<FilterControl, List<string>>
		(p => p.Items, new List<string>());
 
public List<string> Items 
{
	get 
        {
		return GetValue (ItemsProperty) as List<string>;
	}
	set 
        { 
		SetValue (ItemsProperty, value);
	}
}

When the control is defined next step is to create a renderer for iOS. The renderer will inherit from ViewRenderer.

public class FilterControlRenderer : ViewRenderer<FilterControl, UISegmentedControl>

In the override of the OnElementChanged we will create the native control, UISegmentedControl. I will put the code to map the list of choises to the segment control in a separate private method so I also can use it if the list of choices is changed. Then I will call it from the override of the OnElementPropertyChanged method.

You need to listen to ValueChanged of the UISegmentedControl so you can write the value back to the Xamarin.Forms control. In the event for selection changed I want both the old and new value, there for will I dont set selected index from the renderer. Instead I call a method in the control that is setting SelectedIndex and throwing the selection changed event.

protected override void OnElementChanged (ElementChangedEventArgs<FilterControl> e)
{
	base.OnElementChanged (e);
 
	var segmentControl = new UISegmentedControl ();
        SetNativeControl (segmentControl);
 
	UpdateSegments ();
 
	segmentControl.SelectedSegment = Element.SelectedIndex;
	segmentControl.SizeToFit ();
 
	segmentControl.ValueChanged += (object sender, EventArgs args) => 
	{
		if(segmentControl.SelectedSegment != Element.SelectedIndex)
		{
		     Element.OnSelectedIndexChanged((int)segmentControl.SelectedSegment);
		}
	};
}
 
private void UpdateSegments()
{
	Control.RemoveAllSegments ();
 
	for (int i = 0; i < Element.Items.Count; i++) 
	{
		Control.InsertSegment (Element.Items [i],i,true);
	}
}
public void OnSelectedIndexChanged(int newValue)
{
        var args = new IndexChangedEventArgs () {
		NewValue = newValue,
		OldValue = SelectedIndex
	};
 
	SelectedIndex = newValue;
 
	if (SelectedIndexChanged != null) 
	{				
		SelectedIndexChanged (this, args);
	}
}

Of course we also need to update the native control if the value for selected index is changed. That code will be placed in the OnElementPropertyChanged method.

protected override void OnElementPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
        base.OnElementPropertyChanged (sender, e);
 
	if (e.PropertyName == FilterControl.SelectedIndexProperty.PropertyName) 
	{
		if (Control.SelectedSegment != Element.SelectedIndex) 
		{
		         Control.SelectedSegment = Element.SelectedIndex;
		}
	}

The complete code can be found on GitHub, https://github.com/dhindrik/XamarinFormsSamples/tree/master/SegmentedControl

If you want the control to be in the NavigationBar on iOS read this blog post.

Xamarin.Forms: Add content to TitleView in your iOS app

If you’re building an iOS app you maybe want to add content to the NavigationBar. This blog post will show you how to build a generic solution so you don’t have to write a custom renderer for each view that you want NavigationBar content for.

First of all I creating a new class that inherits from ContentPage and adding a property of type View. I use to name the property TitleView, because that’s the name in iOS.

public class ExtendedContentPage : Xamarin.Forms.ContentPage
{
    public View TitleView { get; set; }
}

Now If your views/pages inherits from your new class you can add content to TitleView.

<local:ExtendedContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:TitleViewSample"
             x:Class="TitleViewSample.MainView">
  <local:ExtendedContentPage.TitleView>
    <Button x:Name="TestButton" Text="Test" BackgroundColor="Green" />
  </local:ExtendedContentPage.TitleView>
</local:ExtendedContentPage>

To get it work you also have to write a custom page renderer for iOS. Override the WillMoveToParentViewController method in the renderer that will inherit PageRenderer and add the code below.

 base.WillMoveToParentViewController(parent);
 
 var page = (ExtendedContentPage)Element;
 
 var renderer = RendererFactory.GetRenderer(page.TitleView);
 var view = renderer.NativeView;
 view.SizeToFit();
 
 parent.NavigationItem.TitleView = view.Subviews[0];

It is important that you adding the first subview of the native view, otherwise it will be just blank.

The complete code can be found on GitHub, https://github.com/dhindrik/XamarinFormsSamples/tree/master/TitleView

Xamarin.Forms Android CardView

When Google introduced Material Design for Android they introduced a new view called CardView. Xamarin.Forms doesn’t have support for CardView by default but you can easily create your own view that renderers a CardView on Android.

First step is to create a Xamarin.Forms control in your shared project.

public class CardContentView : ContentView
{
 
 
		public static readonly BindableProperty CornerRadiusProperty = 
			BindableProperty.Create<CardContentView,float> 
		( p => p.CornderRadius, 3.0F);   
 
 
 
        public new static readonly BindableProperty BackgroundColorProperty =
            BindableProperty.Create<CardContentView, Color>
        (p => p.BackgroundColor, Color.White);
 
        public float CornderRadius
        {
            get { return (float)GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }
 
        public new Color BackgroundColor
        {
            get { return (Color)GetValue(BackgroundColorProperty); }
            set { SetValue(BackgroundColorProperty, value); }
        }
 
		protected override SizeRequest OnSizeRequest (double widthConstraint, double heightConstraint)
		{
			if (Content == null)
				return new SizeRequest(new Size(100, 100));
 
			return Content.GetSizeRequest (widthConstraint, heightConstraint);
		}
	}

Next step is to create a custom renderer for Android, on iOS and Windows it will be rendered as a regular ContentView.
You need to add a NuGet package named Xamarin.Android.Support.v7.CardView to get the Android CardView. The renderer will inherit form CardView and implement the IVisaulElementRenderer interface. To get card shadow on both Android KitKat and Android Lollipop you have to set UseCompatPadding to true. You have to remove all view from the ViewGroup, if you don’t do that you will get problems when you using the control in a ListView. The Load method on the VisualElementPackager will load content in to the Card.

[assembly:ExportRendererAttribute(typeof(CardContentView), typeof(CardViewRenderer))]
namespace CardViewFormsAndroid
{
	public class CardViewRenderer : CardView, 
		IVisualElementRenderer
	{
		public CardViewRenderer () : base (Forms.Context)
		{
		}
 
		public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
 
		bool init;
		ViewGroup packed;
		public void SetElement (VisualElement element)
		{
			var oldElement = this.Element;
 
			if (oldElement != null)
				oldElement.PropertyChanged -= HandlePropertyChanged;
 
			this.Element = element;
			if (this.Element != null) {
 
				this.Element.PropertyChanged += HandlePropertyChanged;
			}
 
            ViewGroup.RemoveAllViews();
				//sizes to match the forms view
				//updates properties, handles visual element properties
				Tracker = new VisualElementTracker (this);
 
            Packager = new VisualElementPackager(this);
            Packager.Load();
 
            UseCompatPadding = true;
 
            SetContentPadding((int)TheView.Padding.Left, (int)TheView.Padding.Top,
                   (int)TheView.Padding.Right, (int)TheView.Padding.Bottom);
 
                Radius = TheView.CornderRadius;
                SetCardBackgroundColor(TheView.BackgroundColor.ToAndroid());
 
			if(ElementChanged != null)
				ElementChanged (this, new VisualElementChangedEventArgs (oldElement, this.Element));
		}
 
 
 
 
		public CardContentView TheView
		{
			get { return this.Element == null ? null : (CardContentView)Element; }
		}
 
 
		void HandlePropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
		{
			if (e.PropertyName == "Content") {
 
                //Packager.Load();
 
                Tracker.UpdateLayout();
            } else if (e.PropertyName == CardContentView.PaddingProperty.PropertyName) {
				SetContentPadding ((int)TheView.Padding.Left, (int)TheView.Padding.Top,
					(int)TheView.Padding.Right, (int)TheView.Padding.Bottom);
			} else if (e.PropertyName == CardContentView.CornerRadiusProperty.PropertyName) {
				this.Radius = TheView.CornderRadius;
			} else if (e.PropertyName == CardContentView.BackgroundColorProperty.PropertyName) {
				if(TheView.BackgroundColor != null)
				SetCardBackgroundColor (TheView.BackgroundColor.ToAndroid ());
 
			}
		}
 
		public SizeRequest GetDesiredSize (int widthConstraint, int heightConstraint)
		{
			packed.Measure (widthConstraint, heightConstraint);
 
			//Measure child here and determine size
			return new SizeRequest (new Size (packed.MeasuredWidth, packed.MeasuredHeight));
		}
 
		public void UpdateLayout ()
		{
			if (Tracker == null)
				return;
 
			Tracker.UpdateLayout ();
		}
 
		public VisualElementTracker Tracker {
			get;
			private set;
		}
 
        public VisualElementPackager Packager
        {
            get;
            private set;
        }
 
        public Android.Views.ViewGroup ViewGroup {
			get{ return this; }
		}
 
		public VisualElement Element {
			get;
			private set;
		}
 
 
    }
}

You can browse the complete code with a working example on GitHub, https://github.com/dhindrik/Xamarin.Forms-Awesome-Controls
The repository is a fork from James Montemagno’s repository for awesome Xamarin.Forms controls. What I have done is that I have changed a few things so can have layouts as content, a StackLayout for example.

Get started with Xamarin.Forms for Windows

Xamarin has finally released support for Windows apps using WinRT. This make it possible to write apps for Windows Phone 8.1 (the stable release uses Windows Phone 8 Silverlight but can be upgraded to Windows Phone 8.1 Silverlight by changing target platform) and Windows 8.1. It’s still in preview and Xamarin not recommending that we uses it for production apps yet. This post will show you how to add Windows apps to your existing Xamarin.Forms project.

First thing to do is to make sure that you have the correct PCL profile for your shared Xamarin.Forms project. If you have created your project from Xamarin.Forms PCL template you have to check Windows Phone 8.1.

PCL Target type

Next step is to create a Blank Windows App.

Create black Windows app

When you have done that you have to add the nuget package for Xamarin.Forms to the Windows project.

Run Install-Package Xamarin.Forms.Windows -Pre in the “Package Manager Console”.

When the package is installed open up MainPage.xaml and add the Xamarin.Forms namespace and change Page to forms:WindowsPage.

<forms:WindowsPage
    x:Class="MyApp.WinStore.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyApp.WinStore"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:forms="using:Xamarin.Forms.Platform.WinRT"
    mc:Ignorable="d">
 
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
 
    </Grid>
</forms:WindowsPage>

You also have to remove that MainPage is inherits from Page in MainPage.xaml.cs. To start the forms application run LoadApplication with the App class from the shared project as an argument.

 public sealed partial class MainPage
    {
        public MainPage()
        {
            this.InitializeComponent();
 
            LoadApplication(new MyApp.App());
        }
    }

The last step is to call the Xamarin.Forms Init method (Xamarin.Forms.Forms.Init(e);) in App.xaml.cs. The call should be in the OnLaunched method after before if (e.PreviousExecutionState == ApplicationExecutionState.Terminated). It will be around line 65.

if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();
                // Set the default language
                rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];
 
                rootFrame.NavigationFailed += OnNavigationFailed;
 
                Xamarin.Forms.Forms.Init(e);
 
                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    //TODO: Load state from previously suspended application
                }
 
                // Place the frame in the current Window
                Window.Current.Content = rootFrame;

Xamarin Forms for WinRT

If you want to use Xamarin.Forms for a Windows Phone 8.1 (with WinRT) app follow the same steps, for a Windows Phone 8.1 project.

Xamarin.Forms: Placeholder text color password box Windows Phone

When using the Entry control in Xamarin.Forms with IsPassword set to true the text color property will not affect placeholder text on Windows Phone. To solve this you need to create a custom renderer for entry in your Windows Phone project.

The native control will be a Grid that contains a PasswordBox (PhoneTextBox if IsPassword is false). The easiest way to find the PasswordBox is to loop through all controls in the Grid and set Foreground color to the text color specified on the Xamarin.Forms control.

[assembly: ExportRenderer(typeof(Entry), typeof(ImprovedEntryRenderer))]
namespace MyApp.WinPhone.Renderers
{
      public class ImprovedEntryViewRenderer : EntryRenderer
      {
            protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                 base.OnElementPropertyChanged(sender, e);
 
                 if(e.PropertyName == "Renderer" || e.PropertyName == "TextColor")
                 {
                      foreach(var ctrl in Control)
                      {
                           var control = (System.Windows.Controls.Control)ctrl;
                           control.Foreground = new SolidColorBrush(System.Windows.Media.Color.FromArgb(byte.Parse(TextColor.A), byte.Parse(TextColor.R), byte.Parse(TextColor.G), byte.Parse(TextColor.B)));
                      }
                 }
            }
      }
}

You have to run the code both if e.PropertyName is “Renderer” and if it’s “TextColor”. That’s because first time the control is rendered it will use “Renderer” as property name, if text color then is changes it will be used as property name.

You have to set the foreground in OnElementPropertyChanged, if you set it in OnElementChanged it will be changed by the base class. If you set it in OnElementPropertyChanged after you have called the base method it will be the last that is running in the renderer.

Preserve data when deploying Xamarin.Android app

By default all your data from your previous runs is deleted when you’re deploying an Xamarin.Android app. In many cases you don’t want the data to be deleted.

Visual Studio
To preserve data go to Tools -> Options -> Xamarin -> Android Settings and check “Preserve application data/cache on device between deploys”.Visual Studio

Xamarin Studio
To preserve data go to Tools -> Options -> Android and check “Preserve data/cache between application deploys”.
Xamarin Studio

Building a ScreenshotManager to capture the screen with code

This blog post will show you how to build a screenshot manager to use from shared code when building apps with Xamarin for Windows Phone, Android and iOS.

First of all we need to create an interface in the shared code library. In this example there will be one method in the screenshot manager. This method will capture the screen and return a byte array that you for example can save to a server.

 public interface IScreenshotManager
 {
        Task<byte[]> CaptureAsync();
 }

All platforms that we will target capture the screen in different ways. Therefor will each platform have a own implementation of the interface. If you register the interface with a platform specific implementation for each platform you can call the ScreenshotManager from shared code.

Windows Phone
For Windows Phone you need to render the root frame into a WritableBitmap.

public class ScreenshotManager : IScreenshotManager
    {
        public async Task<byte[]> CaptureAsync()
        {
            var rootFrame = Application.Current.RootVisual as PhoneApplicationFrame;
 
            var screenImage = new WriteableBitmap((int)rootFrame.ActualWidth, (int)rootFrame.ActualHeight);
            screenImage.Render(rootFrame, new MatrixTransform());
            screenImage.Invalidate();
 
            using (var stream = new MemoryStream())
            {
                screenImage.SaveJpeg(stream, screenImage.PixelWidth, screenImage.PixelHeight, 0, 100);
                var bytes = stream.ToArray();
                return bytes;
            }
        }
    }

iOS
For iOS you need the view from the RootViewController to create a screenshot from. This code will work for iOS7 and above.

public class ScreenshotManager : IScreenshotManager
    {
        public async System.Threading.Tasks.Task<byte[]> CaptureAsync()
        {
            var view = UIApplication.SharedApplication.KeyWindow.RootViewController.View;
 
            UIGraphics.BeginImageContext(view.Frame.Size);
            view.DrawViewHierarchy(view.Frame, true);
            var image = UIGraphics.GetImageFromCurrentImageContext();
            UIGraphics.EndImageContext();
 
            using(var imageData = image.AsPNG())
            {
                var bytes = new byte[imageData.Length];
                System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, bytes, 0, Convert.ToInt32(imageData.Length));
                return bytes;
            }
 
        }
    }

Android
To create a screenshot in Android you need to have access to a activity. The easiest way to access a activity is to create a static method on the ScreenshotManager that you setting before using the ScreenshotManager.

public class ScreenshotManager : IScreenshotManager
    {
        public static Activity Activity { get; set; }
 
        public async System.Threading.Tasks.Task<byte[]> CaptureAsync()
        {
            if(Activity == null)
            {
                throw new Exception("You have to set ScreenshotManager.Activity in your Android project");
            }
 
            var view = Activity.Window.DecorView;
            view.DrawingCacheEnabled = true;
 
            Bitmap bitmap = view.GetDrawingCache(true);
 
            byte[] bitmapData;
 
            using (var stream = new MemoryStream())
            {
                bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
                bitmapData = stream.ToArray();
            }
 
            return bitmapData;
        }
    }

Using Context Actions in Xamarin.Forms ListView

One of the news in Xamarin.Forms 1.3 is what Xamarin call context actions. With context actions you can make a ListView much more richer. What you can do is to add a menu to every row in the ListView. On Windows Phone and Android is the menu showing when you’re long pressing on a item, on iOS you will swipe left on the item to show the menu. As you can see on the screenshots below the menu looks different on each platform. On Windows Phone the menu will pop up, on iOS it will be be on the row and on Android at the top of the screen.

contextactions contextactions_android contextactions_ios

To use context action add them to the ViewCell in the ItemTemplate as shown in the code below.

 <ViewCell.ContextActions>
       <MenuItem Text="Share" />
       <MenuItem Text="Buy" Command="{Binding AddToBasket}" />
</ViewCell.ContextActions>

MenuItem has four properties that is good to know:
Text: What’s shown to the user
Clicked: Adds a event listener to the menu item.
Command: Bind a command to the menu item.
IsDestructive: Will make the menu item red on iOS if it’s set to true.

 <ListView ItemsSource="{Binding Products}" VerticalOptions="Start" RowHeight="40">
 
      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <ViewCell.ContextActions>
              <MenuItem Text="Share" />
              <MenuItem Text="Buy" Command="{Binding AddToBasket}" />
            </ViewCell.ContextActions>
 
            <ContentView Padding="0,10">
              <Grid>
                <Grid.ColumnDefinitions>
                  <ColumnDefinition Width="2*" />
                  <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
 
              <Label Text="{Binding Product.Name}" TextColor="Black" />
              <Label Grid.Column="1" HorizontalOptions="End" Text="{Binding Product.Price, StringFormat='{0:0.00} kr'}" TextColor="Red" />
 
              </Grid>
            </ContentView>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>

Grouping a ListView – Xamarin.Forms

In many cases when you want a list of items you want to group them in the list. To use groups in a ListView with Xamarin.Forms is very easy.

Below are screenshots from Android, iOS and Windows Phone where there is no HeaderTemplate used.
android_groups ios_groups wp_groups

First step is to create a group class that inherits from ObservableCollection and create two properties, one for title and one for short title.

public class ListViewModel
{
     ObservableCollection<MyListViewGroup> Groups {get; set;}
}
 
public class MyListViewGroup : ObservableCollection<MyListViewItem>
{
     public string Title {get; set;}
     public string ShortTitle {get; set;}
}
 
public class MyListViewItem
{
     public string Name {get; set;}
}

IsGroupingEnabled is the keyword to enable grouping for the ListView, if it’s value is true the ListView will be grouped.

GroupTitle is the group title.

GroupShortTitle is the value that will be shown when you tap a group title in Windows Phone and the value at the left side on iOS. Android will not use GroupShortTitle. It will be used to jump a specific group.

To style the header use the GroupHeaderTemplate property.

 <ListView Grid.Row="1" ItemsSource="{Binding Groups}" 
           GroupShortNameBinding="{Binding ShortTitle}" IsGroupingEnabled="true" 
           GroupDisplayBinding="{ Binding Title }">
            <ListView.GroupHeaderTemplate>
              <DataTemplate>
                <ViewCell>
                  <ViewCell.View>
                    <Label Text="{Binding Title}" />
                  </ViewCell.View>
                </ViewCell>
              </DataTemplate>
            </ListView.GroupHeaderTemplate>
            <ListView.ItemTemplate>
              <DataTemplate>
                   <TextCell Text="{Binding Name}" />
              </DataTemplate>
            </ListView.ItemTemplate>
</ListView>

Monitor an app with Xamarin Insights

I Think it’s really important to monitor apps. Of course the most important part is to log errors in the app. But I also like to track how my users uses my apps. For the lastest app I have built I have used a monitoring tool from Xamarin called Insights, http://xamarin.com/insights.

While I alwas have crossplatform and testability in mind when I buildning apps I first create a logger interface.

public interface ILogger
{
     Task InitializeAsync();
 
    //Log exceptions
     void LogException(Exception ex);
 
     //Log information about events, for example view navigation
     Task TrackAsync(string identifier, Dictionary<string, string> details = null);
 
     //Log information about a user
     Task Identify(string userId, Dictionary<string, string> details = null);
}

While Xamarin Insights is compatible with Portable Class Library I writing the implementation in a project that I can share between platforms.

public class InsightsLogger : ILogger
{
     public virtual Task InitializeAsync()
     {
     }
 
     public void LogException(Exception ex)
     {
           Insights.Report(ex, ReportSeverity.Error);
     }
 
     public Task TrackAsync(string identifier, Dictionary<string, string> details = null)
     {
           Insights.Track(identifier, details);
     }
 
     public Task Identify(string userId, Dictionary<string, string> details = null)
     {
          Insights.Identify(userId, details);
     }
}

The InitializeAsync method is virtual so it can be overridden, when using Xamarin Insights each platform has to have the Initialize code. Because of that I also need to create a logger class for each platform.

While I don’t want errors from development together with production data I use to create two apps in Insights, one for development and one for production. I using conditional compile for to initialize with the right app key.

 public class WinPhoneInsightsLogger : Logging.InsightsLogger
    {
        public override async Task Initialize()
        {
#if DEBUG
            Insights.Initialize("key for debug use");
#else
            Insights.Initialize("key for production use");
#endif
        }
    }

if you want to read more about Xamarin Insights you can do it here, http://xamarin.com/insights