Kode adaptif versi

Anda dapat berpikir tentang menulis kode adaptif mirip dengan bagaimana Anda berpikir tentang menciptakan UI adaptif. Anda dapat mendesain UI dasar anda untuk berjalan pada layar terkecil, lalu memindahkan atau menambahkan elemen ketika anda mendeteksi bahwa aplikasi anda berjalan pada layar yang lebih besar. Dengan kode adaptif, Anda menulis kode dasar anda untuk berjalan pada versi OS terendah, dan anda dapat menambahkan fitur yang dipilih sendiri ketika anda mendeteksi bahwa aplikasi anda berjalan pada versi yang lebih tinggi di mana fitur baru tersedia.

Untuk info latar belakang penting tentang ApiInformation, kontrak API, dan mengonfigurasi Visual Studio, lihat Versi aplikasi adaptif.

Pemeriksaan API runtime

Anda menggunakan Windows. Kelas Foundation.Metadata.ApiInformation dalam kondisi dalam kode Anda untuk menguji keberadaan API yang ingin Anda panggil. Kondisi ini dievaluasi di mana pun aplikasi Anda berjalan, tetapi dievaluasi hanya pada perangkat di mana API hadir dan karenanya tersedia untuk dipanggil. Ini memungkinkan Anda menulis kode adaptif versi untuk membuat aplikasi yang menggunakan API yang hanya tersedia pada versi OS tertentu.

Di sini, kami melihat contoh spesifik untuk menargetkan fitur baru di Windows Insider Preview. Untuk gambaran umum penggunaan ApiInformation, lihat Pemrograman dengan SDK ekstensi dan posting blog Mendeteksi fitur secara dinamis dengan kontrak API.

Tip

Banyak pemeriksaan API runtime dapat memengaruhi kinerja aplikasi Anda. Kami menunjukkan pemeriksaan inline dalam contoh-contoh ini. Dalam kode produksi, Anda harus melakukan pemeriksaan sekali dan menyimpan hasilnya, lalu menggunakan hasil cache di seluruh aplikasi Anda.

Skenario yang tidak didukung

Dalam kebanyakan kasus, Anda dapat mengatur Versi Minimum aplikasi ke SDK versi 10240 dan menggunakan pemeriksaan runtime untuk mengaktifkan API baru saat aplikasi Anda berjalan di versi yang lebih baru. Namun, ada beberapa kasus di mana Anda harus meningkatkan Versi Minimum aplikasi Anda untuk menggunakan fitur baru.

Anda harus meningkatkan Versi Minimum aplikasi jika menggunakan:

  • API baru yang memerlukan kemampuan yang tidak tersedia di versi sebelumnya. Anda harus meningkatkan versi minimum yang didukung menjadi versi yang menyertakan kemampuan tersebut. Untuk info selengkapnya, lihat Deklarasi kemampuan aplikasi.
  • kunci sumber daya baru ditambahkan ke generic.xaml dan tidak tersedia di versi sebelumnya. Versi generic.xaml yang digunakan saat runtime ditentukan oleh versi OS yang dijalankan perangkat. Anda tidak dapat menggunakan pemeriksaan API runtime untuk menentukan keberadaan sumber daya XAML. Jadi, Anda hanya boleh menggunakan kunci sumber daya yang tersedia dalam versi minimum yang didukung aplikasi Anda atau XAMLParseException akan menyebabkan aplikasi Anda mogok saat runtime.

Opsi kode adaptif

Ada dua cara untuk membuat kode adaptif. Dalam kebanyakan kasus, Anda menulis markup aplikasi untuk dijalankan pada versi Minimum, lalu menggunakan kode aplikasi untuk memanfaatkan fitur OS yang lebih baru saat hadir. Namun, jika Anda perlu memperbarui properti dalam status visual, dan hanya ada properti atau perubahan nilai pencacahan antara versi OS, Anda dapat membuat pemicu status yang dapat diperluas yang diaktifkan berdasarkan keberadaan API.

Di sini, kami membandingkan opsi-opsi ini.

Kode aplikasi

Kapan harus menggunakan:

  • Direkomendasikan untuk semua skenario kode adaptif kecuali untuk kasus tertentu yang didefinisikan di bawah ini untuk pemicu yang dapat diperluas.

Keuntungan:

  • Menghindari overhead pengembang / kompleksitas mengikat perbedaan API menjadi markup.

Halangan-halangan:

  • Tidak ada dukungan desainer.

Pemicu Status

Kapan harus menggunakan:

  • Gunakan ketika hanya ada properti atau perubahan enum antara versi OS yang tidak memerlukan perubahan logika, dan terhubung ke status visual.

Keuntungan:

  • Memungkinkan Anda membuat status visual tertentu yang dipicu berdasarkan keberadaan API.
  • Beberapa dukungan desainer tersedia.

Halangan-halangan:

  • Penggunaan pemicu kustom dibatasi untuk status visual, yang tidak cocok untuk tata letak adaptif yang rumit.
  • Harus menggunakan Setters untuk menentukan perubahan nilai, jadi hanya perubahan sederhana yang mungkin.
  • Pemicu status kustom cukup bertele-tele untuk disiapkan dan digunakan.

Contoh kode adaptif

Di bagian ini, kami menampilkan beberapa contoh kode adaptif yang menggunakan API yang baru di Windows 10, versi 1607 (Windows Insider Preview).

Contoh 1: Nilai enum baru

Windows 10, versi 1607 menambahkan nilai baru ke pencacahan InputScopeNameValue: ChatWithoutEmoji. Cakupan input baru ini memiliki perilaku input yang sama dengan cakupan input Chat (pemeriksaan ejaan, lengkap secara otomatis, kapitalisasi otomatis), tetapi memetakan ke keyboard sentuh tanpa tombol emoji. Ini berguna jika Anda membuat pemilih emoji Anda sendiri dan ingin menonaktifkan tombol emoji bawaan di keyboard sentuh.

Contoh ini menunjukkan cara memeriksa apakah nilai enum ChatWithoutEmoji ada dan menetapkan properti InputScope dari TextBox jika demikian. Jika tidak ada pada sistem aplikasi dijalankan, InputScope diatur ke Chat sebagai gantinya. Kode yang ditampilkan dapat ditempatkan di consructor Halaman atau penangan peristiwa Page.Loaded.

Tip

Saat Anda memeriksa API, gunakan string statis alih-alih mengandalkan fitur bahasa .NET, jika tidak, aplikasi Anda mungkin mencoba mengakses jenis yang tidak ditentukan dan macet saat runtime.

C#

// Create a TextBox control for sending messages 
// and initialize an InputScope object.
TextBox messageBox = new TextBox();
messageBox.AcceptsReturn = true;
messageBox.TextWrapping = TextWrapping.Wrap;
InputScope scope = new InputScope();
InputScopeName scopeName = new InputScopeName();

// Check that the ChatWithEmoji value is present.
// (It's present starting with Windows 10, version 1607,
//  the Target version for the app. This check returns false on earlier versions.)         
if (ApiInformation.IsEnumNamedValuePresent("Windows.UI.Xaml.Input.InputScopeNameValue", "ChatWithoutEmoji"))
{
    // Set new ChatWithoutEmoji InputScope if present.
    scopeName.NameValue = InputScopeNameValue.ChatWithoutEmoji;
}
else
{
    // Fall back to Chat InputScope.
    scopeName.NameValue = InputScopeNameValue.Chat;
}

// Set InputScope on messaging TextBox.
scope.Names.Add(scopeName);
messageBox.InputScope = scope;

// For this example, set the TextBox text to show the selected InputScope.
messageBox.Text = messageBox.InputScope.Names[0].NameValue.ToString();

// Add the TextBox to the XAML visual tree (rootGrid is defined in XAML).
rootGrid.Children.Add(messageBox);

Dalam contoh sebelumnya, TextBox dibuat dan semua properti diatur dalam kode. Namun, jika Anda memiliki XAML yang ada, dan hanya perlu mengubah properti InputScope pada sistem di mana nilai baru didukung, Anda dapat melakukannya tanpa mengubah XAML Anda, seperti yang ditunjukkan di sini. Anda mengatur nilai default ke Chat di XAML, tetapi Anda menggantinya dalam kode jika nilai ChatWithoutEmoji ada.

XAML

<TextBox x:Name="messageBox"
         AcceptsReturn="True" TextWrapping="Wrap"
         InputScope="Chat"
         Loaded="messageBox_Loaded"/>

C#

private void messageBox_Loaded(object sender, RoutedEventArgs e)
{
    if (ApiInformation.IsEnumNamedValuePresent("Windows.UI.Xaml.Input.InputScopeNameValue", "ChatWithoutEmoji"))
    {
        // Check that the ChatWithEmoji value is present.
        // (It's present starting with Windows 10, version 1607,
        //  the Target version for the app. This code is skipped on earlier versions.)
        InputScope scope = new InputScope();
        InputScopeName scopeName = new InputScopeName();
        scopeName.NameValue = InputScopeNameValue.ChatWithoutEmoji;
        // Set InputScope on messaging TextBox.
        scope.Names.Add(scopeName);
        messageBox.InputScope = scope;
    }

    // For this example, set the TextBox text to show the selected InputScope.
    // This is outside of the API check, so it will happen on all OS versions.
    messageBox.Text = messageBox.InputScope.Names[0].NameValue.ToString();
}

Sekarang setelah kita memiliki contoh konkret, mari kita lihat bagaimana pengaturan versi Target dan Minimum berlaku untuk itu.

Dalam contoh ini, Anda dapat menggunakan nilai pencacahan Obrolan di XAML, atau dalam kode tanpa pemeriksaan, karena ada di versi OS minimum yang didukung.

Jika Anda menggunakan nilai ChatWithoutEmoji di XAML, atau dalam kode tanpa pemeriksaan, itu akan mengkompilasi tanpa kesalahan karena ada di versi Target OS. Ini juga akan berjalan tanpa kesalahan pada sistem dengan versi Target OS. Namun, ketika aplikasi berjalan pada sistem dengan OS menggunakan versi Minimum, itu akan crash saat runtime karena nilai enum ChatWithoutEmoji tidak ada. Oleh karena itu, Anda harus menggunakan nilai ini hanya dalam kode, dan membungkusnya dalam pemeriksaan API runtime sehingga hanya dipanggil jika didukung pada sistem saat ini.

Contoh 2: Kontrol baru

Versi baru Windows biasanya membawa kontrol baru ke permukaan API UWP yang membawa fungsionalitas baru ke platform. Untuk memanfaatkan keberadaan kontrol baru, gunakan metode ApiInformation.IsTypePresent .

Windows 10, versi 1607 memperkenalkan kontrol media baru yang disebut MediaPlayerElement. Kontrol ini dibangun di atas kelas MediaPlayer , sehingga membawa fitur seperti kemampuan untuk dengan mudah mengikat ke audio latar belakang, dan memanfaatkan peningkatan arsitektur dalam tumpukan media.

Namun, jika aplikasi berjalan pada perangkat yang menjalankan versi Windows 10 lebih lama dari versi 1607, Anda harus menggunakan kontrol MediaElement alih-alih kontrol MediaPlayerElement yang baru. Anda dapat menggunakan metode ApiInformation.IsTypePresent untuk memeriksa keberadaan kontrol MediaPlayerElement saat runtime, dan memuat kontrol mana pun yang cocok untuk sistem tempat aplikasi berjalan.

Contoh ini menunjukkan cara membuat aplikasi yang menggunakan MediaPlayerElement baru atau MediaElement lama tergantung pada apakah jenis MediaPlayerElement hadir. Dalam kode ini, Anda menggunakan kelas UserControl untuk komponenisasi kontrol dan UI dan kode terkait mereka sehingga Anda dapat menggantinya masuk dan keluar berdasarkan versi OS. Sebagai alternatif, Anda dapat menggunakan kontrol khusus, yang menyediakan lebih banyak fungsionalitas dan perilaku khusus daripada yang diperlukan untuk contoh sederhana ini.

MediaPlayerUserControl

Ini MediaPlayerUserControl merangkum MediaPlayerElement dan beberapa tombol yang digunakan untuk melewati bingkai media demi bingkai. UserControl memungkinkan Anda memperlakukan kontrol ini sebagai entitas tunggal dan membuatnya lebih mudah untuk beralih dengan MediaElement pada sistem yang lebih lama. Kontrol pengguna ini harus digunakan hanya pada sistem di mana MediaPlayerElement hadir, sehingga Anda tidak menggunakan pemeriksaan ApiInformation dalam kode di dalam kontrol pengguna ini.

Catatan

Untuk menjaga contoh ini sederhana dan terfokus, tombol langkah bingkai ditempatkan di luar pemutar media. Untuk pengalaman pengguna yang lebih baik, Anda harus menyesuaikan MediaTransportControls untuk menyertakan tombol kustom Anda. Lihat Kontrol transportasi kustom untuk info selengkapnya.

XAML

<UserControl
    x:Class="MediaApp.MediaPlayerUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MediaApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

    <Grid x:Name="MPE_grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" 
                    HorizontalAlignment="Center" Grid.Row="1">
            <RepeatButton Click="StepBack_Click" Content="Step Back"/>
            <RepeatButton Click="StepForward_Click" Content="Step Forward"/>
        </StackPanel>
    </Grid>
</UserControl>

C#

using System;
using Windows.Media.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace MediaApp
{
    public sealed partial class MediaPlayerUserControl : UserControl
    {
        public MediaPlayerUserControl()
        {
            this.InitializeComponent();
            
            // The markup code compiler runs against the Minimum OS version so MediaPlayerElement must be created in app code
            MPE = new MediaPlayerElement();
            Uri videoSource = new Uri("ms-appx:///Assets/UWPDesign.mp4");
	        MPE.Source = MediaSource.CreateFromUri(videoSource);
	        MPE.AreTransportControlsEnabled = true;
            MPE.MediaPlayer.AutoPlay = true;

            // Add MediaPlayerElement to the Grid
            MPE_grid.Children.Add(MPE);

        }

        private void StepForward_Click(object sender, RoutedEventArgs e)
        {
            // Step forward one frame, only available using MediaPlayerElement.
            MPE.MediaPlayer.StepForwardOneFrame();
        }

        private void StepBack_Click(object sender, RoutedEventArgs e)
        {
            // Step forward one frame, only available using MediaPlayerElement.
            MPE.MediaPlayer.StepForwardOneFrame();
        }
    }
}

MediaElementUserControl

Ini MediaElementUserControl merangkum kontrol MediaElement .

XAML

<UserControl
    x:Class="MediaApp.MediaElementUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MediaApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

    <Grid>
        <MediaElement AreTransportControlsEnabled="True" 
                      Source="Assets/UWPDesign.mp4"/>
    </Grid>
</UserControl>

Catatan

Halaman kode untuk MediaElementUserControl hanya berisi kode yang dihasilkan, jadi tidak ditampilkan.

Menginisialisasi kontrol berdasarkan IsTypePresent

Saat runtime, Anda memanggil ApiInformation.IsTypePresent untuk memeriksa MediaPlayerElement. Jika ada, Anda memuat MediaPlayerUserControl, jika tidak, Anda memuat MediaElementUserControl.

C#

public MainPage()
{
    this.InitializeComponent();

    UserControl mediaControl;

    // Check for presence of type MediaPlayerElement.
    if (ApiInformation.IsTypePresent("Windows.UI.Xaml.Controls.MediaPlayerElement"))
    {
        mediaControl = new MediaPlayerUserControl();
    }
    else
    {
        mediaControl = new MediaElementUserControl();
    }

    // Add mediaControl to XAML visual tree (rootGrid is defined in XAML).
    rootGrid.Children.Add(mediaControl);
}

Penting

Ingat bahwa pemeriksaan ini hanya mengatur mediaControl objek ke salah satu MediaPlayerUserControl atau MediaElementUserControl. Anda perlu melakukan pemeriksaan bersyarat ini di tempat lain dalam kode Anda yang perlu Anda tentukan apakah akan menggunakan API MediaPlayerElement atau MediaElement. Anda harus melakukan pemeriksaan sekali dan menyimpan hasilnya, lalu menggunakan hasil cache di seluruh aplikasi Anda.

Contoh pemicu status

Pemicu status yang dapat diperluas memungkinkan Anda menggunakan markup dan kode bersama-sama untuk memicu perubahan status visual berdasarkan kondisi yang Anda periksa kodenya; dalam hal ini, keberadaan API tertentu. Kami tidak merekomendasikan pemicu status untuk skenario kode adaptif umum karena overhead yang terlibat, dan pembatasan hanya untuk status visual.

Anda harus menggunakan pemicu status untuk kode adaptif hanya jika Anda memiliki perubahan UI kecil antara versi OS yang berbeda yang tidak akan memengaruhi UI yang tersisa, seperti properti atau perubahan nilai pencacahan pada kontrol.

Contoh 1: Properti baru

Langkah pertama dalam menyiapkan pemicu status yang dapat diperluas adalah mensubklasifikasi kelas StateTriggerBase untuk membuat pemicu kustom yang akan aktif berdasarkan keberadaan API. Contoh ini menunjukkan pemicu yang diaktifkan jika kehadiran properti cocok dengan variabel yang _isPresent ditetapkan dalam XAML.

C#

class IsPropertyPresentTrigger : StateTriggerBase
{
    public string TypeName { get; set; }
    public string PropertyName { get; set; }

    private Boolean _isPresent;
    private bool? _isPropertyPresent = null;

    public Boolean IsPresent
    {
        get { return _isPresent; }
        set
        {
            _isPresent = value;
            if (_isPropertyPresent == null)
            {
                // Call into ApiInformation method to determine if property is present.
                _isPropertyPresent =
                ApiInformation.IsPropertyPresent(TypeName, PropertyName);
            }

            // If the property presence matches _isPresent then the trigger will be activated;
            SetActive(_isPresent == _isPropertyPresent);
        }
    }
}

Langkah selanjutnya adalah menyiapkan pemicu status visual di XAML sehingga dua status visual yang berbeda menghasilkan berdasarkan keberadaan API.

Windows 10, versi 1607 memperkenalkan properti baru pada kelas FrameworkElement yang disebut AllowFocusOnInteraction yang menentukan apakah kontrol mengambil fokus ketika pengguna berinteraksi dengannya. Ini berguna jika Anda ingin tetap fokus pada kotak teks untuk entri data (dan menjaga keyboard sentuh tetap ditampilkan) saat pengguna mengklik tombol.

Pemicu dalam contoh ini memeriksa apakah properti ada. Jika properti hadir, itu mengatur properti AllowFocusOnInteraction pada Tombol ke false; jika properti tidak ada, Tombol mempertahankan status aslinya. TextBox disertakan untuk memudahkan melihat efek properti ini saat Anda menjalankan kode.

XAML

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
        <TextBox Width="300" Height="36"/>
        <!-- Button to set the new property on. -->
        <Button x:Name="testButton" Content="Test" Margin="12"/>
    </StackPanel>

    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="propertyPresentStateGroup">
            <VisualState>
                <VisualState.StateTriggers>
                    <!--Trigger will activate if the AllowFocusOnInteraction property is present-->
                    <local:IsPropertyPresentTrigger 
                        TypeName="Windows.UI.Xaml.FrameworkElement" 
                        PropertyName="AllowFocusOnInteraction" IsPresent="True"/>
                </VisualState.StateTriggers>
                <VisualState.Setters>
                    <Setter Target="testButton.AllowFocusOnInteraction" 
                            Value="False"/>
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Grid>

Contoh 2: Nilai enum baru

Contoh ini menunjukkan cara mengatur nilai pencacahan yang berbeda berdasarkan apakah ada nilai. Ini menggunakan pemicu status kustom untuk mencapai hasil yang sama dengan contoh obrolan sebelumnya. Dalam contoh ini, Anda menggunakan cakupan input ChatWithoutEmoji baru jika perangkat berjalan Windows 10, versi 1607, jika tidak, cakupan input Chat digunakan. Status visual yang menggunakan pemicu ini diatur dalam gaya if-else di mana cakupan input dipilih berdasarkan keberadaan nilai enum baru.

C#

class IsEnumPresentTrigger : StateTriggerBase
{
    public string EnumTypeName { get; set; }
    public string EnumValueName { get; set; }

    private Boolean _isPresent;
    private bool? _isEnumValuePresent = null;

    public Boolean IsPresent
    {
        get { return _isPresent; }
        set
        {
            _isPresent = value;

            if (_isEnumValuePresent == null)
            {
                // Call into ApiInformation method to determine if value is present.
                _isEnumValuePresent =
                ApiInformation.IsEnumNamedValuePresent(EnumTypeName, EnumValueName);
            }

            // If the property presence matches _isPresent then the trigger will be activated;
            SetActive(_isPresent == _isEnumValuePresent);
        }
    }
}

XAML

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <TextBox x:Name="messageBox"
     AcceptsReturn="True" TextWrapping="Wrap"/>


    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="EnumPresentStates">
            <!--if-->
            <VisualState x:Name="isPresent">
                <VisualState.StateTriggers>
                    <local:IsEnumPresentTrigger 
                        EnumTypeName="Windows.UI.Xaml.Input.InputScopeNameValue" 
                        EnumValueName="ChatWithoutEmoji" IsPresent="True"/>
                </VisualState.StateTriggers>
                <VisualState.Setters>
                    <Setter Target="messageBox.InputScope" Value="ChatWithoutEmoji"/>
                </VisualState.Setters>
            </VisualState>
            <!--else-->
            <VisualState x:Name="isNotPresent">
                <VisualState.StateTriggers>
                    <local:IsEnumPresentTrigger 
                        EnumTypeName="Windows.UI.Xaml.Input.InputScopeNameValue" 
                        EnumValueName="ChatWithoutEmoji" IsPresent="False"/>
                </VisualState.StateTriggers>
                <VisualState.Setters>
                    <Setter Target="messageBox.InputScope" Value="Chat"/>
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Grid>