Corrado's Blog 2.0

Online thoughts of a technology funatic

Application wide resources (reloaded)

Back in September 2014 I blogged about adding application wide resources to Xamarin Forms applications, that technique is no longer needed since starting from v 1.3 Xamarin added support to app wide resources into Xamarin Forms core.

So what it this post about? It is about adding (and using) them in a more user-friendly way.

Since v 1.3 you can create a resource that can be reachable from all pages this way:

public class App : Application
    {
        public App()
        {
            this.Resources = new ResourceDictionary
            {
                {"TextColor", Color.FromHex("00FF00")}
            };

            // The root page of your application
            this.MainPage = new MainView();
        }
    }

And use the resource from a XAML page this way:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="App1.MainView">
    <Label Text="Hello from Xamarin Forms" 
             VerticalOptions="Center" 
             HorizontalOptions="Center" 
             TextColor="{StaticResource TextColor}" />
</ContentPage>

Ok, It works but is not very friendly, resource name is not listed in XAML Intellisense (powered by Resharper of course) and as XAML lover I’d like to define my application resources in XAML the same way I do in a page.

Luckily you can and it’s very easy:

1- Delete App.cs file

2-Add a new Forms XAML Page and name it App.cs

image

3-Make App.cs inherit from Application instead of ContentPage

public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
        }
    }

4-Replace the generated XAML with following one, don’t forget to substitute [YourNamespaceHere] with your own’s application namespace

<Application xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="[YourNamespaceHere].App">
    <Application.Resources>
        <ResourceDictionary>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Your project should now look like this:

image

and you can add your resources inside ResourceDictionary as in following example:

<Application xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="[YourNamespaceHere].App">
    <Application.Resources>
        <ResourceDictionary>
            <Color x:Key="TextColor">Red</Color>
        </ResourceDictionary>
    </Application.Resources>
</Application>

And now, your resource will work as expected but it will also be listed by Intellisense

image

Definitively more friendly than using code (IMHO)

Hope to see this feature added to Xamarin Forms template soon.

Visual Studio 2013: Fixing NuGet package install error in Xamarin Android projects

Recently, quite often indeed, I’m facing an issue trying to add some NuGet packages to a Xamarin Android project, in this particular case I was trying to add Xamarin.Android.Support.RecyclerView package.

image

The error I get after clicking Install is:

image

Some search on the internet provided me some solutions, the one working for me is changing Android version option from:

image

to an explicit value (API 21 in my case)

image

after this change, all packages get installed properly.

Behaviors for Xamarin Forms v2.0

I’ve pushed version 2.0 of my behavior implementation for Xamarin Forms and moved it to a new location on GitHub: https://github.com/corradocavalli/Corcav.Behaviors

The reason for this is that, in order to prevent conflict with Xamarin API I’ve renamed the root namespace so upgrading to this version will break your code and I’m really sorry for this, but I’m sure it won’t take too long to fix it.

I’ve also removed old nuget package and replaced with this one : ’https://www.nuget.org/packages/Corcav.Behaviors/

Version 2.0 uses Xamarin Forms 1.3.4 and Unified API project as iOS demo.

Sorry for breaking your code, my fault using Xamarin as assembly name Sad smile (lesson learned), honestly didn’t expect such success, and I thank you for that.

Get device unique identifier in Xamarin Forms

Last week I introduced some ISV to the wonderful world of Xamarin and while discussing Xamarin Forms a few of them asked how to get an identifier that uniquely identifies a device.

I had the same issue in the past with a Windows Phone application so I know how retrieve it on this platform and I remember speaking with the guy that was working on the same app for iOS that he told me that Apple changed the policy so the API that was used in the past was no longer available and he had to find a workaround, more on this later.

The answer to the original question is quite easy from the architectural side: Just use a Dependency Service and you’re done, here are the steps:

Let’s create a IDevice interface in the common/shared project and let’s add a GetIdentifier method

public interface IDevice
 {
  string GetIdentifier();  
 }

Once done you can easily get device unique identifier within shared code using this snippet:

IDevice device = DependencyService.Get<IDevice>();
string deviceIdentifier = device.GetIdentifier();

Let’s now implement device specific code, starting from Windows Phone, add this class to Windows Phone project and add ID_CAP_IDENTITY_DEVICE capability.

[assembly: Xamarin.Forms.Dependency(typeof(WinPhoneDevice))]
namespace XFUniqueIdentifier.WinPhone
{
 public class WinPhoneDevice : IDevice
 {
  public string GetIdentifier()
  {
   byte[] myDeviceId = (byte[])Microsoft.Phone.Info.DeviceExtendedProperties.GetValue("DeviceUniqueId");
   return Convert.ToBase64String(myDeviceId);
  }
 }
}

Now add following class to Android project:

[assembly: Xamarin.Forms.Dependency(typeof(AndroidDevice))]
namespace XFUniqueIdentifier.Droid
{
 public class AndroidDevice : IDevice
 {
  public string GetIdentifier()
  {
   return Settings.Secure.GetString(Forms.Context.ContentResolver, Settings.Secure.AndroidId);
  }
 }
}

And now the hardest part: iOS developer used to get device identifier using Device UniqueIdentifier api but this has deprecated by Apple so an alternative has to be found.

After some investigations (also considered generating unique id at startup but this would fail if app is reinstalled) I found this post on Xamarin forums so kudos goes to the original author.

It’s now time to add a new class to iOS project with following code:

[assembly: Xamarin.Forms.Dependency(typeof(IOSDevice))]
namespace XFUniqueIdentifier.iOS
{
 using System.IO;
 using System.Runtime.InteropServices;

 public class IOSDevice : IDevice
 {
  [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
  private static extern uint IOServiceGetMatchingService(uint masterPort, IntPtr matching);

  [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
  private static extern IntPtr IOServiceMatching(string s);

  [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
  private static extern IntPtr IORegistryEntryCreateCFProperty(uint entry, IntPtr key, IntPtr allocator, uint options);

  [DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
  private static extern int IOObjectRelease(uint o);

  public string GetIdentifier()
  {
   string serial = string.Empty;
   uint platformExpert = IOServiceGetMatchingService(0, IOServiceMatching("IOPlatformExpertDevice"));
   if (platformExpert != 0)
   {
    NSString key = (NSString) "IOPlatformSerialNumber";
    IntPtr serialNumber = IORegistryEntryCreateCFProperty(platformExpert, key.Handle, IntPtr.Zero, 0);
    if (serialNumber != IntPtr.Zero)
    {
     serial = NSString.FromHandle(serialNumber);
    }

    IOObjectRelease(platformExpert);
   }

   return serial;
  }  
}

And you’re done, have fun and make a good use of device unique id. Smile

Xamarin build server is too old?

This morning, no idea why, trying to compile a Xamarin.iOS app resulted in an error “Xamarin build server is tool old…” apparently without any reason since nothing has changed on my machine during the weekend. Tried also connecting to a different Mac machine but error was still there while compiling using Xamarin Studio on the Mac machine was ok.
After some investigation I’ve found the solution: looks like that “someone” changed my proxy settings (probably Fiddler) and that was causing the weird message.

Turning it off did the trick.

image

Hope this will help you in case you see this weird message too.

Declarative event handlers in Xamarin Android

In Android applications the UI components are totally isolated from associated activity so to ‘glue’ them you need to follow a ‘javascript like’ approach: find the element fist  and then interact with it. Let’s say you have a .axml file containing a button with id MyButton

 <Button
        android:id="@+id/MyButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/Hello" />

in the associated activity, to subscribe its Click event you have to write this code:

public class MainActivity : Activity
 {
  int count = 1;

  protected override void OnCreate(Bundle bundle)
  {
   base.OnCreate(bundle);

   SetContentView(Resource.Layout.Main);
   Button button = FindViewById<Button>(Resource.Id.MyButton);
   button.Click += button_Click;
  }

  void button_Click(object sender, EventArgs e)
  {
   (sender as Button).Text = string.Format("{0} clicks!", count++);
  }
 }

If you, like me, come from XAML and love the declarative approach I’ve got a good new for you, there’s an alternative, just follow this steps:

1-Add a reference to Mono.Android.Export library

2-Declare you handler in .axml file this way:

<Button
        android:id="@+id/MyButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/Hello"
    android:onClick="myHandler"/>

3-Refactor the code in associated activity this way:

public class MainActivity : Activity
 {
  int count = 1;

  protected override void OnCreate(Bundle bundle)
  {
   base.OnCreate(bundle);

   SetContentView(Resource.Layout.Main);
  }

  [Java.Interop.Export("myHandler")]
  public void button_Click(View v)
  {
   (v as Button).Text = string.Format("{0} clicks!", count++);
  }
 }

And you’re done!

As you see the trick is to export the function, using the Java.Interop.Export attribute that also allows you to use a export the method with a different name, leave it empty if you don’t need to differentiate.

Lot simpler, and cleaner… IMHO Smile

TranslateTo issue with Xamarin Forms 1.2.3

This Xamarin Forms snippets shifts a button left when clicked and restores it back when dialog is dismissed.

public static Page CreateMainPage3()
  {
   StackLayout container = new StackLayout() { Orientation = StackOrientation.Vertical };
   Button btn = new Button() { Text = "Ok" };
   btn.Clicked += async (s, e) =>
   {
    await btn.TranslateTo(-btn.Width, 0, 500);
    await MainPage.DisplayAlert("Test", "Hello", "Ok", "Cancel");
    await btn.TranslateTo(0, 0, 500);
   };

   container.Children.Add(btn);
   
   return MainPage = new ContentPage()
   {
    Content = container,
    Padding = new Thickness(10, 20, 10, 20)
   };
  }

Easy right? unfortunately it doesn’t work on Android (it is ok on Windows Phone and iOS) with Xamarin Forms version 1.2.3

The workaround is quite easy: use LayoutTo instead of TranslateTo, following code works on all platforms.

...
btn.Clicked += async (s, e) =>
   {
    Rectangle defaultRect = btn.Bounds;
    await btn.LayoutTo(new Rectangle(-btn.Width, btn.Y, btn.Width, btn.Height), 500);
    await MainPage.DisplayAlert("Test", "Hello", "Ok", "Cancel");
    await btn.LayoutTo(defaultRect, 500);
   };
 ...

UserControls in Xamarin Forms

While Xamarin Forms have a rich set of controls (a.k.a. Views in Xamarin documentation) sometimes you need to create something that is an aggregate of existing controls plus custom logic. For this requirement the Windows platform provides you a UserControl that is a surface where you can drag the controls that make up the UI and write related logic within it.
This seems to lack on Xamarin Forms at the moment.

Well, that’s not true, since you can easily create UserControls your own in Xamarin Forms, let’s see how with an example (see below)

image image

Lat’s say I need to populate a list of Entries whose content has to be validated and if not valid a feedback has to be provided to the user. Since we don’t like to repeat ourselves and since this might be a requirement we might also have in future I decided to create a custom control that contains all the validation logic and exposes a set of properties that let me customize the runtime behavior of the control.

Here’s the MainView’s XAML of the previous screenshots:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
      xmlns:c="clr-namespace:UserControl;assembly=UserControl"
        x:Class="UserControl.MainView">

 <StackLayout Padding="10" >
  <Label Text="UserControls in action!" x:Name="MyLabel" Font="Bold,Large"  />
  <c:SmartEntry Header="Enter Username (min 3, max 8 chars)"
         ValidMode="MinMax"
         Min="3"
         Max="8"
         InvalidText="Sorry, username is not valid"
         Text="{Binding Username, Mode=TwoWay}"
         />
  <c:SmartEntry Header="Enter Email"
         ValidMode="Email"
         InvalidText="Whoops! invalid email..."
         Text="{Binding Email, Mode=TwoWay}"
         />

  <Label Font="Large" Text="{Binding Info}" TextColor="Green" />

 </StackLayout>
</ContentPage>

I’m sure you noted the SmartEntry component that is our UserControl, let’s see how I created it.

Everything starts from a UserControl class that inherits from Frame

namespace UserControl
{
 using Xamarin.Forms;

 public class UserControl : Frame
 {
  private Dictionary<string, object> cache = new Dictionary<string, object>();

  public UserControl()
  {
   this.Padding = new Thickness(0, 0, 0, 0);
  }

  protected T FindByViewPrivate<T>(string name)
  {
   if (this.cache.ContainsKey(name))
   {
    return (T)this.cache[name];
   }

   Type t = this.GetType();
   FieldInfo fi = t.GetRuntimeFields().FirstOrDefault(f => f.Name == name);
   if (fi == null) throw new NullReferenceException(string.Format("Field {0} not found.", name));
   T value = (T)fi.GetValue(this);
   this.cache.Add(name, value);
   return value;
  }
 }
}

This class just resets default padding added by Frame so that content fills the container and a FindByViewPrivate<T> method that gives us access to private UI element instances created by LoadFromXaml method when it parses the XAML.

now let’s see the “real” UserControl, XAML first:

<?xml version="1.0" encoding="utf-8" ?>
<c:UserControl xmlns="http://xamarin.com/schemas/2014/forms"
     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
     xmlns:c="clr-namespace:UserControl;assembly=UserControl"
     x:Class="UserControl.SmartEntry">
 <StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
  <StackLayout Orientation="Horizontal">
   <Label x:Name="HeaderLabel" HorizontalOptions="Start"  />
   <Frame Padding="10,0,0,0">
    <Label TextColor="Red" x:Name="InvalidTextLabel" IsVisible="false"  />
   </Frame>
  </StackLayout>
  <Entry x:Name="TextEntry" />
 </StackLayout>
</c:UserControl>

then code-behind (I’ve removed Validate() method code for brevity, get the full code from the link at the end of this post)

namespace UserControl
{
 public enum ValidationModes
 {
  Email,
  MinMax
 }

 public partial class SmartEntry : UserControl
 {
  public SmartEntry()
  {
   InitializeComponent();

   TextEntry.TextChanged += (s, e) =>
   {
    bool isTextValid = this.Validate(e.NewTextValue);
    InvalidTextLabel.IsVisible = !isTextValid;
    if (isTextValid) this.Text = e.NewTextValue;
   };
  }

  public ValidationModes ValidMode { get; set; }

  public int Min { get; set; }
  public int Max { get; set; }

  public string InvalidText
  {
   get { return InvalidTextLabel.Text; }
   set { InvalidTextLabel.Text = value; }
  }

  public string Header
  {
   get { return HeaderLabel.Text; }
   set { HeaderLabel.Text = value; }
  }

  public static readonly BindableProperty TextProperty =
    BindableProperty.Create<SmartEntry, string>(
    p => p.Text, null, propertyChanged: OnTextChanged);

  private static void OnTextChanged(BindableObject bindable, string oldvalue, string newvalue)
  {
   SmartEntry origin = (SmartEntry)bindable;
   Entry textBox = origin.FindByViewPrivate<Entry>("TextEntry");
   textBox.Text = newvalue;
  }

  public string Text
  {
   get { return (string)GetValue(TextProperty); }
   set { SetValue(TextProperty, value); }
  }
 }
}

I’ve added some simple public properties: Min,Max,Header,InvalidText and a BindableProperty Text so that we can bind it to our ViewModel for further processing.

As you see, inside OnTextChanged I use FindByViewPrivate<Entry> to retrieve the proper control instance from the static method.

All properties are used inside MainView’s XAML to customize each UserControl differently.

If you want to see the example in action download the code here

Have fun Winking smile

Force assembly linking in Xamarin.iOS projects

I’ve updated Xamarin.Forms.Behaviors package to v 1.2 to fix an issue reported by alexandremblah (thank you!) that was preventing the package to work on iOS, the issue was that Xamarin.Behaviors.dll couldn’t be loaded.

image

After a few tests I’ve used JetBrains DotPeek to see what gets deployed to emulator

image

and, as you see, Xamarin.Behaviors dll is missing, that’s the cause of the exception.

In iOS project there’s an option that might solve the problem:

image

but unfortunately changing Linker option to “Link all assemblies” didn’t fix, probably because all Behavior stuff is handled in XAML which is loaded at runtime, so there is no code using Behaviors types.

The only option I’ve found to force iOS linker to deploy Behaviors library is to include some code on iOS project that interacts with some entities in the library, so I’ve added an Infrastructure static class that exposes a Init method that does practically nothing.

public static class Infrastructure
 {
  private static DateTime initDate;
  public static void Init()
  {
   initDate = DateTime.UtcNow;
  }
 }

And inside iOS project you have to add a call to Infrastructure’s Init method.

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
  {
   Xamarin.Forms.Forms.Init();

   //Added to prevent iOS linker to strip behaviors assembly out of deployed package.
   Xamarin.Behaviors.Infrastructure.Init();

   window = new UIWindow(UIScreen.MainScreen.Bounds);

   window.RootViewController = App.GetMainPage().CreateViewController();

   window.MakeKeyAndVisible();

   return true;
  }

with this magic trick assemblies gets included and behaviors work fine:

image

Android and Windows Phone projects don’t require this method invocation.

Enjoy and thank for your feedback.

ToolbarItems in Xamarin Forms

A interesting Xamarin Forms feature well hidden inside documentation is that Page class exposes a collection of ToolbarItems. a ToolbarItem is an element that renders a “menu” on each platform letting you add elements like Menu on Android/iOS or ApplicationBar/MenuItems in Windows Phone.

To use Toolbaitems just create a new Xamarin Forms solutions and update all Xamarin related packages to latest version (there are several fixes related to Toolbaritems since v.1.0) then create a new MainView.xaml page (you can use code if you prefer, but I like XAML…) and add a couple of ToolBarItems to Page’s ToolbarItems collection:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        x:Class="ToolbarItems.Views.MainView">
 <ContentPage.ToolbarItems>
  <ToolbarItem Name="Menu1" Activated="OnClick"  Order="Primary" Priority="0" />
  <ToolbarItem Name="Menu2" Activated="OnClick"  Order="Primary" Priority="1" />
 </ContentPage.ToolbarItems>

</ContentPage>

and this is code-behind:

void OnClick(object sender, EventArgs e)
  {
   ToolbarItem tbi = (ToolbarItem) sender;
   this.DisplayAlert("Selected!", tbi.Name,"OK");
  }

Change the startup page embedding MainView.xaml in a Navigation Page (this step is required in order to have menus available on Android platform)

namespace ToolbarItems
{
 public class App
 {
  public static Page GetMainPage()
  {
   return new NavigationPage(new MainView());
  }
 }
}

Here’s what you get on different platforms:

imageimageimage

please note how menus in iOS are in opposite direction compared to Android and Windows Phone, and how Windows Phone obviously miss the appropriate icons.

Let’s fix both issues with a little help from OnPlatform (used both resources and Property elements to cover both usage scenarios)

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        x:Class="ToolbarItems.Views.MainView">
 <ContentPage.Resources>
  <ResourceDictionary>
   <OnPlatform x:Key="Priority1" x:TypeArguments="x:Int32"
      iOS="0"
      Android="1"
      WinPhone="1" />
   <OnPlatform x:Key="Priority2" x:TypeArguments="x:Int32"
      iOS="1"
      Android="0"
      WinPhone="0" />
  </ResourceDictionary>
 </ContentPage.Resources>

 <ContentPage.ToolbarItems>
  <ToolbarItem Name="Menu1" Activated="OnClick" Order="Primary" Priority="{StaticResource Priority1}"  >
   <ToolbarItem.Icon>
    <OnPlatform x:TypeArguments="FileImageSource"
      WinPhone="Toolkit.Content/ApplicationBar.Add.png" />
   </ToolbarItem.Icon>
  </ToolbarItem>
  <ToolbarItem Name="Menu2" Activated="OnClick" Order="Primary" Priority="{StaticResource Priority2}" >
   <ToolbarItem.Icon>
    <OnPlatform x:TypeArguments="FileImageSource"
     WinPhone="Toolkit.Content/ApplicationBar.Check.png" />
   </ToolbarItem.Icon>
  </ToolbarItem>
 </ContentPage.ToolbarItems>
</ContentPage>

Of course you can use Icon property on each platform (see Android sample below)

image

The role of Order property depends on platform, on Windows Phone using Secondary  creates MenuItems, on Android it adds entries to Page menu and on iOS render menu as horizontally aligned buttons.

Android

image

Windows Phone

image

iOS

image

I took a while for me to know about this feature, glad I’ve found it.

Older Posts »