#Xamarin Grid Row and Column Definitions – Simplified!

Xamarin Grids let you layout your controls in, well, a Grid. Rows and Columns. Before Xamarin Forms 4.7 here’s how your layout a 3 by 3 grid:

<Grid>
 <Grid.RowDefinitions>
  <RowDefinition Height="Auto"></RowDefinition>
  <RowDefinition Height="*"></RowDefinition>
  <RowDefinition Height="*"></RowDefinition>
 </Grid.RowDefinitions>

 <Grid.ColumnDefinitions>
  <ColumnDefinition Width="*"></ColumnDefinition>
  <ColumnDefinition Width="*"></ColumnDefinition>
  <ColumnDefinition Width="*"></ColumnDefinition>
 </Grid.ColumnDefinitions>

That’s a lot of typing.

Once you upgrade to Xamarin Forms 4.7 you can then use a short form:

<Grid ColumnDefinitions="*,*,*" RowDefinitions="Auto,*,*">

Thanks Morton for your suggestion to the Xamarin team!

#Xamarin: It’s actually about the money

ca44147d22ccf7fe745b9a59e0054e7c.gifAs developers, we naturally focus on the tech side of tech. Xamarin is a great toolset that allows you to create native, fast apps with a single codebase using the .Net you already know and love.

However, I think the key benefit, the one that gets overlooked in reviews and overviews of the software, is that Xamarin saves your company money. People are expensive so not having to hire an iOS and Android team, and not having to retrain the developers in a language they may not know saves a significant amount of money.

At Maximizer, a long-standing CRM company,  we focus on building great tools, but tools that are affordable as well. The app team which I lead has only 1 group of developers focusing on building a great CRM app. We have one set of Program Managers supporting the single team. There is one build process and one deployment process. No effort is needed to keep the Android and iOS teams in sync as there is only one codebase.

So, glory in the technical wonders of Xamarin, but smile at the money you are saving your company and customers.

I wrote this post after I was surprised as the level of interest in Xamarin and money-saving of one of my recent Twitter posts.

Screenshot 2020-01-21 at 8.27.31 AM.png

 

 

 

#Xamarin Animated #GIFs

In the latest version of Xamarin , version 4.4, Images can now show animated GIFs.

Normally my posts are a bit longer to explain how to implement a new Xamarin feature, but this one is so simple, yet powerful, that only a small post is needed.

Sweet.

The Image tag now has an attribute called IsAnimationPlaying. Set that to true and the source to an animated GIF, either local or remote.

That’s it. Your app can now show Borat.

<Image Source="borat.gif" IsAnimationPlaying="true"></Image>

animation.gif

Xamarin: Using Telerik TabView with Data Trigger Colors

Xamarin TelerikThe Telerik TabView for Xamarin is a great tool to hold and alter the content of several pages. One of the downsides is that the styles offered are a bit limited. I wanted to change the Text color fo the Tab Header Text to be white or off-white depending on the selected state.

No problem as Xamarin as Data Triggers that can change the property of an item depending on the value of data.

Step 1: Add a custom TabViewItem header

<telerikPrimitives:TabViewItem.Header>
	<telerikPrimitives:TabViewHeaderItem>
	   <telerikPrimitives:TabViewHeaderItem.Content>
                <Label Text="SAVED SEARCHES" />
           </telerikPrimitives:TabViewHeaderItem.Content>
	 </telerikPrimitives:TabViewHeaderItem>
</telerikPrimitives:TabViewItem.Header>

In the above, we now have a label for the header. the next step is to add Data Triggers to that Label so that when IsSelected is changed, the Data Trigger will change the Label’s color

Step 2: Attach a Data Trigger to the Label

<Label Text="SAVED SEARCHES">
  <Label.Triggers>
     <DataTrigger TargetType="Label" Binding="{TemplateBinding IsSelected}" Value="True">
	 <Setter Property="TextColor" Value="White"></Setter>
     </DataTrigger>
     <DataTrigger TargetType="Label" Binding="{TemplateBinding IsSelected}" Value="False">
         <Setter Property="TextColor" Value="#aaffffff"></Setter>
     </DataTrigger>
</Label.Triggers>

A Data Trigger is like a sentence. you can read it like this:

WHEN the IsSelected IS true THEN change the TEXTCOLOR to white.

WHEN the IsSelected IS faflseTHEN change the TEXTCOLOR to off-white.

Here’s the result with several tabs:

2019-10-18 11_31_18-iPhone 11 iOS 13.1.png

#Xamarin Forms Currency Entry Field

images.jpgI needed a simple Entry field in Xamarin Forms which would work as a currency field. It should show 0.00 when empty, but then clear out automatically once the user tapped it. I ended up using a simple behavior to accomplish it in a very simply way.

currency.gif

Here’s how:

Create a file called CurrencyBehavior.cs in your shared project:

public class CurrencyBehavior : Behavior<Entry>
    {
	    private bool _hasFormattedOnce = false;
	    protected override void OnAttachedTo(Entry entry)
	    {
		    entry.TextChanged += OnEntryTextChanged;
			entry.Focused += EntryOnFocused;
			entry.Unfocused += EntryOnUnfocused;
		    base.OnAttachedTo(entry);
	    }
 
	    private void EntryOnUnfocused(object senderFocusEventArgs e)
	    {
		    var entry = sender as Entry;
		    if (entry?.Text.HasValues()==false)
		    {
			    entry.Text = "0.00";
		    } 
	    }
 
	    private void EntryOnFocused(object senderFocusEventArgs e)
	    {
		    var entry =  sender as Entry;
		    if (entry?.Text == "0.00")
		    {
			    entry.Text = "";
		    }
	    }
 
	    protected override void OnDetachingFrom(Entry entry)
	    {
		    entry.TextChanged -= OnEntryTextChanged;
		    entry.Focused -= EntryOnFocused;
		    entry.Unfocused -= EntryOnUnfocused;
		    base.OnDetachingFrom(entry);
	    }
 
	    private   void OnEntryTextChanged(object senderTextChangedEventArgs args)
	    {
		    if (!_hasFormattedOnce && args.NewTextValue == "0")
		    {
			    ((Entrysender).Text = "0.00";
			    _hasFormattedOnce = true;
		    }
	    }
 
 
    }

Now, on your Entry control, add the behavior:

	            <Entry 
	                   Text="{Binding MyMoneyPropertyInMyViewModel}"
	                   Keyboard="Numeric">
		            <Entry.Behaviors>
			            <behaviors:CurrencyBehavior />
		            </Entry.Behaviors>
	            </Entry>

 

#Xamarin Forms Shell – How to make it work on iOS 9

images.jpg Xamarin Forms Shell is a great way to reduce your code complexity. It handles a lot of plumbing for you. Highly recommended; I use it in my 50,000 user app.

However, it doesn’t work on iOS 9 as of version 4.1. This article will show you how to get it to work on iOS 9

Credits: Based on discussions from a GitHub forum, code is taken from there so all create to the forum contributors listed there. Thanks! https://github.com/JRPMike

What’s the problem:

Tabs. iOS 9 doesn’t allow certain colorings of tabs (UnselectedItemTintColor), so we need to ensure that we don’t ask for a color to be applied when we are in iOS 9

We are going to add a ShellRenderer so that in iOS we can call the Tab colorings only when we are in iOS 10 and above.

Add a file called MyShellRenderer.cs to your iOS project only.

Then add this:

[assemblyExportRenderer(typeof(Shell), typeof(App.iOS.Renderers.MyShellRenderer))]
namespace App.iOS.Renderers
{

    public class MyShellRenderer : ShellRenderer
    {
 
        protected override void OnElementSet(Shell element)
        {
            base.OnElementSet(element);
        }
 
        protected override IShellTabBarAppearanceTracker CreateTabBarAppearanceTracker()
        {
	        return new MySafeShellTabBarAppearanceTracker();
        }
    
 
        
    }
 
	public class MySafeShellTabBarAppearanceTracker : IShellTabBarAppearanceTracker
	{
		UIColor _defaultBarTint;
		UIColor _defaultTint;
		UIColor _defaultUnselectedTint;
 
		public void ResetAppearance(UITabBarController controller)
		{
			if (_defaultTint == null)
				return;
 
			var tabBar = controller.TabBar;
			tabBar.BarTintColor = _defaultBarTint;
			tabBar.TintColor = _defaultTint;
			tabBar.UnselectedItemTintColor = _defaultUnselectedTint;
		}
 
		public void SetAppearance(UITabBarController controllerShellAppearance appearance)
		{
			IShellAppearanceElement appearanceElement = appearance;
			var backgroundColor = appearanceElement.EffectiveTabBarBackgroundColor;
			var foregroundColor = appearanceElement.EffectiveTabBarForegroundColor// currently unused
			var disabledColor = appearanceElement.EffectiveTabBarDisabledColor// unused on iOS
			var unselectedColor = appearanceElement.EffectiveTabBarUnselectedColor;
			var titleColor = appearanceElement.EffectiveTabBarTitleColor;
 
			var tabBar = controller.TabBar;
			bool operatingSystemHasUnselectedTint = UIDevice.CurrentDevice.CheckSystemVersion(10, 0);
			if (_defaultTint == null)
			{
				_defaultBarTint = tabBar.BarTintColor;
				_defaultTint = tabBar.TintColor;
				if (operatingSystemHasUnselectedTint)
				{
					_defaultUnselectedTint = tabBar.UnselectedItemTintColor;
				}
			}
 
			if (!backgroundColor.IsDefault)	tabBar.BarTintColor = backgroundColor.ToUIColor();
			if (!titleColor.IsDefault)	tabBar.TintColor = titleColor.ToUIColor();
			if (operatingSystemHasUnselectedTint)
			{
				if (!unselectedColor.IsDefault)
					tabBar.UnselectedItemTintColor = unselectedColor.ToUIColor();
			}
		}
 
		public void UpdateLayout(UITabBarController controller)
		{
		}
 
		#region IDisposable Support
 
		protected virtual void Dispose(bool disposing)
		{
		}
 
		public void Dispose()
		{
			Dispose(true);
		}
 
		#endregion
	}
}

 

#Xamarin.Forms: Remote Image Markup Extension

downloadI had a requirement to have an Image get its source from a URL. I didn’t want to hard code it as I wanted a central place to manage them in case they did change in the future.

I decided to write a simple MarkupExtension so that I could pass in an Enum and get a Source for the Image, like this:

<Image Source="{local:RemoteImages Login_Help_Live}" />

First, we create the Enum to indicate which image to use:

public enum EnumRemoteImages
{
    Login_Help_Live ,
    Login_Help_OnPrem,
    Multiple_tab_coachmark ,
    Multiple_swipe_coachmark,
    Notifications_coachmark,
    Todays_Business_coachmark
}

Our MarkupExtension will be used by the Source property on the Image tag, so we indicate it:

[ContentProperty("Source")]
public class RemoteImagesExtension : IMarkupExtension

And offer a reference:

public EnumRemoteImages Source { get; set; }

Then we provide the logic which simply matches an enum with an image:

public object ProvideValue(IServiceProvider serviceProvider)
        {
            ImageSource s = null;
            string url = GetImageUrlFor(Source);
            if (url.IsValidUrl())
                s = new UriImageSource()
                {
                  Uri = new Uri(url)
                };
            
            return s;
        }
 
        public static string GetImageUrlFor(EnumRemoteImages image)
        {
            string url = "https://i.ibb.co/2gcHxdg/Small-Logo.png";
            switch (image)
            {
                case EnumRemoteImages.Login_Help_Live:
                    url = "https://i.ibb.co/JdNPS4Y/login-help-live.png";
                    break;
                case EnumRemoteImages.Login_Help_OnPrem:
                    url = "https://i.ibb.co/bLbKCDS/login-help-onprem.png";
                    break;
 
                case EnumRemoteImages.Multiple_tab_coachmark:
                    url = "https://i.ibb.co/tZ9nKDt/multiple-tab-coachmark.png";
                    break;
                case EnumRemoteImages.Multiple_swipe_coachmark:
                    url = "https://i.ibb.co/XzkWX01/filter-swipe-coachmark.png";
                    break;
                case EnumRemoteImages.Notifications_coachmark:
                    url = "https://i.ibb.co/Zchr0Dq/notifications-coachmark.png";
                    break;
                case EnumRemoteImages.Todays_Business_coachmark:
                    url = "https://i.ibb.co/QMS8Fc2/todays-business-coachmark.png";
                    break;
 
            }
 
            return url;
        }

The final full class looks like this:

[ContentProperty("Source")]
    public class RemoteImagesExtension : IMarkupExtension
    {
 
        public EnumRemoteImages Source { getset; }
 
        public object ProvideValue(IServiceProvider serviceProvider)
        {
            ImageSource s = null;
            string url = GetImageUrlFor(Source);
            if (url.IsValidUrl())
                s = new UriImageSource()
                {
                  Uri = new Uri(url)
                };
            
            return s;
        }
 
        public static string GetImageUrlFor(EnumRemoteImages image)
        {
            string url = "https://i.ibb.co/2gcHxdg/Small-Logo.png";
            switch (image)
            {
                case EnumRemoteImages.Login_Help_Live:
                    url = "https://i.ibb.co/JdNPS4Y/login-help-live.png";
                    break;
                case EnumRemoteImages.Login_Help_OnPrem:
                    url = "https://i.ibb.co/bLbKCDS/login-help-onprem.png";
                    break;
 
                case EnumRemoteImages.Multiple_tab_coachmark:
                    url = "https://i.ibb.co/tZ9nKDt/multiple-tab-coachmark.png";
                    break;
                case EnumRemoteImages.Multiple_swipe_coachmark:
                    url = "https://i.ibb.co/XzkWX01/filter-swipe-coachmark.png";
                    break;
                case EnumRemoteImages.Notifications_coachmark:
                    url = "https://i.ibb.co/Zchr0Dq/notifications-coachmark.png";
                    break;
                case EnumRemoteImages.Todays_Business_coachmark:
                    url = "https://i.ibb.co/QMS8Fc2/todays-business-coachmark.png";
                    break;
 
            }
 
            return url;
        }
    }

 

#Xamarin.Forms: Using Google Maps instead of Apple Maps

download.jpgIf your user has Google Maps installed on their phone, especially for your iPhone users, they probably want to use that instead of Apple Maps. When your app needs to open Maps, it should open Google Maps in this case.

Here’s how to force the use of Google Maps in Xamarin Forms.

First, install the Xamarin.Essentials NuGet into all your projects first.

When Google Maps is installed, it tells the OS that it’s there and it can be called. To determine if it’s there, we are going to check for it using Xamarin.Essentials:

var supportsUri = await Launcher.CanOpenAsync("comgooglemaps://");

If we find it’s there, then we can call it:

await Launcher.OpenAsync($"comgooglemaps://?center={location.Latitude},{location.Longitude}&zoom=12");

If not then we can just open the default Maps for the OS:

await Xamarin.Essentials.Map.OpenAsync(location, options);

Here’s the full code:

        public static async Task ShowMap(Location locationstring title = null)
        {
            if (location != null)
            {
                var supportsUri = await Launcher.CanOpenAsync("comgooglemaps://");
                if (supportsUri)
                {
                    await Launcher.OpenAsync($"comgooglemaps://?center={location.Latitude},{location.Longitude}&zoom=12");
                }
                else
                {
	                var options = new MapLaunchOptions { Name = title??"Address" };
                   await Xamarin.Essentials.Map.OpenAsync(locationoptions);
                }
            }
        }

 

Google Maps is beloved. Let your users continue to enjoy it.