Corrado's Blog 2.0

Online thoughts of a technology funatic

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.

Xamarin.Forms.Behaviors v.1.1.0

I’ve pushed an update to Xamarin.Forms.Behaviors library, fixed some bugs and added Relative Commanding to EventToCommand Behavior.

What is Relative Commanding?

It is a way to let EventToCommand invoke a Command that is exposed by a ViewModel that’s not part of actual BindingContext, typical example is when you are inside an ItemTemplate (ViewCell in Xamarin Forms) and you need to invoke a command that’s hosted inside another viewmodel.

To support this feature I’ve added two new properties to EventToCommand: CommandName and CommandContext, former is the name (string) of the Command you want to invoke, latter is the BindingContext that exposes that command.

How can you link CommandContext with desired BindingContext? Use the new RelativeContext markup extension exposed by Behaviors library to ‘link’ a BindingContext to another (CommandContext in case of EventToCommand)

Here’s a sample taken from GitHub repo.

MainViewModel exposes both an ObservableCollection of Items and a NickSelectedCommand and is also bound to MainPage’s BindingContext, here’s how you can let ListView’s items invoke MainViewModel command.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
      xmlns:b="clr-namespace:Xamarin.Behaviors;assembly=Xamarin.Behaviors"
        x:Class="Xamarin.Behaviors.Demo.Views.MainView"
      x:Name="MainPage"> 

  <ListView ItemsSource="{Binding Items}"
      IsGroupingEnabled="false"
      RowHeight="60">
   <ListView.ItemTemplate>
    <DataTemplate>
     <ViewCell>
      <StackLayout Orientation="Horizontal" Padding="10" >
       <StackLayout Orientation="Vertical"  HorizontalOptions="FillAndExpand" Spacing="-50"  >
        <Button Text="{Binding NickName}">
         <b:Interaction.Behaviors>
          <b:BehaviorCollection>
           <b:EventToCommand CommandNameContext="{b:RelativeContext MainPage}"
                 EventName="Clicked"
                 CommandName="NickSelectedCommand"
                 CommandParameter="{Binding NickName}" />
          </b:BehaviorCollection>
         </b:Interaction.Behaviors>
        </Button>
        
       </StackLayout>
      </StackLayout>

     </ViewCell>
    </DataTemplate>
   </ListView.ItemTemplate>
  </ListView>
  
  
 </StackLayout>
</ContentPage>

As you see we ‘linked’ CommandNameContext to the BindingContext associated to MainPage element through RelativeContext extension an we told EventToCommand that button’s Clicked event should invoke a Command named “NickSelectedCommad” also note that original BindingContext is preserved so we can safely use binding for the CommandParameter property.

Happy Behavioring… Smile

Application wide resources in Xamarin Forms

Xamarin Form’s XAML infrastructure is very similar to Windows counterpart (and I thank Xamarin for that!) unfortunately in current version a feature is not there yet: Application wide resources that is resources  that are shared among all application pages.

In this blog post I’ll show you how to add this feature to a Xamarin Forms application.

Step 1

Create a new Xamarin Forms application (shared)

image

Step 2

Create a CoreApplication class that inherits from View, this class provides a ResourceDictionary property and can be lately extended with other Application features.

namespace Xamarin.Forms
{
 public class CoreApplication:View
 {
 }
}

Step 3

Create an App class that inherits for CoreApplication that is splitted between XAML and associated code-behind.

<?xml version="1.0" encoding="utf-8" ?>
<CoreApplication xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        x:Class="SharedResources.App">
 <CoreApplication.Resources>
  <ResourceDictionary>
   ... app wide resources here...
  </ResourceDictionary>
 </CoreApplication.Resources> 
</CoreApplication>

code behind:

namespace SharedResources
{
 public partial class App : CoreApplication
 {
  static App current;

  public App()
  {
   InitializeComponent();
  }

  public static App Current
  {
   get
   {
    return current ?? (current = new App());
   }
  }

  public Page GetMainPage()
  {
   return new MainView();   
  }
 }
}

As you see, together with standard XAML initialization call InitializeComponent the call exposes a Current static application returning a singleton App instance and Xamarin Forms standard method GetMainPage that returns application’s main page.

Step 4

Since App class has changed we need to update the code generated by Xamarin Forms template that retrieve application’s main page in each platform, thank to Current property modification is very easy, just a line of code for each supported platform.

Android

namespace SharedResourced.Droid
{
 [Activity(Label = "SharedResourced", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
 public class MainActivity : AndroidActivity
 {
  protected override void OnCreate(Bundle bundle)
  {
   base.OnCreate(bundle);

   Xamarin.Forms.Forms.Init(this, bundle);

   SetPage(SharedResources.App.Current.GetMainPage());
  }
 }
}

iOS

namespace SharedResourced.iOS
{
 [Register("AppDelegate")]
 public partial class AppDelegate : UIApplicationDelegate
 {
  UIWindow window;

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

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

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

   window.MakeKeyAndVisible();

   return true;
  }
 }
}

Windows Phone

namespace SharedResourced.WinPhone
{
 public partial class MainPage : PhoneApplicationPage
 {
  public MainPage()
  {
   InitializeComponent();

   Forms.Init();
   Content = SharedResources.App.Current.GetMainPage().ConvertPageToUIElement(this);
  }
 }
}

Step 6

Since we need to lookup application wide resources in XAML, let’s use one of the less known XAML features: Custom Markup Extension. As in Windows, defining a Custom Markup Extension in Xamarin Forms is just a matter of implementing the IMarkupExtension interface (not sure about requirement of class name ending with *Extension but I’ll follow the convention)

Here’s the code

namespace Extensions
{
 [ContentProperty("Key")]
 public class GlobalResourceExtension : IMarkupExtension
 {
  public string Key { get; set; }
  public object ProvideValue(IServiceProvider serviceProvider)
  {
   if (this.Key == null)
    throw new InvalidOperationException("you must specify a key in {GlobalResource}");
   if (serviceProvider == null)
    throw new ArgumentNullException("serviceProvider");

   object value;
   bool found = App.Current.Resources.TryGetValue(this.Key, out value);
   if (found) return value;
   throw new ArgumentNullException(string.Format("Can't find a global resource for key {0}", this.Key));
  }
 }
}

No rocket science, just a simple lookup into Application’s resource dictionary to get required resource.

Step 7

Time to test our work, let’s add some dummy resources to App.Xaml and consume them in a XAML page.

<CoreApplication xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        x:Class="SharedResources.App">
 <CoreApplication.Resources>
  <ResourceDictionary>
   <LayoutOptions x:Key="hOptions" Alignment="Start" />
   <x:String x:Key="color">Red</x:String>
   <x:String x:Key="color2">#CC00FF00</x:String>
   <OnPlatform x:Key="Angle" x:TypeArguments="x:Double">
    <OnPlatform.iOS>0</OnPlatform.iOS>
    <OnPlatform.Android>-15</OnPlatform.Android>
    <OnPlatform.WinPhone>90</OnPlatform.WinPhone>
   </OnPlatform>
  </ResourceDictionary>
 </CoreApplication.Resources>
</CoreApplication>

MainView.Xaml

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
      xmlns:e="clr-namespace:Extensions;assembly=SharedResourced"
        x:Class="SharedResourced.MainView">
 <StackLayout Spacing="30">
  <Label Text="Hello" 
     VerticalOptions="{e:GlobalResource hOptions}" 
     TextColor="{e:GlobalResource color2}" 
     HorizontalOptions="Center" />

  <Label Text="Hello2" 
     VerticalOptions="{e:GlobalResource hOptions}" 
     Font="Large" 
     TextColor="{e:GlobalResource color}" 
     HorizontalOptions="Center"
     Rotation="{e:GlobalResource Angle}"/>
 </StackLayout>
</ContentPage>

Note the presence of xaml namespace “e” that contains our custom markup extension.

Here you go, just a few steps and now you have Application wide resources instead of repeating them in each page.

Here’s the Windows Phone and Android output (sorry no Mac machine connected at this moment Winking smile)

image  image

Older Posts »