Bagian 3. Ekstensi Markup XAML
Ekstensi markup XAML merupakan fitur penting di XAML yang memungkinkan properti diatur ke objek atau nilai yang dirujuk secara tidak langsung dari sumber lain. Ekstensi markup XAML sangat penting untuk berbagi objek, dan mereferensikan konstanta yang digunakan di seluruh aplikasi, tetapi mereka menemukan utilitas terbesar mereka dalam pengikatan data.
Ekstensi Markup XAML
Secara umum, Anda menggunakan XAML untuk mengatur properti objek ke nilai eksplisit, seperti string, angka, anggota enumerasi, atau string yang dikonversi ke nilai di belakang layar.
Namun, terkadang, properti harus mereferensikan nilai yang ditentukan di tempat lain, atau yang mungkin memerlukan sedikit pemrosesan berdasarkan kode pada runtime. Untuk tujuan ini, ekstensi markup XAML tersedia.
Ekstensi markup XAML ini bukan ekstensi XML. XAML sepenuhnya legal XML. Mereka disebut "ekstensi" karena didukung oleh kode di kelas yang mengimplementasikan IMarkupExtension
. Anda dapat menulis ekstensi markup kustom Anda sendiri.
Dalam banyak kasus, ekstensi markup XAML langsung dapat dikenali dalam file XAML karena muncul sebagai pengaturan atribut yang dibatasi oleh kurung kurawal: { dan }, tetapi terkadang ekstensi markup muncul dalam markup sebagai elemen konvensional.
Sumber Daya Bersama
Beberapa halaman XAML berisi beberapa tampilan dengan properti yang diatur ke nilai yang sama. Misalnya, banyak pengaturan properti untuk objek ini Button
sama:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SharedResourcesPage"
Title="Shared Resources Page">
<StackLayout>
<Button Text="Do this!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
BorderWidth="3"
Rotation="-15"
TextColor="Red"
FontSize="24" />
<Button Text="Do that!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
BorderWidth="3"
Rotation="-15"
TextColor="Red"
FontSize="24" />
<Button Text="Do the other thing!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
BorderWidth="3"
Rotation="-15"
TextColor="Red"
FontSize="24" />
</StackLayout>
</ContentPage>
Jika salah satu properti ini perlu diubah, Anda mungkin lebih suka membuat perubahan hanya sekali daripada tiga kali. Jika ini adalah kode, Anda mungkin akan menggunakan konstanta dan objek baca-saja statis untuk membantu menjaga nilai tersebut tetap konsisten dan mudah dimodifikasi.
Di XAML, salah satu solusi populer adalah menyimpan nilai atau objek tersebut dalam kamus sumber daya. Kelas VisualElement
mendefinisikan properti bernama Resources
jenis ResourceDictionary
, yang merupakan kamus dengan kunci jenis string
dan nilai jenis object
. Anda dapat memasukkan objek ke dalam kamus ini lalu mereferensikannya dari markup, semuanya di XAML.
Untuk menggunakan kamus sumber daya pada halaman, sertakan sepasang Resources
tag elemen properti. Paling nyaman untuk menempatkan ini di bagian atas halaman:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SharedResourcesPage"
Title="Shared Resources Page">
<ContentPage.Resources>
</ContentPage.Resources>
...
</ContentPage>
Penting juga untuk secara eksplisit menyertakan ResourceDictionary
tag:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SharedResourcesPage"
Title="Shared Resources Page">
<ContentPage.Resources>
<ResourceDictionary>
</ResourceDictionary>
</ContentPage.Resources>
...
</ContentPage>
Sekarang objek dan nilai dari berbagai jenis dapat ditambahkan ke kamus sumber daya. Jenis-jenis ini harus dapat diinstansiasi. Mereka tidak bisa menjadi kelas abstrak, misalnya. Jenis-jenis ini juga harus memiliki konstruktor tanpa parameter publik. Setiap item memerlukan kunci kamus yang ditentukan dengan x:Key
atribut . Contohnya:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SharedResourcesPage"
Title="Shared Resources Page">
<ContentPage.Resources>
<ResourceDictionary>
<LayoutOptions x:Key="horzOptions"
Alignment="Center" />
<LayoutOptions x:Key="vertOptions"
Alignment="Center"
Expands="True" />
</ResourceDictionary>
</ContentPage.Resources>
...
</ContentPage>
Kedua item ini adalah nilai dari jenis LayoutOptions
struktur , dan masing-masing memiliki kunci unik dan satu atau dua kumpulan properti. Dalam kode dan markup, jauh lebih umum untuk menggunakan bidang LayoutOptions
statis , tetapi di sini lebih nyaman untuk mengatur properti.
Sekarang perlu untuk mengatur HorizontalOptions
properti dan VerticalOptions
tombol ini ke sumber daya ini, dan itu dilakukan dengan StaticResource
ekstensi markup XAML:
<Button Text="Do this!"
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="3"
Rotation="-15"
TextColor="Red"
FontSize="24" />
Ekstensi StaticResource
markup selalu dibatasi dengan kurung kurawal, dan menyertakan kunci kamus.
Nama StaticResource
membedakannya dari DynamicResource
, yang Xamarin.Forms juga mendukung. DynamicResource
adalah untuk kunci kamus yang terkait dengan nilai yang mungkin berubah selama runtime, sementara StaticResource
mengakses elemen dari kamus hanya sekali ketika elemen di halaman dibangun.
BorderWidth
Untuk properti , perlu untuk menyimpan ganda dalam kamus. XAML dengan mudah mendefinisikan tag untuk jenis data umum seperti x:Double
dan x:Int32
:
<ContentPage.Resources>
<ResourceDictionary>
<LayoutOptions x:Key="horzOptions"
Alignment="Center" />
<LayoutOptions x:Key="vertOptions"
Alignment="Center"
Expands="True" />
<x:Double x:Key="borderWidth">
3
</x:Double>
</ResourceDictionary>
</ContentPage.Resources>
Anda tidak perlu meletakkannya pada tiga baris. Entri kamus untuk sudut rotasi ini hanya memakan waktu satu baris:
<ContentPage.Resources>
<ResourceDictionary>
<LayoutOptions x:Key="horzOptions"
Alignment="Center" />
<LayoutOptions x:Key="vertOptions"
Alignment="Center"
Expands="True" />
<x:Double x:Key="borderWidth">
3
</x:Double>
<x:Double x:Key="rotationAngle">-15</x:Double>
</ResourceDictionary>
</ContentPage.Resources>
Kedua sumber daya tersebut dapat dirujuk dengan cara yang sama seperti LayoutOptions
nilai:
<Button Text="Do this!"
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="Red"
FontSize="24" />
Untuk sumber daya jenis Color
, Anda dapat menggunakan representasi string yang sama dengan yang Anda gunakan saat secara langsung menetapkan atribut jenis ini. Konverter jenis dipanggil saat sumber daya dibuat. Berikut adalah sumber daya jenis Color
:
<Color x:Key="textColor">Red</Color>
Seringkali, program mengatur FontSize
properti ke anggota NamedSize
enumerasi seperti Large
. Kelas FontSizeConverter
bekerja di belakang layar untuk mengonversinya menjadi nilai yang bergantung pada platform menggunakan metode .Device.GetNamedSized
Namun, saat menentukan sumber daya ukuran font, lebih masuk akal untuk menggunakan nilai numerik, yang x:Double
ditunjukkan di sini sebagai jenis:
<x:Double x:Key="fontSize">24</x:Double>
Sekarang semua properti kecuali Text
ditentukan oleh pengaturan sumber daya:
<Button Text="Do this!"
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="{StaticResource textColor}"
FontSize="{StaticResource fontSize}" />
Anda juga dapat menggunakan OnPlatform
dalam kamus sumber daya untuk menentukan nilai yang berbeda untuk platform. Berikut adalah bagaimana OnPlatform
objek dapat menjadi bagian dari kamus sumber daya untuk warna teks yang berbeda:
<OnPlatform x:Key="textColor"
x:TypeArguments="Color">
<On Platform="iOS" Value="Red" />
<On Platform="Android" Value="Aqua" />
<On Platform="UWP" Value="#80FF80" />
</OnPlatform>
Perhatikan bahwa OnPlatform
mendapatkan atribut karena x:Key
merupakan objek dalam kamus dan x:TypeArguments
atribut karena merupakan kelas generik. Atribut iOS
, Android
, dan UWP
dikonversi ke Color
nilai saat objek diinisialisasi.
Berikut adalah file XAML lengkap akhir dengan tiga tombol yang mengakses enam nilai bersama:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SharedResourcesPage"
Title="Shared Resources Page">
<ContentPage.Resources>
<ResourceDictionary>
<LayoutOptions x:Key="horzOptions"
Alignment="Center" />
<LayoutOptions x:Key="vertOptions"
Alignment="Center"
Expands="True" />
<x:Double x:Key="borderWidth">3</x:Double>
<x:Double x:Key="rotationAngle">-15</x:Double>
<OnPlatform x:Key="textColor"
x:TypeArguments="Color">
<On Platform="iOS" Value="Red" />
<On Platform="Android" Value="Aqua" />
<On Platform="UWP" Value="#80FF80" />
</OnPlatform>
<x:Double x:Key="fontSize">24</x:Double>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<Button Text="Do this!"
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="{StaticResource textColor}"
FontSize="{StaticResource fontSize}" />
<Button Text="Do that!"
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="{StaticResource textColor}"
FontSize="{StaticResource fontSize}" />
<Button Text="Do the other thing!"
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="{StaticResource textColor}"
FontSize="{StaticResource fontSize}" />
</StackLayout>
</ContentPage>
Cuplikan layar memverifikasi gaya yang konsisten, dan gaya yang bergantung pada platform:
Meskipun paling umum untuk menentukan Resources
koleksi di bagian atas halaman, perlu diingat bahwa Resources
properti ditentukan oleh VisualElement
, dan Anda dapat memiliki Resources
koleksi pada elemen lain di halaman. Misalnya, coba tambahkan ke StackLayout
dalam contoh ini:
<StackLayout>
<StackLayout.Resources>
<ResourceDictionary>
<Color x:Key="textColor">Blue</Color>
</ResourceDictionary>
</StackLayout.Resources>
...
</StackLayout>
Anda akan menemukan bahwa warna teks tombol sekarang berwarna biru. Pada dasarnya, setiap kali pengurai XAML menemukan StaticResource
ekstensi markup, ia mencari pohon visual dan menggunakan yang pertama ResourceDictionary
yang ditemuinya yang berisi kunci tersebut.
Salah satu jenis objek yang paling umum disimpan dalam kamus sumber daya adalah Xamarin.FormsStyle
, yang menentukan kumpulan pengaturan properti. Gaya dibahas dalam artikel Gaya.
Terkadang pengembang baru dengan XAML bertanya-tanya apakah mereka dapat menempatkan elemen visual seperti Label
atau Button
di ResourceDictionary
. Meskipun itu pasti mungkin, itu tidak masuk akal. Tujuannya ResourceDictionary
adalah untuk berbagi objek. Elemen visual tidak dapat dibagikan. Instans yang sama tidak dapat muncul dua kali pada satu halaman.
Ekstensi x:Static Markup
Terlepas dari kesamaan nama mereka, x:Static
dan StaticResource
sangat berbeda. StaticResource
mengembalikan objek dari kamus sumber daya saat x:Static
mengakses salah satu hal berikut:
- bidang statis publik
- properti statis publik
- bidang konstanta publik
- anggota enumerasi.
StaticResource
Ekstensi markup didukung oleh implementasi XAML yang menentukan kamus sumber daya, sementara x:Static
merupakan bagian intrinsik dari XAML, seperti x
yang diungkapkan awalan.
Berikut adalah beberapa contoh yang menunjukkan bagaimana x:Static
dapat secara eksplisit mereferensikan bidang statis dan anggota enumerasi:
<Label Text="Hello, XAML!"
VerticalOptions="{x:Static LayoutOptions.Start}"
HorizontalTextAlignment="{x:Static TextAlignment.Center}"
TextColor="{x:Static Color.Aqua}" />
Sejauh ini, ini tidak terlalu mengesankan. x:Static
Tetapi ekstensi markup juga dapat mereferensikan bidang statis atau properti dari kode Anda sendiri. Misalnya, berikut adalah AppConstants
kelas yang berisi beberapa bidang statis yang mungkin ingin Anda gunakan di beberapa halaman di seluruh aplikasi:
using System;
using Xamarin.Forms;
namespace XamlSamples
{
static class AppConstants
{
public static readonly Thickness PagePadding;
public static readonly Font TitleFont;
public static readonly Color BackgroundColor = Color.Aqua;
public static readonly Color ForegroundColor = Color.Brown;
static AppConstants()
{
switch (Device.RuntimePlatform)
{
case Device.iOS:
PagePadding = new Thickness(5, 20, 5, 0);
TitleFont = Font.SystemFontOfSize(35, FontAttributes.Bold);
break;
case Device.Android:
PagePadding = new Thickness(5, 0, 5, 0);
TitleFont = Font.SystemFontOfSize(40, FontAttributes.Bold);
break;
case Device.UWP:
PagePadding = new Thickness(5, 0, 5, 0);
TitleFont = Font.SystemFontOfSize(50, FontAttributes.Bold);
break;
}
}
}
}
Untuk mereferensikan bidang statis kelas ini dalam file XAML, Anda memerlukan beberapa cara untuk menunjukkan dalam file XAML tempat file ini berada. Anda melakukan ini dengan deklarasi namespace XML.
Ingat bahwa file XAML yang dibuat sebagai bagian dari templat XAML standar Xamarin.Forms berisi dua deklarasi namespace XML: satu untuk mengakses Xamarin.Forms kelas dan yang lain untuk mereferensikan tag dan atribut intrinsik ke XAML:
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Anda memerlukan deklarasi namespace XML tambahan untuk mengakses kelas lain. Setiap deklarasi namespace XML tambahan menentukan awalan baru. Untuk mengakses kelas lokal ke pustaka .NET Standard aplikasi bersama, seperti AppConstants
, pemrogram XAML sering menggunakan awalan local
. Deklarasi namespace harus menunjukkan nama namespace CLR (Common Language Runtime), juga dikenal sebagai nama namespace .NET, yang merupakan nama yang muncul dalam definisi C# namespace
atau dalam direktif using
:
xmlns:local="clr-namespace:XamlSamples"
Anda juga dapat menentukan deklarasi namespace XML untuk namespace layanan .NET di rakitan apa pun yang direferensikan pustaka .NET Standard. Misalnya, berikut adalah sys
awalan untuk namespace .NET System
standar, yang ada di rakitan netstandard . Karena ini adalah rakitan lain, Anda juga harus menentukan nama rakitan, dalam hal ini netstandard:
xmlns:sys="clr-namespace:System;assembly=netstandard"
Perhatikan bahwa kata kunci clr-namespace
diikuti oleh titik dua dan kemudian nama namespace layanan .NET, diikuti dengan titik koma, kata kunci assembly
, tanda sama dengan, dan nama rakitan.
Ya, titik dua mengikuti clr-namespace
tetapi tanda sama dengan mengikuti assembly
. Sintaks didefinisikan dengan cara ini dengan sengaja: Sebagian besar deklarasi namespace XML mereferensikan URI yang memulai nama skema URI seperti http
, yang selalu diikuti oleh titik dua. Bagian clr-namespace
dari string ini dimaksudkan untuk meniluki konvensi tersebut.
Kedua deklarasi namespace ini disertakan dalam sampel StaticConstantsPage . Perhatikan bahwa BoxView
dimensi diatur ke Math.PI
dan Math.E
, tetapi diskalakan oleh faktor 100:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples"
xmlns:sys="clr-namespace:System;assembly=netstandard"
x:Class="XamlSamples.StaticConstantsPage"
Title="Static Constants Page"
Padding="{x:Static local:AppConstants.PagePadding}">
<StackLayout>
<Label Text="Hello, XAML!"
TextColor="{x:Static local:AppConstants.BackgroundColor}"
BackgroundColor="{x:Static local:AppConstants.ForegroundColor}"
Font="{x:Static local:AppConstants.TitleFont}"
HorizontalOptions="Center" />
<BoxView WidthRequest="{x:Static sys:Math.PI}"
HeightRequest="{x:Static sys:Math.E}"
Color="{x:Static local:AppConstants.ForegroundColor}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Scale="100" />
</StackLayout>
</ContentPage>
Ukuran yang dihasilkan BoxView
relatif terhadap layar bergantung pada platform:
Ekstensi Markup Standar Lainnya
Beberapa ekstensi markup intrinsik ke XAML dan didukung dalam Xamarin.Forms file XAML. Beberapa di antaranya tidak sering digunakan tetapi sangat penting ketika Anda membutuhkannya:
- Jika properti memiliki non-nilai
null
secara default tetapi Anda ingin mengaturnya kenull
, atur ke{x:Null}
ekstensi markup. - Jika properti berjenis
Type
, Anda dapat menetapkannya keType
objek menggunakan ekstensi{x:Type someClass}
markup . - Anda dapat menentukan array di XAML menggunakan
x:Array
ekstensi markup. Ekstensi markup ini memiliki atribut yang diperlukan bernamaType
yang menunjukkan jenis elemen dalam array. - Ekstensi
Binding
markup dibahas di Bagian 4. Dasar-Dasar Pengikatan Data. - Ekstensi
RelativeSource
markup dibahas dalam Pengikatan Relatif.
Ekstensi Markup ConstraintExpression
Ekstensi markup dapat memiliki properti, tetapi tidak diatur seperti atribut XML. Dalam ekstensi markup, pengaturan properti dipisahkan oleh koma, dan tidak ada tanda kutip yang muncul dalam kurung kurawal.
Ini dapat diilustrasikan dengan Xamarin.Forms ekstensi markup bernama ConstraintExpression
, yang digunakan dengan RelativeLayout
kelas . Anda dapat menentukan lokasi atau ukuran tampilan anak sebagai konstanta, atau relatif terhadap tampilan induk atau nama lainnya. Sintaks ConstraintExpression
memungkinkan Anda mengatur posisi atau ukuran tampilan menggunakan Factor
satu kali properti tampilan lain, ditambah Constant
. Apa pun yang lebih kompleks dari yang membutuhkan kode.
Berikut adalah contohnya:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.RelativeLayoutPage"
Title="RelativeLayout Page">
<RelativeLayout>
<!-- Upper left -->
<BoxView Color="Red"
RelativeLayout.XConstraint=
"{ConstraintExpression Type=Constant,
Constant=0}"
RelativeLayout.YConstraint=
"{ConstraintExpression Type=Constant,
Constant=0}" />
<!-- Upper right -->
<BoxView Color="Green"
RelativeLayout.XConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Width,
Factor=1,
Constant=-40}"
RelativeLayout.YConstraint=
"{ConstraintExpression Type=Constant,
Constant=0}" />
<!-- Lower left -->
<BoxView Color="Blue"
RelativeLayout.XConstraint=
"{ConstraintExpression Type=Constant,
Constant=0}"
RelativeLayout.YConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Height,
Factor=1,
Constant=-40}" />
<!-- Lower right -->
<BoxView Color="Yellow"
RelativeLayout.XConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Width,
Factor=1,
Constant=-40}"
RelativeLayout.YConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Height,
Factor=1,
Constant=-40}" />
<!-- Centered and 1/3 width and height of parent -->
<BoxView x:Name="oneThird"
Color="Red"
RelativeLayout.XConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Width,
Factor=0.33}"
RelativeLayout.YConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Height,
Factor=0.33}"
RelativeLayout.WidthConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Width,
Factor=0.33}"
RelativeLayout.HeightConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Height,
Factor=0.33}" />
<!-- 1/3 width and height of previous -->
<BoxView Color="Blue"
RelativeLayout.XConstraint=
"{ConstraintExpression Type=RelativeToView,
ElementName=oneThird,
Property=X}"
RelativeLayout.YConstraint=
"{ConstraintExpression Type=RelativeToView,
ElementName=oneThird,
Property=Y}"
RelativeLayout.WidthConstraint=
"{ConstraintExpression Type=RelativeToView,
ElementName=oneThird,
Property=Width,
Factor=0.33}"
RelativeLayout.HeightConstraint=
"{ConstraintExpression Type=RelativeToView,
ElementName=oneThird,
Property=Height,
Factor=0.33}" />
</RelativeLayout>
</ContentPage>
Mungkin pelajaran terpenting yang harus Anda ambil dari sampel ini adalah sintaks ekstensi markup: Tidak ada tanda kutip yang harus muncul dalam kurung kurawal ekstensi markup. Saat mengetik ekstensi markup dalam file XAML, wajar jika Anda ingin memasukkan nilai properti dalam tanda kutip. Tahan godaan!
Berikut adalah program yang berjalan:
Ringkasan
Ekstensi markup XAML yang ditampilkan di sini memberikan dukungan penting untuk file XAML. Tetapi mungkin ekstensi markup XAML yang paling berharga adalah Binding
, yang tercakup dalam bagian berikutnya dari seri ini, Bagian 4. Dasar-Dasar Pengikatan Data.