Tata letak responsif

Sistem tata letak XAML menyediakan ukuran otomatis elemen, panel tata letak, dan status visual untuk membantu Anda membuat UI responsif. Dengan tata letak responsif, Anda dapat membuat aplikasi terlihat hebat di layar dengan ukuran jendela aplikasi, resolusi, kepadatan piksel, dan orientasi yang berbeda. Anda juga dapat menggunakan XAML untuk memposisikan ulang, mengubah ukuran, mereflow, 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 tata letak XAML dan panel yang sesuai untuk memposisikan ulang, mengubah ukuran, dan me-reflow konten dengan cara yang cair.

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 terpotong di berbagai bentuk fisik dan ukuran tampilan. Di sisi lain, tata letak cairan menyusut, tumbuh, dan mengalir kembali 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 antarmuka pengguna keseluruhan 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 responsif, gunakan ukuran otomatis atau proporsional untuk elemen, dan izinkan panel tata letak memposisikan elemen-elemen anak sesuai kebutuhan.

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

Tinggi dan Lebar

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

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

Nota

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

Ukuran proporsional, juga disebut ukuran bintang, mendistribusikan ruang yang tersedia di antara baris dan kolom dari sebuah grid berdasarkan proporsi bobot yang tertimbang. Dalam XAML, nilai bintang dinyatakan sebagai * (atau n* untuk ukuran bintang 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 Ukuran Deskripsi
Kolom_1 Auto Kolom akan berukuran agar sesuai dengan kontennya.
Column_2 * Setelah proses perhitungan kolom Otomatis, kolom tersebut mendapatkan bagian dari lebar yang tersisa. Column_2 akan menjadi separuh lebar Column_4.
Column_3 44 Kolom akan lebarnya 44 piksel.
Kolom_4 2* Setelah proses perhitungan kolom Otomatis, kolom tersebut mendapatkan bagian dari lebar yang tersisa. Column_4 akan 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>

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

Batasan ukuran

Saat Anda menggunakan ukuran otomatis di UI, Anda mungkin masih perlu menempatkan batasan pada ukuran elemen. Anda dapat mengatur properti MinWidth/MaxWidth, serta MinHeight/MaxHeight, guna menetapkan batas ukuran elemen yang tetap memungkinkan penyesuaian yang dinamis.

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

Perataan

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

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

Dengan perataan Stretch , elemen mengisi semua ruang yang disediakan dalam kontainer induk. "Stretch adalah pengaturan 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 default Stretch yang ditempatkan dalam Grid membentang untuk mengisi sel yang mengandungnya. Elemen yang sama yang ditempatkan dalam Kanvas akan menyesuaikan ukurannya dengan 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, serta halaman referensi HorizontalAlignment dan halaman referensi VerticalAlignment.

Visibilitas

Anda dapat menampilkan atau menyembunyikan elemen dengan mengatur properti Visibility ke salah satu nilai enumerasi Visibility: Visible atau Collapsed. Ketika sebuah elemen diciutkan, elemen tersebut tidak mengambil 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.

Petunjuk / Saran

Saat Anda memiliki elemen di UI yang Dilipat secara default, objek tetap dibuat saat startup, meskipun tidak terlihat. Anda dapat menunda pemuatan elemen-elemen ini sampai 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 styling

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 Pengontrolan gaya.

Panel tata letak

Untuk memposisikan 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 memosisikan dan mengatur elemen UI di dalamnya.

Hal utama yang perlu dipertimbangkan saat memilih panel tata letak adalah bagaimana panel memposisikan dan mengukur elemen turunannya. Anda perlu juga mempertimbangkan cara elemen turunan yang tumpang tindih ditumpuk di atas satu sama lain.

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

Panel Kontrol Deskripsi
Canvas Kanvas tidak mendukung UI dinamis; Anda mengontrol semua aspek penempatan dan ukuran dari elemen-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 secara mutlak menggunakan properti terlampir Canvas.Top dan Canvas.Left.
  • Pengaturan lapisan dapat ditentukan secara eksplisit menggunakan properti terlampir Canvas.ZIndex.
  • Nilai peregangan untuk HorizontalAlignment/VerticalAlignment diabaikan. Jika ukuran elemen tidak diatur secara eksplisit, ukurannya akan menyesuaikan dengan kontennya.
  • Konten anak tidak terpotong secara visual jika lebih besar dari panel.
  • Konten anak tidak dibatasi oleh batas panel.
  • Kisi Kisi mendukung pengubahan ukuran dinamis untuk 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 peregangan untuk HorizontalAlignment/VerticalAlignment dihormati. Jika ukuran elemen tidak diatur secara eksplisit, elemen tersebut membentang untuk mengisi ruang yang tersedia di sel kisi.
  • Konten elemen anak akan terpotong secara visual jika ukurannya 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 keselarasan panel, perataan elemen sejenis, dan penempatan elemen sejenis.
  • Nilai stretch untuk HorizontalAlignment/VerticalAlignment diabaikan kecuali properti tertaut RelativePanel untuk perataan mengakibatkan peregangan (misalnya, sebuah elemen disejajarkan baik pada tepi kanan maupun kiri panel). Jika ukuran elemen tidak diatur secara eksplisit dan tidak direntangkan, ukurannya menyesuaikan dengan kontennya.
  • Konten elemen anak akan terpotong secara visual jika ukurannya 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 peregangan untuk HorizontalAlignment/VerticalAlignment dipatuhi 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 mengikuti ukuran kontennya.
  • Konten elemen anak akan terpotong secara visual jika ukurannya lebih besar dari panel.
  • Ukuran konten tidak dibatasi oleh batas panel dalam arah yang ditentukan oleh properti Orientasi, sehingga konten yang dapat digulir 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 stretch untuk HorizontalAlignment dan VerticalAlignment diabaikan. Elemen berukuran seperti yang ditentukan oleh properti ItemHeight dan ItemWidth. Jika properti ini tidak diatur, properti tersebut mengambil nilainya dari ukuran sel pertama.
  • Konten elemen anak akan terpotong secara visual jika ukurannya 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-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 dukungan untuk pengubahan ukuran otomatis, pemposisian ulang, dan pengaliran 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 bertambah 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.

    Nota

    Windows tidak menyediakan cara bagi aplikasi Anda untuk mendeteksi perangkat tertentu yang dijalankan aplikasi Anda. Ini dapat memberi tahu Anda resolusi yang efektif, dan jumlah ruang layar yang tersedia untuk aplikasi (ukuran jendela aplikasi). Disarankan untuk mendefinisikan status visual untuk ukuran layar dan titik pemisah.

    <Page ...
        SizeChanged="Page_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 Page_SizeChanged(object sender, Microsoft.UI.Xaml.SizeChangedEventArgs e)
    {
        if (e.Size.Width > 640)
            VisualStateManager.GoToState(this, "WideState", false);
        else
            VisualStateManager.GoToState(this, "DefaultState", false);
    }
    
    // YourPage.h
    void Page_SizeChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::SizeChangedEventArgs const& e);
    
    // YourPage.cpp
    void YourPage::Page_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 diperlukan Storyboard objek 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 sudah ada yang menggunakannya.

    Mulai dari Windows 10, Anda dapat menggunakan sintaks Setter yang disederhanakan di sini, dan Anda dapat menggunakan sintaks 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 Storyboard 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 elemen 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 dipasang ke elemen anak pertama dari root agar pemicu berfungsi secara otomatis. (Di sini, Kisi adalah anak pertama dari elemen Halaman akar.)

    Sintaks properti terlampir

    Di VisualState, Anda biasanya mengatur 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 menunjukkan 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 keadaan 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 State.

    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 Pengontrolan 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 State.

    <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>