Optimalkan markup XAML Anda

Mengurai markup XAML untuk membuat objek dalam memori memakan waktu untuk UI yang kompleks. Berikut adalah beberapa hal yang dapat Anda lakukan untuk meningkatkan penguraian dan waktu muat markup XAML Anda dan efisiensi memori aplikasi Anda.

Saat memulai aplikasi, batasi markup XAML yang dimuat hanya untuk apa yang Anda butuhkan untuk antarmuka pengguna awal Anda. Periksa markup di halaman awal Anda (termasuk sumber daya halaman) dan konfirmasikan bahwa Anda tidak memuat elemen tambahan yang tidak diperlukan segera. Elemen-elemen ini dapat berasal dari berbagai sumber, seperti kamus sumber daya, elemen yang awalnya diciutkan, dan elemen yang diambil dari elemen lain.

Mengoptimalkan XAML Anda untuk efisiensi membutuhkan pertukaran; tidak selalu ada satu solusi untuk setiap situasi. Di sini, kami melihat beberapa masalah umum dan memberikan panduan yang dapat Anda gunakan untuk melakukan trade-off yang tepat untuk aplikasi Anda.

Meminimalkan jumlah elemen

Meskipun platform XAML mampu menampilkan sejumlah besar elemen, Anda dapat membuat aplikasi Anda lay out dan merender lebih cepat dengan menggunakan jumlah elemen terkecil untuk mencapai visual yang Anda inginkan.

Pilihan yang Anda buat dalam cara Anda menata kontrol UI akan memengaruhi jumlah elemen UI yang dibuat saat aplikasi Anda dimulai. Untuk informasi selengkapnya tentang mengoptimalkan tata letak, lihat Mengoptimalkan tata letak XAML Anda.

Jumlah elemen sangat penting dalam templat data karena setiap elemen dibuat lagi untuk setiap item data. Untuk informasi tentang mengurangi jumlah elemen dalam daftar atau kisi, lihat Pengurangan elemen per item di artikel pengoptimalan UI ListView dan GridView.

Di sini, kami melihat beberapa cara lain untuk mengurangi jumlah elemen yang harus dimuat aplikasi Anda saat startup.

Menuangkan pembuatan item

Jika markup XAML Anda berisi elemen yang tidak langsung ditampilkan, Anda dapat menukar pemuatan elemen tersebut hingga elemen tersebut ditampilkan. Misalnya, Anda dapat menunda pembuatan konten yang tidak terlihat seperti tab sekunder di UI seperti tab. Atau, Anda mungkin menampilkan item dalam tampilan kisi secara default, tetapi menyediakan opsi bagi pengguna untuk menampilkan data dalam daftar sebagai gantinya. Anda dapat menunda pemuatan daftar hingga diperlukan.

Gunakan atribut x:Load alih-alih properti Visibilitas untuk mengontrol kapan elemen ditampilkan. Ketika visibilitas elemen diatur ke Diciutkan, maka dilewati selama render pass, tetapi Anda masih membayar biaya instans objek dalam memori. Saat Anda menggunakan x:Load sebagai gantinya, kerangka kerja tidak membuat instans objek sampai diperlukan, sehingga biaya memori bahkan lebih rendah. Kelemahannya adalah Anda membayar overhead memori kecil (sekitar 600 byte) ketika UI tidak dimuat.

Catatan

Anda dapat menunda pemuatan elemen menggunakan atribut x:Load atau x:DeferLoadStrategy . Atribut x:Load tersedia mulai windows 10 Creators Update (versi 1703, SDK build 15063). Versi min yang ditargetkan oleh proyek Visual Studio Anda harus Windows 10 Creators Update (10.0, Build 15063) untuk menggunakan x:Load. Untuk menargetkan versi sebelumnya, gunakan x:DeferLoadStrategy.

Contoh berikut menunjukkan perbedaan jumlah elemen dan penggunaan memori ketika teknik yang berbeda digunakan untuk menyembunyikan elemen UI. ListView dan GridView yang berisi item identik ditempatkan di Grid akar halaman. ListView tidak terlihat, tetapi GridView ditampilkan. XAML dalam masing-masing contoh ini menghasilkan UI yang sama di layar. Kami menggunakan alat Visual Studio untuk pembuatan profil dan performa untuk memeriksa jumlah elemen dan penggunaan memori.

Opsi 1 - Tidak Efisien

Di sini, ListView dimuat, tetapi tidak terlihat karena Lebarnya adalah 0. ListView dan setiap elemen turunannya dibuat di pohon visual dan dimuat ke dalam memori.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE.-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <ListView x:Name="List1" Width="0">
        <ListViewItem>Item 1</ListViewItem>
        <ListViewItem>Item 2</ListViewItem>
        <ListViewItem>Item 3</ListViewItem>
        <ListViewItem>Item 4</ListViewItem>
        <ListViewItem>Item 5</ListViewItem>
        <ListViewItem>Item 6</ListViewItem>
        <ListViewItem>Item 7</ListViewItem>
        <ListViewItem>Item 8</ListViewItem>
        <ListViewItem>Item 9</ListViewItem>
        <ListViewItem>Item 10</ListViewItem>
    </ListView>

    <GridView x:Name="Grid1">
        <GridViewItem>Item 1</GridViewItem>
        <GridViewItem>Item 2</GridViewItem>
        <GridViewItem>Item 3</GridViewItem>
        <GridViewItem>Item 4</GridViewItem>
        <GridViewItem>Item 5</GridViewItem>
        <GridViewItem>Item 6</GridViewItem>
        <GridViewItem>Item 7</GridViewItem>
        <GridViewItem>Item 8</GridViewItem>
        <GridViewItem>Item 9</GridViewItem>
        <GridViewItem>Item 10</GridViewItem>
    </GridView>
</Grid>

Pohon visual langsung dengan ListView dimuat. Jumlah elemen total untuk halaman adalah 89.

Screenshot of the visual tree with list view.

ListView dan turunannya dimuat ke dalam memori.

Screenshot of the Managed Memory Test App 1 dot E X E table showing ListView and its children are loaded into memory.

Opsi 2 - Lebih baik

Di sini, Visibilitas ListView diatur ke diciutkan (XAML lainnya identik dengan aslinya). ListView dibuat di pohon visual, tetapi elemen turunannya tidak. Namun, mereka dimuat ke dalam memori, sehingga penggunaan memori identik dengan contoh sebelumnya.

<ListView x:Name="List1" Visibility="Collapsed">

Pohon visual langsung dengan ListView diciutkan. Jumlah elemen total untuk halaman adalah 46.

Screenshot of the visual tree with collapsed list view.

ListView dan turunannya dimuat ke dalam memori.

An updated screenshot of the Managed Memory Test App 1 dot E X E table showing ListView and its children are loaded into memory.

Opsi 3 - Paling Efisien

Di sini, ListView memiliki atribut x:Load yang diatur ke False (XAML lainnya identik dengan aslinya). ListView tidak dibuat di pohon visual atau dimuat ke dalam memori saat memulai.

<ListView x:Name="List1" Visibility="Collapsed" x:Load="False">

Pohon visual langsung dengan ListView tidak dimuat. Jumlah elemen total untuk halaman adalah 45.

Visual tree with list view not loaded

ListView dan turunannya tidak dimuat ke dalam memori.

Visual tree with list view

Catatan

Jumlah elemen dan penggunaan memori dalam contoh ini sangat kecil dan hanya ditunjukkan untuk menunjukkan konsep. Dalam contoh ini, overhead penggunaan x:Load lebih besar dari penghematan memori, sehingga aplikasi tidak akan mendapat manfaat. Anda harus menggunakan alat pembuatan profil di aplikasi untuk menentukan apakah aplikasi Anda akan mendapat manfaat dari pemuatan yang ditangguhkan atau tidak.

Menggunakan properti panel tata letak

Panel tata letak memiliki properti Latar Belakang sehingga tidak perlu meletakkan Persegi di depan Panel hanya untuk mewarnainya.

Efisien

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid>
    <Rectangle Fill="Black"/>
</Grid>

Efisien

<Grid Background="Black"/>

Panel tata letak juga memiliki properti batas bawaan, sehingga Anda tidak perlu meletakkan elemen Batas di sekitar panel tata letak. Lihat Mengoptimalkan tata letak XAML Anda untuk informasi dan contoh selengkapnya.

Gunakan gambar sebagai pengganti elemen berbasis vektor

Jika Anda menggunakan kembali elemen berbasis vektor yang sama cukup kali, elemen tersebut menjadi lebih efisien untuk menggunakan elemen Gambar sebagai gantinya. Elemen berbasis vektor bisa lebih mahal karena CPU harus membuat setiap elemen secara terpisah. File gambar hanya perlu didekodekan sekali.

Mengoptimalkan sumber daya dan kamus sumber daya

Anda biasanya menggunakan kamus sumber daya untuk menyimpan, pada tingkat global yang agak global, sumber daya yang ingin Anda referensikan di beberapa tempat di aplikasi Anda. Misalnya, gaya, kuas, templat, dan sebagainya.

Secara umum, kami telah mengoptimalkan ResourceDictionary untuk tidak membuat instans sumber daya kecuali jika diminta. Tetapi ada situasi yang harus Anda hindari sehingga sumber daya tidak diinstansiasi secara tidak perlu.

Sumber daya dengan x:Name

Gunakan atribut x:Key untuk mereferensikan sumber daya Anda. Sumber daya apa pun dengan atribut x:Name tidak akan mendapat manfaat dari pengoptimalan platform; sebagai gantinya, sumber daya dibuat segera setelah ResourceDictionary dibuat. Ini terjadi karena x:Name memberi tahu platform bahwa aplikasi Anda memerlukan akses bidang ke sumber daya ini, sehingga platform perlu membuat sesuatu untuk membuat referensi.

ResourceDictionary dalam UserControl

ResourceDictionary yang ditentukan di dalam UserControl membawa penalti. Platform ini membuat salinan ResourceDictionary tersebut untuk setiap instans UserControl. Jika Anda memiliki UserControl yang banyak digunakan, maka pindahkan ResourceDictionary keluar dari UserControl dan letakkan di tingkat halaman.

Cakupan Resource dan ResourceDictionary

Jika halaman mereferensikan kontrol pengguna atau sumber daya yang ditentukan dalam file yang berbeda, maka kerangka kerja juga mengurai file tersebut.

Di sini, karena InitialPage.xaml menggunakan satu sumber daya dari ExampleResourceDictionary.xaml, seluruh ExampleResourceDictionary.xaml harus diurai saat startup.

InitialPage.xaml.

<Page x:Class="ExampleNamespace.InitialPage" ...>
    <Page.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="ExampleResourceDictionary.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Page.Resources>

    <Grid>
        <TextBox Foreground="{StaticResource TextBrush}"/>
    </Grid>
</Page>

ExampleResourceDictionary.xaml.

<ResourceDictionary>
    <SolidColorBrush x:Key="TextBrush" Color="#FF3F42CC"/>

    <!--This ResourceDictionary contains many other resources that
        are used in the app, but are not needed during startup.-->
</ResourceDictionary>

Jika Anda menggunakan sumber daya di banyak halaman di seluruh aplikasi, maka menyimpannya di App.xaml adalah praktik yang baik, dan menghindari duplikasi. Tetapi App.xaml diuraikan pada startup aplikasi sehingga sumber daya apa pun yang hanya digunakan dalam satu halaman (kecuali halaman tersebut adalah halaman awal) harus dimasukkan ke dalam sumber daya lokal halaman. Contoh ini menunjukkan App.xaml yang berisi sumber daya yang hanya digunakan oleh satu halaman (itu bukan halaman awal). Ini tidak perlu meningkatkan waktu pengaktifan aplikasi.

App.xaml

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Application ...>
     <Application.Resources>
        <SolidColorBrush x:Key="DefaultAppTextBrush" Color="#FF3F42CC"/>
        <SolidColorBrush x:Key="InitialPageTextBrush" Color="#FF3F42CC"/>
        <SolidColorBrush x:Key="SecondPageTextBrush" Color="#FF3F42CC"/>
        <SolidColorBrush x:Key="ThirdPageTextBrush" Color="#FF3F42CC"/>
    </Application.Resources>
</Application>

InitialPage.xaml.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page x:Class="ExampleNamespace.InitialPage" ...>
    <StackPanel>
        <TextBox Foreground="{StaticResource InitialPageTextBrush}"/>
    </StackPanel>
</Page>

SecondPage.xaml.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page x:Class="ExampleNamespace.SecondPage" ...>
    <StackPanel>
        <Button Content="Submit" Foreground="{StaticResource SecondPageTextBrush}"/>
    </StackPanel>
</Page>

Untuk membuat contoh ini lebih efisien, pindah SecondPageTextBrush ke SecondPage.xaml dan pindah ThirdPageTextBrush ke ThirdPage.xaml. InitialPageTextBrush dapat tetap berada di App.xaml karena sumber daya aplikasi harus diurai pada startup aplikasi dalam hal apa pun.

Mengonsolidasikan beberapa kuas yang terlihat sama ke dalam satu sumber daya

Platform XAML mencoba men-cache objek yang umum digunakan sehingga dapat digunakan kembali sesering mungkin. Tetapi XAML tidak dapat dengan mudah mengetahui apakah kuas yang dinyatakan dalam satu bagian markup sama dengan kuas yang dideklarasikan di yang lain. Contoh di sini menggunakan SolidColorBrush untuk menunjukkan, tetapi kasus ini lebih mungkin dan lebih penting dengan GradientBrush. Periksa juga kuas yang menggunakan warna yang telah ditentukan; misalnya, "Orange" dan "#FFFFA500" berwarna sama.

Efisien.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page ... >
    <StackPanel>
        <TextBlock>
            <TextBlock.Foreground>
                <SolidColorBrush Color="#FFFFA500"/>
            </TextBlock.Foreground>
        </TextBlock>
        <Button Content="Submit">
            <Button.Foreground>
                <SolidColorBrush Color="#FFFFA500"/>
            </Button.Foreground>
        </Button>
    </StackPanel>
</Page>

Untuk memperbaiki duplikasi, tentukan kuas sebagai sumber daya. Jika kontrol di halaman lain menggunakan kuas yang sama, pindahkan ke App.xaml.

Efisien.

<Page ... >
    <Page.Resources>
        <SolidColorBrush x:Key="BrandBrush" Color="#FFFFA500"/>
    </Page.Resources>

    <StackPanel>
        <TextBlock Foreground="{StaticResource BrandBrush}" />
        <Button Content="Submit" Foreground="{StaticResource BrandBrush}" />
    </StackPanel>
</Page>

Meminimalkan overdrawing

Overdrawing terjadi di mana lebih dari satu objek digambar dalam piksel layar yang sama. Perhatikan bahwa terkadang ada trade-off antara panduan ini dan keinginan untuk meminimalkan jumlah elemen.

Gunakan Debug Pengaturan. IsOverdrawHeatMapEnabled sebagai diagnostik visual. Anda mungkin menemukan objek yang digambar yang tidak Anda ketahui berada di tempat kejadian.

Elemen transparan atau tersembunyi

Jika elemen tidak terlihat karena transparan atau tersembunyi di belakang elemen lain, dan tidak berkontribusi pada tata letak, maka hapus. Jika elemen tidak terlihat dalam status visual awal tetapi terlihat dalam status visual lainnya, maka gunakan x:Load untuk mengontrol statusnya atau atur Visibilitas ke Ciutkan pada elemen itu sendiri dan ubah nilai menjadi Terlihat dalam status yang sesuai. Akan ada pengecualian untuk heuristik ini: secara umum, nilai yang dimiliki properti di sebagian besar status visual paling baik diatur secara lokal pada elemen .

Elemen komposit

Gunakan elemen komposit alih-alih melapisi beberapa elemen untuk membuat efek. Dalam contoh ini, hasilnya adalah bentuk dua-ton di mana bagian atas berwarna hitam (dari latar belakang Kisi) dan bagian bawah berwarna abu-abu (dari persegi panjang putih semi transparan yang dicampur alfa di atas latar belakang hitam Kisi). Di sini, 150% piksel yang diperlukan untuk mencapai hasilnya sedang diisi.

Efisien.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid Background="Black">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Rectangle Grid.Row="1" Fill="White" Opacity=".5"/>
</Grid>

Efisien.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Rectangle Fill="Black"/>
    <Rectangle Grid.Row="1" Fill="#FF7F7F7F"/>
</Grid>

Panel tata letak

Panel tata letak dapat memiliki dua tujuan: untuk mewarnai area, dan untuk meletakkan elemen anak. Jika elemen lebih jauh kembali dalam z-order sudah mewarnai area maka panel tata letak di depan tidak perlu melukis area tersebut: sebaliknya hanya dapat fokus pada meletakkan anak-anaknya. Berikut adalah contoh.

Efisien.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<GridView Background="Blue">
    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid Background="Blue"/>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

Efisien.

<GridView Background="Blue">
    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid/>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

Jika Grid harus dapat diuji, maka tetapkan nilai latar belakang transparan di atasnya.

Batas

Gunakan elemen Batas untuk menggambar batas di sekitar objek. Dalam contoh ini, Kisi digunakan sebagai batas makeshift di sekitar TextBox. Tetapi semua piksel di sel tengah terlalu fasih.

Efisien.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid Background="Blue" Width="300" Height="45">
    <Grid.RowDefinitions>
        <RowDefinition Height="5"/>
        <RowDefinition/>
        <RowDefinition Height="5"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="5"/>
        <ColumnDefinition/>
        <ColumnDefinition Width="5"/>
    </Grid.ColumnDefinitions>
    <TextBox Grid.Row="1" Grid.Column="1"></TextBox>
</Grid>

Efisien.

<Border BorderBrush="Blue" BorderThickness="5" Width="300" Height="45">
    <TextBox/>
</Border>

Margin

Waspadai margin. Dua elemen tetangga akan tumpang tindih (mungkin tidak sengaja) jika margin negatif meluas ke batas render orang lain dan menyebabkan pembongkaran berlebih.

Cache konten statis

Sumber lain dari overdrawing adalah bentuk yang terbuat dari banyak elemen yang tumpang tindih. Jika Anda mengatur CacheMode ke BitmapCache pada UIElement yang berisi bentuk komposit, maka platform merender elemen ke bitmap sekali dan kemudian menggunakan bitmap itu setiap bingkai alih-alih overdrawing.

Efisien.

<Canvas Background="White">
    <Ellipse Height="40" Width="40" Fill="Blue"/>
    <Ellipse Canvas.Left="21" Height="40" Width="40" Fill="Blue"/>
    <Ellipse Canvas.Top="13" Canvas.Left="10" Height="40" Width="40" Fill="Blue"/>
</Canvas>

Venn diagram with three solid circles

Gambar di atas adalah hasilnya, tetapi berikut adalah peta wilayah overdrawn. Warna merah yang lebih gelap menunjukkan jumlah overdraw yang lebih tinggi.

Venn diagram that shows overlapping areas

Efisien.

<Canvas Background="White" CacheMode="BitmapCache">
    <Ellipse Height="40" Width="40" Fill="Blue"/>
    <Ellipse Canvas.Left="21" Height="40" Width="40" Fill="Blue"/>
    <Ellipse Canvas.Top="13" Canvas.Left="10" Height="40" Width="40" Fill="Blue"/>
</Canvas>

Perhatikan penggunaan CacheMode. Jangan gunakan teknik ini jika salah satu sub-bentuk menganimasikan karena cache bitmap kemungkinan perlu diregenerasi setiap bingkai, mengalahkan tujuannya.

Menggunakan XBF2

XBF2 adalah representasi biner markup XAML yang menghindari semua biaya penguraian teks pada waktu proses. Ini juga mengoptimalkan biner Anda untuk pembuatan beban dan pohon, dan memungkinkan "jalur cepat" untuk jenis XAML untuk meningkatkan biaya pembuatan tumpukan dan objek, misalnya VSM, ResourceDictionary, Styles, dan sebagainya. Ini sepenuhnya dipetakan memori sehingga tidak ada jejak timbunan untuk memuat dan membaca Halaman XAML. Selain itu, ini mengurangi jejak disk halaman XAML yang disimpan dalam appx. XBF2 adalah representasi yang lebih ringkas dan dapat mengurangi jejak disk file XAML/XBF1 komparatif hingga 50%. Misalnya, aplikasi Foto bawaan melihat pengurangan sekitar 60% setelah konversi ke XBF2 turun dari sekitar ~ 1mb aset XBF1 menjadi ~400kb aset XBF2. Kami juga telah melihat manfaat aplikasi di mana saja dari 15 hingga 20% di CPU dan 10 hingga 15% dalam timbunan Win32.

Kontrol dan kamus bawaan XAML yang disediakan kerangka kerja sudah sepenuhnya mendukung XBF2. Untuk aplikasi Anda sendiri, pastikan file proyek Anda mendeklarasikan TargetPlatformVersion 8.2 atau yang lebih baru.

Untuk memeriksa apakah Anda memiliki XBF2, buka aplikasi Anda di editor biner; byte ke-12 dan ke-13 adalah 00 02 jika Anda memiliki XBF2.