Checking for design mode in Xamarin.Forms

Visual Studio Xamarin XAML Uncategorized

6 years ago

Visual Studio for Windows and Mac now includes Xamarin XAML Previewer, which allows you to preview your Xamarin.Forms XAML without having to launch the app. Unfortunately, there are times when your page constructor contains code that cannot be run in design mode (for example service resolution, etc.) and causes the previewer to crash. Can we easily check if the app is in preview (design) mode?

XAML Previewer

XAML Previewer

Hey, are we in preview?

If you check the Xamarin.Forms API, you will find out that there is not yet any class that would allow you to directly check for design mode, nothing in the likes of UWP DesignMode class. It is strange that this has been not yet added, but it seems, we have to work around this fact for now. Checking for design mode in Android (isInEditMode) or iOS (TARGET_INTERFACE_BUILDER macro) platform projects does not work with Xamarin Previewer. So what can we do?

The annoying way

If you enjoy waiting for builds to finish, there is a simple solution you can use - preprocessor flag. You can create a new build configuration based on Debug, name it "Preview" for example, and then define a DESIGNMODE preprocessor flag for the Xamarin.Forms PCL project in this configuration. Whenever you need to prevent design mode from running, you can use the #if preprocessor directive:

public MainPage()
{
    InitializeComponent();

    #if !DESIGNMODE
    //code you don't want to run in design mode
    #endif
}

This certainly works, but has a number of drawbacks. Whenever you want to use the Previewer, you not only have to switch the build configuration (which can take a moment depending on the size of your solution), but you also need to rebuild the PCL as well as the platform projects (Android or iOS) for which you want to preview. This is very annoying and time consuming and bound to cause frustration in case you accidentally try to actually debug the application in the Preview configuration. But there is a better way!

The pleasant way

Previously it was quite easy to check for design mode, by inspecting the Application.Current property for null as the previewer did not instantiate it. This has however changed with Xamarin.Forms 6.2 which "fixed" this "bug". But it turns out, there is a workaround! Although the App instance is created and the constructor is executed, other app lifecycle methods are not! You can verify it quite easily, if you throw an exception inside the OnStart() method override in your App class. The app will crash immediately, but the previewer will work flawlessly. Let's utilize this to build a DesignTimeHelper class:

public static class DesignTimeHelper
{
    public static bool DesignModeOn { get; private set; } = true;

    public static void TurnOffDesignMode()
    {
        DesignModeOn = false;
    }
}

This static helper class has just a single property and a setter method. By default we leave the design mode on. And we can turn it off only with the call to the setter method. And because we now know, that OnStart() is not called in Previewer, we can easily utilize it for this purpose:

public partial class App : Application
{
    public App()
    {
        InitializeComponent();
        //MainPage = new XamarinFormsDesignMode.MainPage();
    }

    protected override void OnStart()
    {
        // Handle when your app starts
        DesignTimeHelper.TurnOffDesignMode();
        // CAUTION: MainPage initialization has to move here!
        MainPage = new XamarinFormsDesignMode.MainPage();
    }
}

There is one little caveat however - main page of the app is by default created in the App constructor. This is a problem, because in the constructor we do not yet know if we are in design mode and because design mode is set to true by default, the main page after app launch would not execute your "previewer-hidden" code even in debug. Luckily, this is quite easy to fix by moving the initialization to the OnStart() method.

Sanity check

It is always true that you should better be safe than sorry, so I recommend adding a sanity check in the DesignTimeHelper class to make sure that design time mode defaults to false in Release mode, in case someone accidentally commented out or removed the call of TurnOffDesignMode method in OnStart():

public static bool DesignModeOn { get; private set; }
#if !RELEASE
    =  true; //design mode is by default false in Release :-)
#endif

It is uglier, but definitely a safer.

Source code

Example source code for this article is available on my GitHub.

Summary

It is surprising that Xamarin.Forms doesn't offer a design mode check out of the box yet, but luckily we can quite easily go around this problem and implement a simple check ourselves.