Część 2. Podstawowa składnia języka XAML
Język XAML jest przeznaczony głównie do tworzenia wystąpień i inicjowania obiektów. Jednak często właściwości muszą być ustawione na obiekty złożone, których nie można łatwo przedstawić jako ciągów XML, a czasami właściwości zdefiniowane przez jedną klasę muszą być ustawione w klasie podrzędnej. Te dwa potrzeby wymagają podstawowych funkcji składni XAML elementów właściwości i dołączonych właściwości.
Elementy właściwości
W języku XAML właściwości klas są zwykle ustawiane jako atrybuty XML:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large"
TextColor="Aqua" />
Istnieje jednak alternatywny sposób ustawiania właściwości w języku XAML. Aby wypróbować tę alternatywę za pomocą TextColor
polecenia , najpierw usuń istniejące TextColor
ustawienie:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large" />
Otwórz tag empty-element Label
, oddzielając go do tagów początkowych i końcowych:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large">
</Label>
W tych tagach dodaj tagi początkowe i końcowe składające się z nazwy klasy i nazwy właściwości rozdzielone kropką:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large">
<Label.TextColor>
</Label.TextColor>
</Label>
Ustaw wartość właściwości jako zawartość tych nowych tagów, w następujący sposób:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large">
<Label.TextColor>
Aqua
</Label.TextColor>
</Label>
Te dwa sposoby określania TextColor
właściwości są funkcjonalnie równoważne, ale nie należy używać dwóch sposobów dla tej samej właściwości, ponieważ w rzeczywistości byłoby to ustawienie właściwości dwa razy i może być niejednoznaczne.
Dzięki tej nowej składni można wprowadzić przydatną terminologię:
Label
jest elementem obiektu. Jest to obiekt wyrażony Xamarin.Forms jako element XML.Text
,VerticalOptions
iFontSize
są atrybutami właściwości.FontAttributes
Są to Xamarin.Forms właściwości wyrażone jako atrybuty XML.- W tym ostatnim fragmencie
TextColor
kodu stał się elementem właściwości. Jest Xamarin.Forms to właściwość, ale jest teraz elementem XML.
Definicja elementów właściwości może początkowo wydawać się naruszeniem składni XML, ale nie jest. Kropka nie ma specjalnego znaczenia w formacie XML. Do dekodera Label.TextColor
XML jest po prostu normalnym elementem podrzędnym.
Jednak w języku XAML ta składnia jest bardzo wyjątkowa. Jedną z reguł dotyczących elementów właściwości jest to, że nic innego nie może pojawić się w tagu Label.TextColor
. Wartość właściwości jest zawsze definiowana jako zawartość między tagami start-element-property i end.
Składnia właściwości-elementu może być używana w więcej niż jednej właściwości:
<Label Text="Hello, XAML!"
VerticalOptions="Center">
<Label.FontAttributes>
Bold
</Label.FontAttributes>
<Label.FontSize>
Large
</Label.FontSize>
<Label.TextColor>
Aqua
</Label.TextColor>
</Label>
Możesz też użyć składni property-element dla wszystkich właściwości:
<Label>
<Label.Text>
Hello, XAML!
</Label.Text>
<Label.FontAttributes>
Bold
</Label.FontAttributes>
<Label.FontSize>
Large
</Label.FontSize>
<Label.TextColor>
Aqua
</Label.TextColor>
<Label.VerticalOptions>
Center
</Label.VerticalOptions>
</Label>
Na początku składnia właściwości-elementu może wydawać się niepotrzebnym długotrwałym zastąpieniem czegoś stosunkowo prostego, a w tych przykładach z pewnością tak jest.
Jednak składnia właściwości-elementu staje się niezbędna, gdy wartość właściwości jest zbyt złożona, aby można było je wyrazić jako prosty ciąg. W tagach property-element można utworzyć wystąpienie innego obiektu i ustawić jego właściwości. Można na przykład jawnie ustawić LayoutOptions
właściwość, taką jak VerticalOptions
wartość z ustawieniami właściwości:
<Label>
...
<Label.VerticalOptions>
<LayoutOptions Alignment="Center" />
</Label.VerticalOptions>
</Label>
Inny przykład: Element Grid
ma dwie właściwości o nazwie RowDefinitions
i ColumnDefinitions
. Te dwie właściwości są typu RowDefinitionCollection
i ColumnDefinitionCollection
, które są kolekcjami RowDefinition
obiektów i ColumnDefinition
. Aby ustawić te kolekcje, należy użyć składni elementu właściwości.
Oto początek pliku XAML dla GridDemoPage
klasy z wyświetlonymi tagami elementów właściwości dla RowDefinitions
kolekcji i ColumnDefinitions
:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.GridDemoPage"
Title="Grid Demo Page">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
...
</Grid>
</ContentPage>
Zwróć uwagę na skróconą składnię definiującą komórki o automatycznym rozmiarze, komórki szerokości i wysokości pikseli oraz ustawienia gwiazdki.
Właściwości dołączone
Wiesz już, że właściwość Grid
wymaga elementów właściwości dla RowDefinitions
kolekcji i ColumnDefinitions
do zdefiniowania wierszy i kolumn. Jednak musi istnieć pewien sposób, aby programista wskazywał wiersz i kolumnę, w której znajduje się każde dziecko Grid
.
W tagu dla każdego elementu podrzędnego określ wiersz i kolumnę tego elementu podrzędnego Grid
przy użyciu następujących atrybutów:
Grid.Row
Grid.Column
Wartości domyślne tych atrybutów to 0. Można również wskazać, czy element podrzędny obejmuje więcej niż jeden wiersz lub kolumnę z następującymi atrybutami:
Grid.RowSpan
Grid.ColumnSpan
Te dwa atrybuty mają wartości domyślne 1.
Oto kompletny plik GridDemoPage.xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.GridDemoPage"
Title="Grid Demo Page">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Label Text="Autosized cell"
Grid.Row="0" Grid.Column="0"
TextColor="White"
BackgroundColor="Blue" />
<BoxView Color="Silver"
HeightRequest="0"
Grid.Row="0" Grid.Column="1" />
<BoxView Color="Teal"
Grid.Row="1" Grid.Column="0" />
<Label Text="Leftover space"
Grid.Row="1" Grid.Column="1"
TextColor="Purple"
BackgroundColor="Aqua"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
<Label Text="Span two rows (or more if you want)"
Grid.Row="0" Grid.Column="2" Grid.RowSpan="2"
TextColor="Yellow"
BackgroundColor="Blue"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
<Label Text="Span two columns"
Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
TextColor="Blue"
BackgroundColor="Yellow"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
<Label Text="Fixed 100x100"
Grid.Row="2" Grid.Column="2"
TextColor="Aqua"
BackgroundColor="Red"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
</Grid>
</ContentPage>
Ustawienia Grid.Row
i Grid.Column
0 nie są wymagane, ale są ogólnie uwzględniane w celu zachowania przejrzystości.
Oto jak wygląda:
Sądząc wyłącznie ze składni, te Grid.Row
atrybuty , Grid.RowSpan
Grid.Column
, i Grid.ColumnSpan
wydają się być statyczne pola lub właściwości Grid
, ale co ciekawe, Grid
nie definiuje żadnych elementów o nazwie Row
, Column
, , RowSpan
lub ColumnSpan
.
Grid
Zamiast tego definiuje cztery właściwości możliwe do powiązania o nazwach RowProperty
, ColumnProperty
, RowSpanProperty
i ColumnSpanProperty
. Są to specjalne typy właściwości, które można powiązać, znane jako dołączone właściwości. Są one definiowane przez klasę Grid
, ale ustawiane dla elementów podrzędnych klasy Grid
.
Jeśli chcesz użyć tych dołączonych właściwości w kodzie, Grid
klasa udostępnia metody statyczne o nazwie SetRow
, GetColumn
i tak dalej. Jednak w języku XAML te dołączone właściwości są ustawiane jako atrybuty w elementach podrzędnych przy użyciu prostych Grid
nazw właściwości.
Dołączone właściwości są zawsze rozpoznawalne w plikach XAML jako atrybuty zawierające zarówno klasę, jak i nazwę właściwości oddzieloną kropką. Są one nazywane dołączonymi właściwościami , ponieważ są definiowane przez jedną klasę (w tym przypadku Grid
), ale dołączone do innych obiektów (w tym przypadku elementów podrzędnych klasy Grid
). Podczas układu program może przesłuchić wartości tych dołączonych właściwości, Grid
aby wiedzieć, gdzie umieścić poszczególne elementy podrzędne.
Klasa AbsoluteLayout
definiuje dwie dołączone właściwości o nazwie LayoutBounds
i LayoutFlags
. Oto wzorzec tablicy kontrolnej zrealizowany przy użyciu proporcjonalnych funkcji pozycjonowania i określania rozmiaru elementu AbsoluteLayout
:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.AbsoluteDemoPage"
Title="Absolute Demo Page">
<AbsoluteLayout BackgroundColor="#FF8080">
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0.33, 0, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="1, 0, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0, 0.33, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0.67, 0.33, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0.33, 0.67, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="1, 0.67, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0, 1, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0.67, 1, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
</AbsoluteLayout>
</ContentPage>
A oto:
W przypadku czegoś takiego możesz kwestionować mądrość używania języka XAML. Z pewnością powtórzenie i regularność prostokąta LayoutBounds
sugeruje, że może być lepiej zrealizowany w kodzie.
To z pewnością uzasadnione obawy i nie ma problemu z równoważeniem użycia kodu i znaczników podczas definiowania interfejsów użytkownika. Można łatwo zdefiniować niektóre wizualizacje w języku XAML, a następnie użyć konstruktora pliku za pomocą kodu, aby dodać więcej wizualizacji, które mogą być lepiej generowane w pętlach.
Właściwości zawartości
W poprzednich przykładach StackLayout
obiekty , Grid
i AbsoluteLayout
są ustawione na Content
właściwość ContentPage
, a elementy podrzędne tych układów są rzeczywiście elementami w Children
kolekcji. Jednak te Content
właściwości i Children
nie znajdują się nigdzie w pliku XAML.
Z pewnością można uwzględnić Content
właściwości i Children
jako elementy właściwości, takie jak w przykładzie XamlPlusCode :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.XamlPlusCodePage"
Title="XAML + Code Page">
<ContentPage.Content>
<StackLayout>
<StackLayout.Children>
<Slider VerticalOptions="CenterAndExpand"
ValueChanged="OnSliderValueChanged" />
<Label x:Name="valueLabel"
Text="A simple Label"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Button Text="Click Me!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Clicked="OnButtonClicked" />
</StackLayout.Children>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Prawdziwe pytanie brzmi: Dlaczego te elementy właściwości nie są wymagane w pliku XAML?
Elementy zdefiniowane w Xamarin.Forms pliku do użycia w języku XAML mogą mieć jedną właściwość oflagowaną w atrybucie ContentProperty
w klasie. Jeśli wyszukasz klasę ContentPage
w dokumentacji online Xamarin.Forms , zobaczysz ten atrybut:
[Xamarin.Forms.ContentProperty("Content")]
public class ContentPage : TemplatedPage
Oznacza to, że Content
tagi property-element nie są wymagane. Przyjmuje się, że do właściwości zostanie przypisana dowolna zawartość XML wyświetlana między tagami początkowymi i końcowymi ContentPage
Content
.
StackLayout
, , AbsoluteLayout
Grid
, i RelativeLayout
wszystkie pochodzą z Layout<View>
, a jeśli wyszukasz Layout<T>
w Xamarin.Forms dokumentacji, zobaczysz inny ContentProperty
atrybut:
[Xamarin.Forms.ContentProperty("Children")]
public abstract class Layout<T> : Layout ...
Umożliwia to automatyczne dodawanie zawartości układu do Children
kolekcji bez jawnych Children
tagów właściwości-element.
Inne klasy mają ContentProperty
również definicje atrybutów. Na przykład właściwość content obiektu Label
to Text
. Zapoznaj się z dokumentacją interfejsu API dla innych osób.
Różnice między platformami przy użyciu platformy OnPlatform
W aplikacjach jednostronicowych często ustawia Padding
się właściwość na stronie, aby uniknąć zastępowania paska stanu systemu iOS. W kodzie możesz użyć Device.RuntimePlatform
właściwości w tym celu:
if (Device.RuntimePlatform == Device.iOS)
{
Padding = new Thickness(0, 20, 0, 0);
}
Możesz również zrobić coś podobnego w języku XAML przy użyciu OnPlatform
klas i On
. Najpierw dołącz elementy właściwości dla Padding
właściwości w górnej części strony:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
</ContentPage.Padding>
...
</ContentPage>
W tych tagach dołącz OnPlatform
tag. OnPlatform
jest klasą ogólną. Należy określić argument typu ogólnego, w tym przypadku Thickness
, który jest typem Padding
właściwości. Na szczęście istnieje atrybut XAML przeznaczony specjalnie do definiowania ogólnych argumentów o nazwie x:TypeArguments
. Powinno to być zgodne z typem ustawianej właściwości:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
OnPlatform
ma właściwość o nazwie Platforms
, która jest obiektami IList
On
. Użyj tagów elementów właściwości dla tej właściwości:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<OnPlatform.Platforms>
</OnPlatform.Platforms>
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
Teraz dodaj On
elementy. Dla każdego z nich ustaw Platform
właściwość i Value
właściwość na znaczniki dla Thickness
właściwości:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<OnPlatform.Platforms>
<On Platform="iOS" Value="0, 20, 0, 0" />
<On Platform="Android" Value="0, 0, 0, 0" />
<On Platform="UWP" Value="0, 0, 0, 0" />
</OnPlatform.Platforms>
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
Ten znacznik można uprościć. Właściwość content obiektu OnPlatform
to Platforms
, więc można usunąć te tagi elementu właściwości:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
<On Platform="Android" Value="0, 0, 0, 0" />
<On Platform="UWP" Value="0, 0, 0, 0" />
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
Właściwość Platform
On
jest typu IList<string>
, więc można uwzględnić wiele platform, jeśli wartości są takie same:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
<On Platform="Android, UWP" Value="0, 0, 0, 0" />
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
Ponieważ system Android i platforma UWP są ustawione na wartość Padding
domyślną , można usunąć ten tag:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
Jest to standardowy sposób ustawiania właściwości zależnej od Padding
platformy w języku XAML. Value
Jeśli ustawienie nie może być reprezentowane przez pojedynczy ciąg, możesz zdefiniować dla niego elementy właściwości:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS">
<On.Value>
0, 20, 0, 0
</On.Value>
</On>
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
Uwaga
Rozszerzenie OnPlatform
znaczników może być również używane w języku XAML do dostosowywania wyglądu interfejsu użytkownika dla poszczególnych platform. Zapewnia tę samą funkcjonalność co OnPlatform
klasy i On
, ale z bardziej zwięzłą reprezentacją. Aby uzyskać więcej informacji, zobacz OnPlatform Markup Extension (Rozszerzenie znaczników OnPlatform).
Podsumowanie
W przypadku elementów właściwości i dołączonych właściwości ustanowiono znaczną część podstawowej składni XAML. Jednak czasami należy ustawić właściwości na obiekty w sposób pośredni, na przykład ze słownika zasobów. To podejście zostało omówione w następnej części, część 3. Rozszerzenia znaczników XAML.