Tata letak responsif dengan XAML

Sistem tata letak XAML menyediakan ukuran otomatis elemen, panel tata letak, dan status visual untuk membantu Anda membuat UI responsif. Dengan tata letak yang responsif, Anda dapat membuat aplikasi terlihat hebat di layar dengan berbagai ukuran jendela aplikasi, resolusi, kepadatan piksel, dan orientasi. Anda juga dapat menggunakan XAML untuk memposisikan ulang, mengubah ukuran, me-reflow, menampilkan/menyembunyikan, mengganti, atau merancang ulang UI aplikasi Anda, seperti yang dibahas dalam teknik desain Responsif. Di sini, kita membahas cara menerapkan tata letak responsif dengan XAML.

Tata letak fluida dengan properti dan panel

Fondasi tata letak responsif adalah penggunaan properti dan panel tata letak XAML yang sesuai untuk memposisikan ulang, mengubah ukuran, dan me-reflow konten dengan cara yang lancar.

Sistem tata letak XAML mendukung tata letak statis dan cairan. Dalam tata letak statis, Anda memberikan kontrol ukuran dan posisi piksel eksplisit. Saat pengguna mengubah resolusi atau orientasi perangkat mereka, UI tidak berubah. Tata letak statis dapat dipotong di berbagai faktor bentuk dan ukuran tampilan. Di sisi lain, tata letak cairan menyusut, tumbuh, dan reflow untuk merespons ruang visual yang tersedia pada perangkat.

Dalam praktiknya, Anda menggunakan kombinasi elemen statis dan cairan untuk membuat UI Anda. Anda masih menggunakan elemen dan nilai statis di beberapa tempat, tetapi pastikan bahwa keseluruhan UI responsif terhadap resolusi, ukuran layar, dan tampilan yang berbeda.

Di sini, kita membahas cara menggunakan properti XAML dan panel tata letak untuk membuat tata letak fluida.

Properti tata letak

Properti tata letak mengontrol ukuran dan posisi elemen. Untuk membuat tata letak fluida, gunakan ukuran otomatis atau proporsional untuk elemen, dan izinkan panel tata letak untuk memposisikan anak-anak mereka sesuai kebutuhan.

Berikut adalah beberapa properti tata letak umum dan cara menggunakannya untuk membuat tata letak fluida.

Tinggi dan Lebar

Properti Tinggi dan Lebar menentukan ukuran elemen. Anda dapat menggunakan nilai tetap yang diukur dalam piksel yang efektif, atau Anda dapat menggunakan ukuran otomatis atau proporsional.

Mengubah ukuran otomatis mengubah ukuran elemen UI agar sesuai dengan konten atau kontainer induknya. Anda juga dapat menggunakan ukuran otomatis dengan baris dan kolom kisi. Untuk menggunakan ukuran otomatis, atur Tinggi dan/atau Lebar elemen UI ke Otomatis.

Catatan

Apakah elemen mengubah ukuran ke kontennya atau kontainernya tergantung pada bagaimana kontainer induk menangani ukuran anak-anaknya. Untuk informasi selengkapnya, lihat Panel tata letak nanti di artikel ini.

Ukuran proporsional, juga disebut ukuran star, mendistribusikan ruang yang tersedia di antara baris dan kolom kisi berdasarkan proporsi tertimbang. Dalam XAML, nilai star dinyatakan sebagai * (atau n* untuk ukuran star tertimbang). Misalnya, untuk menentukan bahwa satu kolom lebih lebar 5 kali dari kolom kedua dalam tata letak 2 kolom, gunakan "5*" dan "*" untuk properti Lebar dalam elemen ColumnDefinition .

Contoh ini menggabungkan ukuran tetap, otomatis, dan proporsional dalam Kisi dengan 4 kolom.

Kolom Pengukuran Deskripsi
Column_1 Auto Ukuran kolom akan sesuai dengan isinya.
Column_2 * Setelah kolom Otomatis dihitung, kolom mendapatkan bagian dari lebar yang tersisa. Column_2 akan menjadi satu setengah lebar seperti Column_4.
Column_3 44 Lebar kolom akan menjadi 44 piksel.
Column_4 2* Setelah kolom Otomatis dihitung, kolom mendapatkan bagian dari lebar yang tersisa. Column_4 akan menjadi dua kali lebih lebar dari Column_2.

Lebar kolom default adalah "*", jadi Anda tidak perlu secara eksplisit mengatur nilai ini untuk kolom kedua.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition/>
        <ColumnDefinition Width="44"/>
        <ColumnDefinition Width="2*"/>
    </Grid.ColumnDefinitions>
    <TextBlock Text="Column 1 sizes to its content." FontSize="24"/>
</Grid>

Di perancang Visual Studio XAML, hasilnya terlihat seperti ini.

Kisi kolom 4 di perancang Visual Studio

Untuk mendapatkan ukuran elemen saat runtime, gunakan properti ActualHeight dan ActualWidth baca-saja alih-alih Tinggi dan Lebar.

Batasan ukuran

Saat Anda menggunakan ukuran otomatis di UI, Anda mungkin masih perlu menempatkan batasan pada ukuran elemen. Anda dapat mengatur properti MinWidth/MaxWidth dan MinHeight/MaxHeight untuk menentukan nilai yang membatasi ukuran elemen sambil memungkinkan pengubahan ukuran cairan.

Dalam Grid, MinWidth/MaxWidth juga dapat digunakan dengan definisi kolom, dan MinHeight/MaxHeight dapat digunakan dengan definisi baris.

Penjajaran

Gunakan properti HorizontalAlignment dan VerticalAlignment untuk menentukan bagaimana elemen harus diposisikan dalam kontainer induknya.

  • Nilai untuk HorizontalAlignment adalah Kiri, Tengah, Kanan, dan Regangkan.
  • Nilai untuk VerticalAlignment adalah Atas, Tengah, Bawah, dan Regangkan.

Dengan perataan Stretch , elemen mengisi semua ruang yang disediakan dalam kontainer induk. Stretch adalah default untuk kedua properti perataan. Namun, beberapa kontrol, seperti Tombol, mengambil alih nilai ini dalam gaya defaultnya. Elemen apa pun yang dapat memiliki elemen turunan dapat memperlakukan nilai Stretch untuk properti HorizontalAlignment dan VerticalAlignment secara unik. Misalnya, elemen yang menggunakan nilai Stretch default yang ditempatkan dalam Kisi membentang untuk mengisi sel yang berisinya. Elemen yang sama ditempatkan dalam ukuran Kanvas ke kontennya. Untuk informasi selengkapnya tentang cara setiap panel menangani nilai Stretch, lihat artikel Panel tata letak .

Untuk informasi selengkapnya, lihat artikel Perataan, margin, dan padding , dan halaman referensi HorizontalAlignment dan VerticalAlignment .

Visibilitas

Anda dapat mengungkapkan atau menyembunyikan elemen dengan mengatur properti Visibilitasnya ke salah satu nilai enumerasi Visibilitas: Terlihat atau Diciutkan. Saat elemen diciutkan, elemen tidak memakan ruang apa pun di tata letak UI.

Anda dapat mengubah properti Visibilitas elemen dalam kode atau dalam status visual. Saat Visibilitas elemen diubah, semua elemen turunannya juga diubah. Anda dapat mengganti bagian UI Anda dengan mengungkapkan satu panel sambil menciutkan panel lainnya.

Tip

Saat Anda memiliki elemen di UI anda yang diciutkan secara default, objek masih dibuat saat startup, meskipun tidak terlihat. Anda dapat menunda pemuatan elemen-elemen ini hingga elemen tersebut ditampilkan dengan menggunakan atribut x:Load untuk menunda pembuatan objek. Ini dapat meningkatkan performa startup. Untuk informasi selengkapnya, lihat atribut x:Load.

Sumber daya gaya

Anda tidak perlu mengatur setiap nilai properti satu per satu pada kontrol. Biasanya lebih efisien untuk mengelompokkan nilai properti ke dalam sumber daya Gaya dan menerapkan Gaya ke kontrol. Ini terutama berlaku ketika Anda perlu menerapkan nilai properti yang sama ke banyak kontrol. Untuk informasi selengkapnya tentang menggunakan gaya, lihat Kontrol gaya.

Panel tata letak

Untuk memosisikan objek visual, Anda harus meletakkannya di panel atau objek kontainer lainnya. Kerangka kerja XAML menyediakan berbagai kelas panel, seperti Canvas, Grid, RelativePanel dan StackPanel, yang berfungsi sebagai kontainer dan memungkinkan Anda untuk memposisikan dan mengatur elemen UI di dalamnya.

Hal utama yang perlu dipertimbangkan ketika memilih panel tata letak adalah bagaimana panel memposisikan dan mengukur elemen turunannya. Anda mungkin juga perlu mempertimbangkan bagaimana elemen anak yang tumpang tindih berlapis di atas satu sama lain.

Berikut adalah perbandingan fitur utama kontrol panel yang disediakan dalam kerangka kerja XAML.

Kontrol Panel Deskripsi
Canvas Kanvas tidak mendukung UI fluida; Anda mengontrol semua aspek penempatan dan ukuran elemen anak. Anda biasanya menggunakannya untuk kasus khusus seperti membuat grafik atau untuk menentukan area statis kecil dari antarmuka pengguna adaptif yang lebih besar. Anda dapat menggunakan kode atau status visual untuk memposisikan ulang elemen saat runtime.
  • Elemen diposisikan benar-benar menggunakan properti terpasang Canvas.Top dan Canvas.Left.
  • Lapisan dapat ditentukan secara eksplisit menggunakan properti terlampir Canvas.ZIndex.
  • Nilai stretch untuk HorizontalAlignment/VerticalAlignment diabaikan. Jika ukuran elemen tidak diatur secara eksplisit, ukurannya akan berukuran ke kontennya.
  • Konten anak tidak diklip secara visual jika lebih besar dari panel.
  • Konten anak tidak dibatasi oleh batas panel.
  • Grid Kisi mendukung pengubahan ukuran cairan elemen anak. Anda dapat menggunakan kode atau status visual untuk memposisikan ulang dan me-reflow elemen.
  • Elemen disusun dalam baris dan kolom menggunakan properti Terlampir Grid.Row dan Grid.Column.
  • Elemen dapat mencakup beberapa baris dan kolom menggunakan properti Terlampir Grid.RowSpan dan Grid.ColumnSpan.
  • Nilai regangan untuk HorizontalAlignment/VerticalAlignment dihormati. Jika ukuran elemen tidak diatur secara eksplisit, elemen tersebut meregang untuk mengisi ruang yang tersedia di sel kisi.
  • Konten anak dipotong secara visual jika lebih besar dari panel.
  • Ukuran konten dibatasi oleh batas panel, sehingga konten yang dapat digulir menunjukkan bilah gulir jika diperlukan.
  • RelativePanel
  • Elemen diatur dalam kaitannya dengan tepi atau tengah panel, dan dalam kaitannya satu sama lain.
  • Elemen diposisikan menggunakan berbagai properti terlampir yang mengontrol perataan panel, perataan saudara kandung, dan posisi saudara kandung.
  • Nilai regangan untuk HorizontalAlignment/VerticalAlignment diabaikan kecuali properti terpasang RelativePanel untuk perataan menyebabkan peregangan (misalnya, elemen diratakan ke tepi kanan dan kiri panel). Jika ukuran elemen tidak diatur secara eksplisit dan tidak direntangkan, ukurannya akan sesuai dengan kontennya.
  • Konten anak dipotong secara visual jika lebih besar dari panel.
  • Ukuran konten dibatasi oleh batas panel, sehingga konten yang dapat digulir menunjukkan bilah gulir jika diperlukan.
  • StackPanel
  • Elemen ditumpuk dalam satu baris baik secara vertikal atau horizontal.
  • Nilai regangan untuk HorizontalAlignment/VerticalAlignment dihormati dalam arah yang berlawanan dengan properti Orientasi. Jika ukuran elemen tidak diatur secara eksplisit, elemen tersebut membentang untuk mengisi lebar yang tersedia (atau tinggi jika Orientasinya Horizontal). Dalam arah yang ditentukan oleh properti Orientasi, elemen berukuran ke kontennya.
  • Konten anak dipotong secara visual jika lebih besar dari panel.
  • Ukuran konten tidak dibatasi oleh batas panel ke arah yang ditentukan oleh properti Orientasi, sehingga konten yang dapat digulirkan membentang di luar batas panel dan tidak menampilkan bilah gulir. Anda harus secara eksplisit membatasi tinggi (atau lebar) konten anak untuk membuat bilah gulirnya ditampilkan.
  • VariableSizedWrapGrid
  • Elemen disusun dalam baris atau kolom yang secara otomatis dibungkus ke baris atau kolom baru saat nilai MaximumRowsOrColumns tercapai.
  • Apakah elemen disusun dalam baris atau kolom ditentukan oleh properti Orientasi.
  • Elemen dapat mencakup beberapa baris dan kolom menggunakan properti terlampir VariableSizedWrapGrid.RowSpan dan VariableSizedWrapGrid.ColumnSpan.
  • Nilai regangan untuk HorizontalAlignment dan VerticalAlignment diabaikan. Elemen berukuran seperti yang ditentukan oleh properti ItemHeight dan ItemWidth. Jika properti ini tidak diatur, properti mengambil nilainya dari ukuran sel pertama.
  • Konten anak dipotong secara visual jika lebih besar dari panel.
  • Ukuran konten dibatasi oleh batas panel, sehingga konten yang dapat digulir menunjukkan bilah gulir jika diperlukan.
  • Untuk informasi terperinci dan contoh panel ini, lihat Panel tata letak.

    Panel tata letak memungkinkan Anda mengatur UI ke dalam grup kontrol logis. Saat Anda menggunakannya dengan pengaturan properti yang sesuai, Anda mendapatkan beberapa dukungan untuk mengubah ukuran otomatis, memposisikan ulang, dan aliran ulang elemen UI. Namun, sebagian besar tata letak UI memerlukan modifikasi lebih lanjut ketika ada perubahan signifikan pada ukuran jendela. Untuk ini, Anda dapat menggunakan status visual.

    Tata letak adaptif dengan status visual dan pemicu status

    Gunakan status visual untuk membuat perubahan signifikan pada UI Anda berdasarkan ukuran jendela atau perubahan lainnya.

    Saat jendela aplikasi tumbuh atau menyusut melebihi jumlah tertentu, Anda mungkin ingin mengubah properti tata letak untuk memposisikan ulang, mengubah ukuran, me-reflow, mengungkapkan, atau mengganti bagian UI Anda. Anda dapat menentukan status visual yang berbeda untuk UI Anda, dan menerapkannya saat lebar jendela atau tinggi jendela melewati ambang yang ditentukan.

    VisualState menentukan nilai properti yang diterapkan ke elemen saat berada dalam status tertentu. Anda mengelompokkan status visual di VisualStateManager yang menerapkan VisualState yang sesuai saat kondisi yang ditentukan terpenuhi. AdaptiveTrigger menyediakan cara mudah untuk mengatur ambang batas (juga disebut 'titik henti') di mana status diterapkan di XAML. Atau, Anda dapat memanggil metode VisualStateManager.GoToState dalam kode Anda untuk menerapkan status visual. Contoh kedua cara ditampilkan di bagian berikutnya.

    Mengatur status visual dalam kode

    Untuk menerapkan status visual dari kode, Anda memanggil metode VisualStateManager.GoToState . Misalnya, untuk menerapkan status saat jendela aplikasi adalah ukuran tertentu, tangani peristiwa SizeChanged dan panggil GoToState untuk menerapkan status yang sesuai.

    Di sini, VisualStateGroup berisi dua definisi VisualState. Yang pertama, DefaultState, kosong. Saat diterapkan, nilai yang ditentukan di halaman XAML diterapkan. Yang kedua, WideState, mengubah properti DisplayMode dari SplitView menjadi Sebaris dan membuka panel. Status ini diterapkan dalam penanganan aktivitas SizeChanged jika lebar jendela lebih besar dari 640 piksel efektif.

    Catatan

    Windows tidak menyediakan cara bagi aplikasi Anda untuk mendeteksi perangkat tertentu yang dijalankan aplikasi Anda. Ini dapat memberi tahu Anda keluarga perangkat (desktop, dll) aplikasi sedang berjalan, resolusi efektif, dan jumlah ruang layar yang tersedia untuk aplikasi (ukuran jendela aplikasi). Sebaiknya definisikan status visual untuk ukuran layar dan titik henti.

    <Page ...
        SizeChanged="CurrentWindow_SizeChanged">
        <Grid>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup>
                    <VisualState x:Name="DefaultState">
                            <Storyboard>
                            </Storyboard>
                        </VisualState>
    
                    <VisualState x:Name="WideState">
                        <Storyboard>
                            <ObjectAnimationUsingKeyFrames
                                Storyboard.TargetProperty="SplitView.DisplayMode"
                                Storyboard.TargetName="mySplitView">
                                <DiscreteObjectKeyFrame KeyTime="0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <SplitViewDisplayMode>Inline</SplitViewDisplayMode>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                            <ObjectAnimationUsingKeyFrames
                                Storyboard.TargetProperty="SplitView.IsPaneOpen"
                                Storyboard.TargetName="mySplitView">
                                <DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
    
            <SplitView x:Name="mySplitView" DisplayMode="CompactInline"
                       IsPaneOpen="False" CompactPaneLength="20">
                <!-- SplitView content -->
    
                <SplitView.Pane>
                    <!-- Pane content -->
                </SplitView.Pane>
            </SplitView>
        </Grid>
    </Page>
    
    private void CurrentWindow_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
    {
        if (e.Size.Width > 640)
            VisualStateManager.GoToState(this, "WideState", false);
        else
            VisualStateManager.GoToState(this, "DefaultState", false);
    }
    
    // YourPage.h
    void CurrentWindow_SizeChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::SizeChangedEventArgs const& e);
    
    // YourPage.cpp
    void YourPage::CurrentWindow_SizeChanged(IInspectable const& sender, SizeChangedEventArgs const& e)
    {
        if (e.NewSize.Width > 640)
            VisualStateManager::GoToState(*this, "WideState", false);
        else
            VisualStateManager::GoToState(*this, "DefaultState", false);
    }
    
    

    Mengatur status visual dalam markup XAML

    Sebelum Windows 10, definisi VisualState memerlukan objek Storyboard untuk perubahan properti, dan Anda harus memanggil GoToState dalam kode untuk menerapkan status. Ini ditunjukkan dalam contoh sebelumnya. Anda masih akan melihat banyak contoh yang menggunakan sintaks ini, atau Anda mungkin memiliki kode yang ada yang menggunakannya.

    Mulai dari Windows 10, Anda dapat menggunakan sintaks Setter yang disederhanakan yang diperlihatkan di sini, dan Anda dapat menggunakan StateTrigger di markup XAML Anda untuk menerapkan status. Anda menggunakan pemicu status untuk membuat aturan sederhana yang secara otomatis memicu perubahan status visual sebagai respons terhadap peristiwa aplikasi.

    Contoh ini melakukan hal yang sama seperti contoh sebelumnya, tetapi menggunakan sintaks Setter yang disederhanakan alih-alih Papan Cerita untuk menentukan perubahan properti. Dan alih-alih memanggil GoToState, ia menggunakan pemicu status AdaptiveTrigger bawaan untuk menerapkan status. Saat Anda menggunakan pemicu status, Anda tidak perlu menentukan yang kosong DefaultState. Pengaturan default diterapkan kembali secara otomatis ketika kondisi pemicu status tidak lagi terpenuhi.

    <Page ...>
        <Grid>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup>
                    <VisualState>
                        <VisualState.StateTriggers>
                            <!-- VisualState to be triggered when the
                                 window width is >=640 effective pixels. -->
                            <AdaptiveTrigger MinWindowWidth="640" />
                        </VisualState.StateTriggers>
    
                        <VisualState.Setters>
                            <Setter Target="mySplitView.DisplayMode" Value="Inline"/>
                            <Setter Target="mySplitView.IsPaneOpen" Value="True"/>
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
    
            <SplitView x:Name="mySplitView" DisplayMode="CompactInline"
                       IsPaneOpen="False" CompactPaneLength="20">
                <!-- SplitView content -->
    
                <SplitView.Pane>
                    <!-- Pane content -->
                </SplitView.Pane>
            </SplitView>
        </Grid>
    </Page>
    

    Penting

    Dalam contoh sebelumnya, properti terlampir VisualStateManager.VisualStateGroups diatur pada elemen Grid . Saat Anda menggunakan StateTriggers, selalu pastikan bahwa VisualStateGroups dilampirkan ke anak pertama akar agar pemicu berlaku secara otomatis. (Di sini, Grid adalah anak pertama dari elemen Halaman akar.)

    Sintaks properti terlampir

    Di VisualState, Anda biasanya menetapkan nilai untuk properti kontrol, atau untuk salah satu properti terlampir panel yang berisi kontrol. Saat Anda mengatur properti terlampir, gunakan tanda kurung di sekitar nama properti terlampir.

    Contoh ini memperlihatkan cara mengatur properti terlampir RelativePanel.AlignHorizontalCenterWithPanel pada TextBox bernama myTextBox. XAML pertama menggunakan sintaks ObjectAnimationUsingKeyFrames dan yang kedua menggunakan sintaks Setter .

    <!-- Set an attached property using ObjectAnimationUsingKeyFrames. -->
    <ObjectAnimationUsingKeyFrames
        Storyboard.TargetProperty="(RelativePanel.AlignHorizontalCenterWithPanel)"
        Storyboard.TargetName="myTextBox">
        <DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
    </ObjectAnimationUsingKeyFrames>
    
    <!-- Set an attached property using Setter. -->
    <Setter Target="myTextBox.(RelativePanel.AlignHorizontalCenterWithPanel)" Value="True"/>
    

    Pemicu status kustom

    Anda dapat memperluas kelas StateTrigger untuk membuat pemicu kustom untuk berbagai skenario. Misalnya, Anda dapat membuat StateTrigger untuk memicu status yang berbeda berdasarkan jenis input, lalu meningkatkan margin di sekitar kontrol saat jenis input disentuh. Atau buat StateTrigger untuk menerapkan status yang berbeda berdasarkan keluarga perangkat tempat aplikasi dijalankan. Untuk contoh cara membangun pemicu kustom dan menggunakannya untuk membuat pengalaman UI yang dioptimalkan dari dalam satu tampilan XAML, lihat sampel Pemicu status.

    Status dan gaya visual

    Anda bisa menggunakan sumber daya Gaya dalam status visual untuk menerapkan sekumpulan perubahan properti ke beberapa kontrol. Untuk informasi selengkapnya tentang menggunakan gaya, lihat Kontrol gaya.

    Dalam XAML yang disederhanakan ini dari sampel pemicu Status, sumber daya Gaya diterapkan ke Tombol untuk menyesuaikan ukuran dan margin untuk input mouse atau sentuhan. Untuk kode lengkap dan definisi pemicu status kustom, lihat sampel Pemicu status.

    <Page ... >
        <Page.Resources>
            <!-- Styles to be used for mouse vs. touch/pen hit targets -->
            <Style x:Key="MouseStyle" TargetType="Rectangle">
                <Setter Property="Margin" Value="5" />
                <Setter Property="Height" Value="20" />
                <Setter Property="Width" Value="20" />
            </Style>
            <Style x:Key="TouchPenStyle" TargetType="Rectangle">
                <Setter Property="Margin" Value="15" />
                <Setter Property="Height" Value="40" />
                <Setter Property="Width" Value="40" />
            </Style>
        </Page.Resources>
    
        <RelativePanel>
            <!-- ... -->
            <Button Content="Color Palette Button" x:Name="MenuButton">
                <Button.Flyout>
                    <Flyout Placement="Bottom">
                        <RelativePanel>
                            <Rectangle Name="BlueRect" Fill="Blue"/>
                            <Rectangle Name="GreenRect" Fill="Green" RelativePanel.RightOf="BlueRect" />
                            <!-- ... -->
                        </RelativePanel>
                    </Flyout>
                </Button.Flyout>
            </Button>
            <!-- ... -->
        </RelativePanel>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="InputTypeStates">
                <!-- Second set of VisualStates for building responsive UI optimized for input type.
                     Take a look at InputTypeTrigger.cs class in CustomTriggers folder to see how this is implemented. -->
                <VisualState>
                    <VisualState.StateTriggers>
                        <!-- This trigger indicates that this VisualState is to be applied when MenuButton is invoked using a mouse. -->
                        <triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Mouse" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="BlueRect.Style" Value="{StaticResource MouseStyle}" />
                        <Setter Target="GreenRect.Style" Value="{StaticResource MouseStyle}" />
                        <!-- ... -->
                    </VisualState.Setters>
                </VisualState>
                <VisualState>
                    <VisualState.StateTriggers>
                        <!-- Multiple trigger statements can be declared in the following way to imply OR usage.
                             For example, the following statements indicate that this VisualState is to be applied when MenuButton is invoked using Touch OR Pen.-->
                        <triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Touch" />
                        <triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Pen" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="BlueRect.Style" Value="{StaticResource TouchPenStyle}" />
                        <Setter Target="GreenRect.Style" Value="{StaticResource TouchPenStyle}" />
                        <!-- ... -->
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Page>