Kontrola režimu náhledu v Xamarin.Forms

Visual Studio Xamarin XAML

7 years ago

Visual Studio pro Windows a Mac nyní obsahuje Xamarin XAML Previewer, který nám umožňuje zobrazit náhled vašich Xamarin.Forms XAML souborů bez nutnosti spuštění aplikace. Bohužel v některých případech může obsahovat konstruktor vašich stránek kód, se kterým si Previewer nedokáže poradit (například service resolution, apod.) a spadne. Můžeme snadno ověřit zda aplikace běží v režimu náhledu (design mode)?

XAML Previewer

XAML Previewer

Haló, jsme v preview?

Když si prohlédnete Xamarin.Forms API, nenajdete žádnou třídu, která by přímo umožňovala ověřit, zda je spuštěn mód náhledu, tedy nic podobného třídě DesignMode v UWP. Je zvláštní, že toto API zatím nebylo přidáno, ale vypadá to, že si zatím budeme muset poradit sami. Po testu zjistíme, že nelze snadno použít ani zabudované kontroly jednotlivých platforem (isInEditMode na Androidu a makra TARGET_INTERFACE_BUILDER na iOS). Co s tím?

Nepříjemná cesta

Pokud se vyžíváte v čekání na doběhnutí kompilace, je jednoduché řešení, které můžete použít - flag preprocesoru. Můžete vytvořit novou build konfiguraci založenou na Debugu, pojmenovat ji kupříkladu "Preview" a pro ni Xamarin.Forms PCL projekt pak v ní definovat flag preprocesoru, např. DESIGNMODE. Kdykoliv budete potřebovat zabránit kódu v běhu v režimu náhledu, můžete použít direktivu #if:

public MainPage()
{
    InitializeComponent();

    #if !DESIGNMODE
    //kód, který v módu náhledu nechcete spustit
    #endif
}

Toto rozhodně funguje, ale má to více nevýhod než je zdrávo. Kdykoliv budete chtít použít Previewer, musíte nejen přepnout aktivní build konfiguraci (což může v závislosti na velikost řešení chvíli trvat), ale také musíte znovu sestavit nejen PCL projekt, ale platformní projekty pro Android a iOS, protože jinak nebude Previewer umět náhled zobrazit. Toto je poměrně nepříjemné a snadno sebere velké množství času. Navíc je velmi snadné omylem aplikaci spustit v této alternativní konfiguraci a dostaneme tak neočekávané chování. Ale jde to lépe!

Příjemná cesta

Ještě nedávno bylo možné stav náhledového režimu určit velmi snadno porovnáním vlastnosti Application.Current s null, protože previewer instanci aplikace nevytvářel. To se však změnilo s verzí Xamarin.Forms 6.2 která tento "bug" "opravila". Naštěstí to můžeme obejít! Přestože Previewer vytváří instanci naší aplikace a její konstruktor je volán, ostatní metody jejího životního cyklu ne! To můžeme poměrně snadno ověřit vyhozením výjimky v metodě OnStart(). Aplikace po spuštění okamžitě spadne, ale Previewer funguje bez problémů. Využijme tohoto faktu a vytvořme si třídu DesignTimeHelper:

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

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

Tato statická třída obsahuje pouze jednu proměnnou a setter metodu. Ve výchozím stavu je design mode zapnut a lze jej vypnout pouze zavoláním setter metody. A protože nyní víme, že OnStart v Previeweru zavolána není, můžeme ji přesně za tímto účelem využít:

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

    protected override void OnStart()
    {
        DesignTimeHelper.TurnOffDesignMode();
        // POZOR: Inicializace MainPage se musí přesunout sem!
        MainPage = new XamarinFormsDesignMode.MainPage();
    }
}

Je tu ale jeden malý háček - hlavní stránka aplikace je ve výchozím stavu vytvořena již v konstruktoru aplikace. To je bohužel problém, protože v konstruktoru ještě nevíme, jaký je stav design mode a protože jeho výchozí hodnota hodnota je true, váš kód "skrytý před Previewerem" by se na hlavní stránce aplikace nespustil ani při spuštění. Naštěstí toto můžeme snadno obejít přesunutím inicializace do metody OnStart().

Dvakrát měř, jednou řež

Protože se opatrnost vždy vyplácí, doporučuji do třídy DesignTimeHelper přidat direktivu #if, která zaručí, že v Release módu bude náhledový režim již od začátku vypnut, kdyby někdo omylem zakomentoval či smazal volání metody TurnOffDesignMode v OnStart():

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

Je to ošklivější, ale na druhou stranu bezpečnější.

Zdrojový kód

Ukázkový zdrojový kód k tomuto článku je k dispozici na mém GitHubu.

Shrnutí

Je překvapivé, že Xamarin.Forms zatím nenabízí možnost zjištění řežimu náhledu jako součást API, ale naštěstí tento nedostatek můžeme snadno obejít a kontrolu implementovat sami.