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

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.

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

Crossplatform and storage strategies

Many applications need to store data related to the application. If you're building applications for multiple platforms the APIs for storage is different.

Following five storage types is the most common ones:

Local storage
Storage to store application data locally on the device.

Remote storage
Storage to store application data in the cloud, for example OneDrive or iCloud. Data that is saved in the cloud could be accessible from different devices.

Cache storage
Storage to use when caching data. Platforms that have a separate storage for cache data will not contain cache storage in the backup, WinRT for an example. Cached data is not necessary to have in a backup, it will be cached again after restore of the backup.

Settings storage
Storage to store application settings locally on the device.

Remote settings storage
Storage to store application settings in the cloud, for example OneDrive or iCloud. Settings will be synced for all devices you have installed the application on.

You can handle storage in different ways, one strategy is to create one interface for each of the storage types above, for example ILocalStorage and IRemoteStorage. Every interface will have an implementation for each platform. With help of an IoC-container (Inversion of Control) we can use the interface to call platform specific code from shared code.

public interface ILocalStorage
{
     Task AddAsync(string key, object value);
     Task<T> GetAsync(string key, object value);
     Task<bool> ContainsAsync(string key);
     Task RemoveAsync(string key);
}

One other strategy is to create one storage interface, IStorage and have a method with storage type as an argument. As with the first strategy each platform will have a platform specific implementation that can be used from shared code with help of IoC.

public enum StorageTypes
{
    Local,
    Remote,
    Cache,
    Settings,
    RemoteSettings
}
 
public interface IStorage
{
     Task AddAsync(string key, object value, StorageTypes storageType);
     Task<T> GetAsync(string key, object value, StorageTypes storageType);
     Task<bool> ContainsAsync(string key, StorageTypes storageType);
     Task RemoveAsync(string key, StorageTypes storageType);
}
 
public class Storage : IStorage
{
     public async Task AddAsync(string key, object value, StorageTypes storageType)
     {
          switch(storageType)
          {
                 case StorageTypes.Local:
                      //code for local storage
                      break;  
                 case StorageTypes.Remote:
                      //code for remote storage
                      break;  
                 case StorageTypes.Cache:
                      //code for cache storage
                      break;  
                 case StorageTypes.Settings:
                      //code for settings storage
                      break;  
                 case StorageTypes.RemoteSettings:
                      //code for remote settings storage
                      break;  
          }     
     }

I prefer the first strategy beacause I like small classes with one role, the maintainability will be much higher and you can make changes to one implementation without the risk of breaking other functionality than that one you want to change.

As an example, one platform maybe don't have CacheStorage, then the LocalStorage and CacheStorage will be pretty much the same. But if that platform will get CacheStorage in the future you can change the implementation of ICacheStorage without touching the working code for LocalStorage.

Extend Label in Xamarin.Forms with a font color property

The label control in Xamarin.Forms does not have a FontColor property. But the control is easy to extend.

First create a new class that inherits from Label.
Create a FontColor property of type string. The value of the FontColor will in this example be an RGBA string.

public class CustomLabel : Label
    {
 
         public static readonly BindableProperty FontColorProperty = 
             BindableProperty.Create<CustomLabel, string>( 
                 p => p.FontColor, ""); 
 
 
         public string FontColor
         { 
             get 
             { 
                 return (string)GetValue(FontColorProperty); 
             } 
             set 
             { 
                 SetValue(FontColorProperty, value); 
             } 
         } 
 
    }

Next step is to write a custom renderer for each platform.

iOS

[assembly: ExportRenderer(typeof(CustomLabel), typeof(CustomLabelRenderer))]
namespace MyApp.iOS.Renderers
{
    public class CustomLabelRenderer : LabelRenderer
    {
 
        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
 
            if(e.PropertyName == "FontColor")
            {
                var view = (CustomLabel)Element;
 
                var values = view.FontColor.Split(',');
 
                Control.TextColor = UIColor.FromRGBA(int.Parse(values[0]), int.Parse(values[1]), int.Parse(values[2]), int.Parse(values[3]));
            }
        }
    }
}

Android

[assembly: ExportRenderer(typeof(CustomLabel), typeof(CustomLabelRenderer))]
namespace MyApp.Android.Renderers
{
    public class CustomLabelRenderer : LabelRenderer
    {
 
        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
 
            if(e.PropertyName == "FontColor")
            {
                var view = (CustomLabel)Element;
 
                var values = view.FontColor.Split(',');
 
                var color = Color.Argb(int.Parse(values[3]),int.Parse(values[0]), int.Parse(values[1]), int.Parse(values[2]));
 
                Control.SetTextColor(color);            
            }
        }
    }
}

Windows Phone

[assembly: ExportRenderer(typeof(CustomLabel), typeof(CustomLabelRenderer))]
namespace MyApp.WinPhone.Renderers
{
    public class CustomLabelRenderer : LabelRenderer
    {
 
        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
 
            if(e.PropertyName == "FontColor")
            {
                var view = (CustomLabel)Element;
 
                var values = view.FontColor.Split(',');
                Control.Foreground = new SolidColorBrush(System.Windows.Media.Color.FromArgb(byte.Parse(values[0]), byte.Parse(values[1]), byte.Parse(values[2]), byte.Parse(values[3])));
            }
        }
    }
}

To use the custom label in XAML you need to declare the namespace.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
					   xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
					   x:Class="MyApp.Views.MyView"
            xmlns:controls="clr-namespace:MyApp.CustomControls;assembly=MyApp">

Then you can use the control i XAML like this:

<controls:CustomLabel FontColor="255,255,255,255" Text="My custom label" />

Xamarin, MvvmCross and ValueConverters

This post will show you how to use ValueConverters in an Android app built with Xamarin. For an introduction to Xamarin and MvvmCross read this blog post.

If you want to convert values before you bind it to a property you can use value converters. For example if you want to view an element if a string property is not null you can use a value converter to convert it to a boolean.

First you will create a new class that inherit from MvxValueConverter and then override the Convert method and write your code there, in this case a simple null check. The class has to name end with ValueConverter.

public class IsNullValueConverter : MvxValueConverter
{
    public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
          return !String.IsNullOrEmpty(value as string);
    } 
}

Then name of the value converter will be the first part of the class name, so my IsNullValueConverter class will be used as IsNull in the Android Xml and the argument will be the property in your view model that you want to use.

<LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        local:MvxBind="Visible IsNull(Name)">

ValueConverters is a very effective and simple way to do convert values without to create a property in the ViewModel for it.