Xamarin UWP: Use multilanguage resource files properly

IIf you are experiencing the oddity that the UWP version of your application can’t find the correct language version of your ‘resx’ files, then you have probably fallen into the same error I did.

The language management of UWP apps works differently to its Android and iOS counterparts.

Reade more about it at: https://docs.microsoft.com/en-us/windows/apps/design/globalizing/manage-language-and-region

How the UWP deals with multilingualism in brief

Only the union can be set as UI language

Two different lists are considered as the list of languages supported by the application. One is the list of languages supported by windows (language pack installed), and the other is the list of languages supported by the application (resx files created for them). The intersection of these can only be handled by the language switching code.

Where to define all of the supported languages by the app

Easily, without mainting it you can define them in only one line making a change in ‘Package.appxmanifest‘ file.

  <Resources>
    <Resource Language="x-generate" />
  </Resources>

The x-generate value will collect all of the available languages on compile time.

Otherwise, you will need to list all of them one by one:

  <Resources>
    <Resource Language="EN-US" />
    <Resource Language="JA-JP" />
    <Resource Language="FR-FR" />
  </Resources>

Best practice?

Perhaps, if the application is running on UWP platform, you should make an if statement for the runtime platform and filter out languages that are not supported by windows.

// Get all of the supported language by windows in BCP-47 language tag (i.e. "en-US")
IReadOnlyList<string> userLanguages = Windows.System.UserProfile.GlobalizationPreferences.Languages;
This content has 2 years. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.

Xamarin.Forms: Use converters with binding objects which can have null as value.

You have a converter, which should return a specific value, even if the binded object is null.

Converters does not get executed, when the binded value is null. But here is a solution, how to handle this situations.

Define an example converter

In this case we will use an Int value to bool value converter.

    public class IsIntGreaterThanConverter : IValueConverter, IMarkupExtension
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is int bindedValue && parameter is int targetValue)
            {
                return bindedValue > targetValue;
            }
            else
                return false;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }

This code returns true, if the binded value is greater than the value provided as a converter parameter.

It can be used with the following code snippet in XAML:

            <Label Text="The value is greater than 0">
                <Label.IsVisible>
                    <Binding Path="Object.IntValue"
                             Converter="{app1:IsIntGreaterThanConverter}">
                        <Binding.ConverterParameter>
                            <x:Int32>0</x:Int32> 
                        </Binding.ConverterParameter>
                    </Binding>
                </Label.IsVisible>
            </Label>

But what happends, when the ‘Object’ is null? Well the converter does not get executed. And since View’s default value of the IsVisible bindable property is true, then the label will be visible, even if the Object is null.

How to handle null scenarios?

Bindings have a property called FallbackValue. Give a value to the fallbackvalue property in order to override the default value, like this:

           <Label Text="The value is greater than 0">
                <Label.IsVisible>
                    <Binding Path="Object.IntValue"

                             FallbackValue="False"

                             Converter="{app1:IsIntGreaterThanConverter}">
                        <Binding.ConverterParameter>
                            <x:Int32>0</x:Int32> 
                        </Binding.ConverterParameter>
                    </Binding>
                </Label.IsVisible>
            </Label>

This should return false even if the binded obejct is null. 🙂

This content has 2 years. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.

Xamarin Forms: White screen between page push and pop solved

If you are experiencing white screen when pushing to Navigation or Modal stack on Android, read on.

I’m not sure is this a bug in Xamarin Forms or not, but I guess this is, because it comes only in certain scenarios, and not always.

What is happening, how do you notice this error?

You have got a NavigationPage. You are pushing a new page to the navigationstack, and the page is not getting rendered, only a blank white screen displays.

If you are rotating the device, the page is getting rendered well.

My environment was:
Xamarin.Forms: 4.8 up to 5.0
Device: Samsung Galaxy A12
Visual Studio 2019 Professional with Xamarin.Android SDK 11.4.0.5

Solution

Always invoke the INavigation’s methods on the applications Main Thread. UI changes must go always on the UI thread of the application.

Create a class which wraps the INavigation presented by your Views. It’s handy to store a reference in this class to the Applications Current MainPage’s INavigation instance, so try to build your code to supply the actual INavigation Instance every time to this class when the application’s mainpage is set.

	public class NavigationDispatcher : INavigation
	{
		private INavigation _navigation;

		public IReadOnlyList<Page> ModalStack => _navigation?.ModalStack;

		public IReadOnlyList<Page> NavigationStack => _navigation?.NavigationStack;

		private void SetNavigation(INavigation navigation)
		{
			_navigation = navigation;
		}

		public void InsertPageBefore(Page page, Page before)
		{
			_ = Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(() =>
			  {
				  _navigation.InsertPageBefore(page, before);
			  });
		}

		public Task<Page> PopAsync()
		{
			return Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
			{
				return await _navigation.PopAsync();
			});
		}

		public Task<Page> PopAsync(bool animated)
		{
			return Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
			{
				return await _navigation.PopAsync(animated);
			});
		}

		public Task<Page> PopModalAsync()
		{
			return Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
			{
				return await _navigation.PopModalAsync();
			});
		}

		public Task<Page> PopModalAsync(bool animated)
		{
			return Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
			{
				return await _navigation.PopModalAsync(animated);
			});
		}

		public Task PopToRootAsync()
		{
			return Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
			{
				await _navigation.PopToRootAsync();
			});
		}

		public Task PopToRootAsync(bool animated)
		{
			return Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
			{
				await _navigation.PopToRootAsync(animated);
			});
		}

		public Task PushAsync(Page page)
		{
			return Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
			{
				await _navigation.PushAsync(page);
			});
		}

		public Task PushAsync(Page page, bool animated)
		{
			return Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
			{
				await _navigation.PushAsync(page, animated);
			});
		}

		public Task PushModalAsync(Page page)
		{
			return Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
			{
				await _navigation.PushModalAsync(page);
			});
		}

		public Task PushModalAsync(Page page, bool animated)
		{
			return Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
			{
				await _navigation.PushModalAsync(page, animated);
			});
		}

		public void RemovePage(Page page)
		{
			_ = Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(() =>
			  {
				  _navigation.RemovePage(page);
			  });
		}
	}

Remarks

Consider a check for the current thread in the methods body.
If they are being executed in the main thread, you won’t need to switch to the main again
.

Bug is reported on Github: https://github.com/xamarin/Xamarin.Forms/issues/11993

This content has 3 years. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.

Xamarin Forms: Close applications programmatically

In some scenarios, we need to programmatically close our application. We can do it in our common code with Xamarin Forms too. Buuuuuut …….

Can we close an iOS application?

There is no API provided for gracefully terminating an iOS application.

In iOS, the user presses the Home button to close applications. Should your application have conditions in which it cannot provide its intended function, the recommended approach is to display an alert for the user that indicates the nature of the problem and possible actions the user could take — turning on WiFi, enabling Location Services, etc. Allow the user to terminate the application at their own discretion.

Do not call the exit function. Applications calling exit will appear to the user to have crashed, rather than performing a graceful termination and animating back to the Home screen.

Source: https://developer.apple.com/library/archive/qa/qa1561/_index.html

Close on Android from Forms code

There is one important place to implement closing apps from code: When you are handling back button presses by yourself.

Imagine, that you have a main screen, which is the root page. When a users presses the hardware back button you should display a confirmer, that says “Do you really want to close the application?”

If the user choose yes, you should make a call like this:

					DependencyService.Get<IPlatformSpecificService>().CloseApplication();

The definition of the IPlatformSpecificService should be look like this:

	public interface IPlatformSpecificService
	{
		void CloseApplication();
	}

And the Android implementation of the IPlatformSpecificService should be something like:

using Plugin.CurrentActivity;

[assembly: Dependency(typeof(DeviceSpecificService))]
namespace AnAwesomeCompany.AGreatProduct.Droid.DependencyServices
{
	public class PlatformSpecificService: IPlatformSpecificService
	{
		public void CloseApplication()
		{
			CrossCurrentActivity.Current.Activity.FinishAndRemoveTask();
		}
	}
}

Activity supports a lot of closing methods of the application. FinishAndRemoveTask will clear the app from the recents list too.

Take a look at official Android documentation at: https://developer.android.com/reference/android/app/Activity#finishAndRemoveTask()

This content has 3 years. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.

Xamarin Forms: Logging with anything from Console to SQLite

And it’s also logging the invoked method name, and the file name containing the method!

My Forms.RecurrenceToolkit NuGet package pack is now extended with logging functionality.

You can use the pre-written Console and SQLite logger without writing much code, or you can implement your own logger in a few lines, and use it instantly simultaneous with other loggers.

Install banditoth.Forms.RecurrenceToolkit.Logging.* packages, and enjoy the painless logging, and focus on the great ideas instead of being a copy paste robot. 🙂

Usage

In your App.xaml.cs, initalize the logger like:

LoggingProvider.Initalize(
	// If you have installed the console logger:
	new ConsoleLogger(),
	// If you have installed SQLite Logger:
	new SQLiteLogger()
	);

The logger is including the calling method’s name, and the .cs file name in the logs. You can access the logger from anywhere by calling these methods:

LoggingProvider.LogCritical("It's a critical message");
LoggingProvider.LogDebug("It's a debug message");
LoggingProvider.LogError("It's an error message");
LoggingProvider.LogException(new Exception(), "It's an exception");
LoggingProvider.LogInformation("It's an information message");
LoggingProvider.LogTrace("It's a trace message");
LoggingProvider.LogTrace(new StackTrace());
LoggingProvider.LogWarning("It's a warning message");

By default, the console and the SQLite logger logs exceptions in error level.

You can implement your own logger by deriving from BaseLogger class, like:

public class CustomLogger : BaseLogger
	{
		public CustomLogger() : base(new LoggerOptions()
		{
			IncludeCallerSourceFullFileName = true, // This will print C:/Users/Path/AssemblyFile.cs
			IncludeCallerSourceShortFileName = false, // This will print AssemblyFile.cs
			ExceptionLevel = Enumerations.LogLevel.Error, // The LogExceptions calls routed to log to the loglevel set.
			IncludeCallerMethodName = true // This can print the calling method's name
		})
		{

		}
		
		public override void LogCritical(string criticalMessage, string callerMethod, string filePath)
		{
			// Your own method
		}

		// .. File continues

Follow for more

https://github.com/banditoth/Forms.RecurrenceToolkit

This content has 3 years. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.