Responsywne UI w Universal Windows Platform – Adaptive Triggers

Tworząc interfejs użytkownika w aplikacjach dla Universal Windows Platform musimy mieć na uwadze to że mogą być one uruchamiane na ekranach o rożnych rozdzielczościach. Takich jak ekrany urządzeń typu smartphone, monitory o różnych rozdzielczościach, czy nawet urządzenia IOT z wbudowanymi wyświetlaczami. Jak to osiągnąć aby UI był czytelny i wygodny w użyciu dla użytkownika Twojej aplikacji? Postaram się odpowiedzieć na to pytanie w poniższym wpisie skupiając się na Adaptive Triggers, oraz o tym czego się o nich dotychczas nauczyłem tworząc aplikacje na tą platformę.

Spójrzmy na poniższy fragment kodu:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState>
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="720" />
                    </VisualState.StateTriggers>

                    <VisualState.Setters>
                        <Setter Target="myPanel.Orientation" Value="Horizontal" />
                        <Setter Target="myPanel.Background" Value="Yellow" />
                        <Setter Target="myTextBlock.FontSize" Value="36" />
                    </VisualState.Setters>
                </VisualState>

            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        
        <StackPanel x:Name="myPanel" Orientation="Vertical" Background="Blue">
            <TextBlock Text="This is a block of text. It is text block 1. " Style="{ThemeResource BodyTextBlockStyle}"/>
            <TextBlock x:Name="myTextBlock" Text="This is a block of text. It is text block 2. " FontSize="24" Style="{ThemeResource BodyTextBlockStyle}"/>
            <TextBlock Text="This is a block of text. It is text block 3. " Style="{ThemeResource BodyTextBlockStyle}"/>
        </StackPanel>
    </Grid>

Jak widzimy mamy tu do czynienia z bardzo prostym wycinkiem UI, ale jest to celowe, aby ułatwić zrozumienia założeń AdaptiveTriggers. Po krótcę wyjaśnię o co w nich chodzi.

  • Najpierw definiujemy wyzwalacz stanu widoku VisualState.StateTrigger, w którym ustawiamy tzw. AdaptiveTrigger. Ten konkretny wyzwalacz zadziała w momencie kiedy wielkość okna aplikacji będzie większa lub równa wielkości 720 efektywnych pikseli. Patrz linia nr 6.
  • Następnie definiujemy jak będą zachowywać się nasze kontrolki, a konkretniej ich właściwości, w tym przykładzie patrz linia nr 11 kolor tła StackPanelu zmieni się na kolor żółty. W tym przykładzie to nie jedyna właściwość, która zmieni wygląd, sprawdź bez uruchamiania kodu co jeszcze się wydarzy.

Aby zmieniać wygląd UI w zależności od wielkości ekranu wystarczy dodać więcej wyzwalaczy AdaptiveTrigger. Poniższy kod XAML jest trochę bardziej skomplikowany, dopisałem więcej wyzwalaczy, które będą zmieniać zachowanie kontrolek w zależności od różnych wielkości ekranu.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState>
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="320" />
                    </VisualState.StateTriggers>

                    <VisualState.Setters>
                        <Setter Target="myPanel.Orientation" Value="Vertical" />
                        <Setter Target="myPanel.Background" Value="Red" />
                        <Setter Target="myTextBlock2.FontSize" Value="12" />
                        <Setter Target="myTextBlock1.Visibility" Value="Collapsed"/>
                        <Setter Target="myTextBlock3.Visibility" Value="Collapsed"/>
                    </VisualState.Setters>
                </VisualState>
                <VisualState>
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="720" />
                    </VisualState.StateTriggers>

                    <VisualState.Setters>
                        <Setter Target="myPanel.Orientation" Value="Horizontal" />
                        <Setter Target="myPanel.Background" Value="Yellow" />
                        <Setter Target="myTextBlock2.FontSize" Value="36" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState>
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="1024" MinWindowHeight="1024"/>
                    </VisualState.StateTriggers>

                    <VisualState.Setters>
                        <Setter Target="myPanel.Orientation" Value="Horizontal" />
                        <Setter Target="myPanel.Background" Value="Green" />
                        <Setter Target="myTextBlock2.FontSize" Value="54" />
                    </VisualState.Setters>
                </VisualState>
                
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        
        <StackPanel x:Name="myPanel" Orientation="Vertical" Background="Blue">
            <TextBlock x:Name="myTextBlock1" Text="This is a block of text. It is text block 1. " Style="{ThemeResource BodyTextBlockStyle}"/>
            <TextBlock x:Name="myTextBlock2" Text="This is a block of text. It is text block 2. " FontSize="24" Style="{ThemeResource BodyTextBlockStyle}"/>
            <TextBlock x:Name="myTextBlock3" Text="This is a block of text. It is text block 3. " Style="{ThemeResource BodyTextBlockStyle}"/>
        </StackPanel>
    </Grid>

Jak zwrócisz uwagę na linię nr 30, to zauważysz że ten wyzwalacz zadziała nie tylko wtedy kiedy szerokość okna osiągnie rozdzielczość 1024, ale również warunkiem jest jego wysokość.

Jakie wartości ustawiać dla różnych rozmiarów ekranów? Microsoft zaleca następujące wartości wyzwalaczy dla poszczególnych rozmiarów ekranu:

  • Phone: <AdaptiveTrigger MinWindowWidth=”320″/>
  • Tablet: <AdaptiveTrigger MinWindowWidth=”720″/>
  • Desktop: <AdaptiveTrigger MinWindowWidth=”1024″/>
  • XBox: <AdaptiveTrigger MinWindowWidth=”1280″/>
  • Surface Hub: <AdaptiveTrigger MinWindowWidth=”1920″/>

Powyższe zestawienie dobrze ilustruje ten obraz:
IC816047

 

 

To nie wszystko co dotyczy AdaptiveTrigger-ów, ponieważ można tworzyć nowe, które będą reagować na wymyślone przez Ciebie warunki i zdarzenia. Wszystko sprowadza się do dziedziczenia po abstrakcyjnej klasie StateTriggerBase. Jednakże w tym poście nie będziemy tworzyć nowych, natomiast polecę ci ten oto adres do repozytorium gdzie jest stworzona całkiem pokaźna kolekcja wyzwalaczy, naprawdę jest w czym wybierać. Z ciekawszych polecam wyzwalacz, który będzie zmieniał wygląd UI, na podstawie tzw. rodziny urządzeń (Desktop, Mobile, IoT or Team). Inny ciekawy zmieni nam interfejs na podstawie tego czy mamy połączenie z internetem czy też nie.

Dziś to na tyle, zachęcam do eksperymentów.