Część 5. Od powiązań danych do wzorca MVVM
Wzorzec architektury Model-View-ViewModel (MVVM) został wynaleziony z myślą o języku XAML. Wzorzec wymusza separację między trzema warstwami oprogramowania — interfejsem użytkownika XAML nazywanym widokiem; danych bazowych, nazywanych modelem; i pośrednik między widokiem a modelem, nazywanym Modelem ViewModel. Model View i ViewModel są często połączone za pośrednictwem powiązań danych zdefiniowanych w pliku XAML. Obiekt BindingContext dla widoku jest zwykle wystąpieniem modelu ViewModel.
Prosty model ViewModel
Jako wprowadzenie do modelu ViewModels najpierw przyjrzyjmy się programowi bez jednego.
Wcześniej pokazano, jak zdefiniować nową deklarację przestrzeni nazw XML, aby umożliwić plikowi XAML odwoływanie się do klas w innych zestawach. Oto program, który definiuje deklarację przestrzeni nazw XML dla System
przestrzeni nazw:
xmlns:sys="clr-namespace:System;assembly=netstandard"
Program może użyć x:Static
polecenia , aby uzyskać bieżącą datę i godzinę z właściwości statycznej DateTime.Now
i ustawić BindingContext
wartość DateTime
na wartość w elemencie StackLayout
:
<StackLayout BindingContext="{x:Static sys:DateTime.Now}" …>
BindingContext
jest właściwością specjalną: po ustawieniu BindingContext
elementu na element jest dziedziczony przez wszystkie elementy podrzędne tego elementu. Oznacza to, że wszystkie elementy podrzędne obiektu StackLayout
mają ten sam BindingContext
obiekt i mogą zawierać proste powiązania z właściwościami tego obiektu.
W programie One-Shot DateTime dwa elementy podrzędne zawierają powiązania z właściwościami tej DateTime
wartości, ale dwie inne elementy podrzędne zawierają powiązania, które wydają się brakować ścieżki powiązania. Oznacza to, że DateTime
sama wartość jest używana dla elementu StringFormat
:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:sys="clr-namespace:System;assembly=netstandard"
x:Class="XamlSamples.OneShotDateTimePage"
Title="One-Shot DateTime Page">
<StackLayout BindingContext="{x:Static sys:DateTime.Now}"
HorizontalOptions="Center"
VerticalOptions="Center">
<Label Text="{Binding Year, StringFormat='The year is {0}'}" />
<Label Text="{Binding StringFormat='The month is {0:MMMM}'}" />
<Label Text="{Binding Day, StringFormat='The day is {0}'}" />
<Label Text="{Binding StringFormat='The time is {0:T}'}" />
</StackLayout>
</ContentPage>
Problem polega na tym, że data i godzina są ustawiane raz po utworzeniu strony i nigdy nie zmieniają się:
Plik XAML może wyświetlać zegar, który zawsze pokazuje bieżący czas, ale potrzebuje kodu, aby pomóc. Podczas myślenia w kategoriach MVVM model i ViewModel są klasami napisanymi w całości w kodzie. Widok jest często plikiem XAML, który odwołuje się do właściwości zdefiniowanych w modelu ViewModel za pomocą powiązań danych.
Odpowiedni model jest ignorowany przez model ViewModel, a odpowiedni model ViewModel jest ignorowany w widoku. Jednak często programista dostosowuje typy danych uwidocznione przez model ViewModel do typów danych skojarzonych z określonymi interfejsami użytkownika. Jeśli na przykład model uzyskuje dostęp do bazy danych zawierającej 8-bitowe ciągi ASCII, model ViewModel będzie musiał przekonwertować między tymi ciągami na ciągi Unicode, aby uwzględnić wyłączne użycie unicode w interfejsie użytkownika.
W prostych przykładach maszyny MVVM (takich jak pokazano tutaj), często nie ma modelu, a wzorzec obejmuje tylko model View i ViewModel połączony z powiązaniami danych.
Oto model ViewModel dla zegara z tylko jedną właściwością o nazwie DateTime
, która aktualizuje właściwość DateTime
co sekundę:
using System;
using System.ComponentModel;
using Xamarin.Forms;
namespace XamlSamples
{
class ClockViewModel : INotifyPropertyChanged
{
DateTime dateTime;
public event PropertyChangedEventHandler PropertyChanged;
public ClockViewModel()
{
this.DateTime = DateTime.Now;
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
this.DateTime = DateTime.Now;
return true;
});
}
public DateTime DateTime
{
set
{
if (dateTime != value)
{
dateTime = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("DateTime"));
}
}
}
get
{
return dateTime;
}
}
}
}
Modely ViewModel zwykle implementują INotifyPropertyChanged
interfejs, co oznacza, że klasa uruchamia PropertyChanged
zdarzenie za każdym razem, gdy jedna z jego właściwości ulegnie zmianie. Mechanizm powiązania danych w Xamarin.Forms programie dołącza procedurę obsługi do tego PropertyChanged
zdarzenia, aby można było otrzymywać powiadomienia o zmianie właściwości i aktualizowaniu obiektu docelowego przy użyciu nowej wartości.
Zegar oparty na tym modelu ViewModel może być tak prosty, jak w następujący sposób:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
x:Class="XamlSamples.ClockPage"
Title="Clock Page">
<Label Text="{Binding DateTime, StringFormat='{0:T}'}"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="Center">
<Label.BindingContext>
<local:ClockViewModel />
</Label.BindingContext>
</Label>
</ContentPage>
Zwróć uwagę, ClockViewModel
że właściwość jest ustawiona na Label
BindingContext
znaczniki elementu using. Alternatywnie możesz utworzyć wystąpienie ClockViewModel
obiektu w Resources
kolekcji i ustawić je na BindingContext
za pomocą StaticResource
rozszerzenia znaczników. Możesz też utworzyć wystąpienie pliku z kodem w modelu ViewModel.
Binding
Rozszerzenie znaczników we Text
właściwości Label
formatuje DateTime
właściwość . Oto ekran:
Można również uzyskać dostęp do poszczególnych właściwości DateTime
właściwości modelu ViewModel, oddzielając właściwości kropkami:
<Label Text="{Binding DateTime.Second, StringFormat='{0}'}" … >
Interaktywna maszyna MVVM
Maszyna MVVM jest często używana z dwukierunkowymi powiązaniami danych dla interaktywnego widoku opartego na bazowym modelu danych.
Oto klasa o nazwie HslViewModel
, która konwertuje Color
wartość na Hue
wartości , Saturation
i Luminosity
i na odwrót:
using System;
using System.ComponentModel;
using Xamarin.Forms;
namespace XamlSamples
{
public class HslViewModel : INotifyPropertyChanged
{
double hue, saturation, luminosity;
Color color;
public event PropertyChangedEventHandler PropertyChanged;
public double Hue
{
set
{
if (hue != value)
{
hue = value;
OnPropertyChanged("Hue");
SetNewColor();
}
}
get
{
return hue;
}
}
public double Saturation
{
set
{
if (saturation != value)
{
saturation = value;
OnPropertyChanged("Saturation");
SetNewColor();
}
}
get
{
return saturation;
}
}
public double Luminosity
{
set
{
if (luminosity != value)
{
luminosity = value;
OnPropertyChanged("Luminosity");
SetNewColor();
}
}
get
{
return luminosity;
}
}
public Color Color
{
set
{
if (color != value)
{
color = value;
OnPropertyChanged("Color");
Hue = value.Hue;
Saturation = value.Saturation;
Luminosity = value.Luminosity;
}
}
get
{
return color;
}
}
void SetNewColor()
{
Color = Color.FromHsla(Hue, Saturation, Luminosity);
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Hue
Zmiany właściwości , Saturation
i Luminosity
powodują zmianę Color
właściwości, a zmiany Color
powodują zmianę pozostałych trzech właściwości. Może to wydawać się nieskończoną pętlą, z tą różnicą, że klasa nie wywołuje PropertyChanged
zdarzenia, chyba że właściwość uległa zmianie. Spowoduje to zakończenie niekontrolowanej pętli opinii.
Poniższy plik XAML zawiera BoxView
właściwość, której Color
właściwość jest powiązana z właściwością Color
ViewModel, oraz trzema Slider
i trzema Label
widokami powiązanymi z właściwościami Hue
, Saturation
i Luminosity
:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
x:Class="XamlSamples.HslColorScrollPage"
Title="HSL Color Scroll Page">
<ContentPage.BindingContext>
<local:HslViewModel Color="Aqua" />
</ContentPage.BindingContext>
<StackLayout Padding="10, 0">
<BoxView Color="{Binding Color}"
VerticalOptions="FillAndExpand" />
<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}"
HorizontalOptions="Center" />
<Slider Value="{Binding Hue, Mode=TwoWay}" />
<Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}"
HorizontalOptions="Center" />
<Slider Value="{Binding Saturation, Mode=TwoWay}" />
<Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}"
HorizontalOptions="Center" />
<Slider Value="{Binding Luminosity, Mode=TwoWay}" />
</StackLayout>
</ContentPage>
Powiązanie dla każdego Label
z nich jest ustawieniem domyślnym OneWay
. Musi tylko wyświetlić wartość. Jednak powiązanie dla każdego Slider
z nich to TwoWay
. Umożliwia to zainicjowanie elementu Slider
z modelu ViewModel. Zwróć uwagę, że Color
właściwość jest ustawiona na Aqua
wartość , gdy wystąpienie modelu ViewModel jest tworzone. Jednak zmiana Slider
w obiekcie musi również ustawić nową wartość właściwości w modelu ViewModel, która następnie oblicza nowy kolor.
Commanding with ViewModels
W wielu przypadkach wzorzec MVVM jest ograniczony do manipulowania elementami danych: obiekty interfejsu użytkownika w widoku obiektów danych równoległych w modelu ViewModel.
Jednak czasami widok musi zawierać przyciski, które wyzwalają różne akcje w modelu ViewModel. Jednak model ViewModel nie może zawierać Clicked
procedur obsługi przycisków, ponieważ spowoduje to powiązanie modelu ViewModel z określonym paradygmatem interfejsu użytkownika.
Aby umożliwić modelom ViewModel bardziej niezależne od określonych obiektów interfejsu użytkownika, ale nadal zezwalaj na wywoływanie metod w modelu ViewModel, istnieje interfejs polecenia . Ten interfejs polecenia jest obsługiwany przez następujące elementy w pliku Xamarin.Forms:
Button
MenuItem
ToolbarItem
SearchBar
TextCell
(i dlatego teżImageCell
)ListView
TapGestureRecognizer
Z wyjątkiem elementu SearchBar
i ListView
te elementy definiują dwie właściwości:
Command
typuSystem.Windows.Input.ICommand
CommandParameter
typuObject
Definiuje SearchBar
właściwości i SearchCommandParameter
, podczas gdy ListView
definiuje RefreshCommand
właściwość typu ICommand
.SearchCommand
Interfejs ICommand
definiuje dwie metody i jedno zdarzenie:
void Execute(object arg)
bool CanExecute(object arg)
event EventHandler CanExecuteChanged
Model ViewModel może definiować właściwości typu ICommand
. Następnie można powiązać te właściwości z właściwością Command
każdego Button
lub innego elementu, a może z widokiem niestandardowym, który implementuje ten interfejs. Opcjonalnie można ustawić CommandParameter
właściwość, aby zidentyfikować poszczególne Button
obiekty (lub inne elementy), które są powiązane z tą właściwością ViewModel. Wewnętrznie metoda wywołuje metodę za każdym razem, Button
gdy użytkownik naciśnie metodę Button
, przekazując ją do Execute
metody CommandParameter
.Execute
Metoda CanExecute
i CanExecuteChanged
zdarzenie są używane w przypadkach, Button
gdy naciśnięcie może być obecnie nieprawidłowe, w takim przypadku Button
polecenie powinno zostać wyłączone. Wywołania Button
CanExecute
, gdy Command
właściwość jest ustawiana po raz pierwszy i za każdym razem, CanExecuteChanged
gdy zdarzenie jest uruchamiane. Jeśli CanExecute
funkcja zwraca false
wartość , Button
funkcja wyłącza się i nie generuje Execute
wywołań.
Aby uzyskać pomoc dotyczącą dodawania poleceń do modelu ViewModels, Xamarin.Forms definiuje dwie klasy, które implementują ICommand
element : Command
i Command<T>
gdzie T
jest typem argumentów do Execute
i CanExecute
. Te dwie klasy definiują kilka konstruktorów oraz metodę ChangeCanExecute
, którą model ViewModel może wywołać, aby wymusić Command
wyzwolenie CanExecuteChanged
zdarzenia przez obiekt.
Oto model ViewModel dla prostego klawiatury przeznaczonego do wprowadzania numerów telefonów. Zwróć uwagę, że Execute
metoda i CanExecute
jest zdefiniowana jako funkcje lambda bezpośrednio w konstruktorze:
using System;
using System.ComponentModel;
using System.Windows.Input;
using Xamarin.Forms;
namespace XamlSamples
{
class KeypadViewModel : INotifyPropertyChanged
{
string inputString = "";
string displayText = "";
char[] specialChars = { '*', '#' };
public event PropertyChangedEventHandler PropertyChanged;
// Constructor
public KeypadViewModel()
{
AddCharCommand = new Command<string>((key) =>
{
// Add the key to the input string.
InputString += key;
});
DeleteCharCommand = new Command(() =>
{
// Strip a character from the input string.
InputString = InputString.Substring(0, InputString.Length - 1);
},
() =>
{
// Return true if there's something to delete.
return InputString.Length > 0;
});
}
// Public properties
public string InputString
{
protected set
{
if (inputString != value)
{
inputString = value;
OnPropertyChanged("InputString");
DisplayText = FormatText(inputString);
// Perhaps the delete button must be enabled/disabled.
((Command)DeleteCharCommand).ChangeCanExecute();
}
}
get { return inputString; }
}
public string DisplayText
{
protected set
{
if (displayText != value)
{
displayText = value;
OnPropertyChanged("DisplayText");
}
}
get { return displayText; }
}
// ICommand implementations
public ICommand AddCharCommand { protected set; get; }
public ICommand DeleteCharCommand { protected set; get; }
string FormatText(string str)
{
bool hasNonNumbers = str.IndexOfAny(specialChars) != -1;
string formatted = str;
if (hasNonNumbers || str.Length < 4 || str.Length > 10)
{
}
else if (str.Length < 8)
{
formatted = String.Format("{0}-{1}",
str.Substring(0, 3),
str.Substring(3));
}
else
{
formatted = String.Format("({0}) {1}-{2}",
str.Substring(0, 3),
str.Substring(3, 3),
str.Substring(6));
}
return formatted;
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
W tym modelu ViewModel przyjęto założenie, że AddCharCommand
właściwość jest powiązana z właściwością Command
kilku przycisków (lub dowolnych innych elementów, które mają interfejs polecenia), z których każda jest identyfikowana przez CommandParameter
element . Przyciski te dodają znaki do InputString
właściwości, która jest następnie sformatowana jako numer telefonu dla DisplayText
właściwości.
Istnieje również druga właściwość typu ICommand
o nazwie DeleteCharCommand
. Jest to powiązane z przyciskiem odstępu wstecz, ale przycisk powinien być wyłączony, jeśli nie ma znaków do usunięcia.
Poniższy klawiatura nie jest tak wyrafinowana wizualnie, jak to możliwe. Zamiast tego znaczniki zostały zredukowane do minimum, aby zademonstrować bardziej wyraźnie użycie interfejsu poleceń:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
x:Class="XamlSamples.KeypadPage"
Title="Keypad Page">
<Grid HorizontalOptions="Center"
VerticalOptions="Center">
<Grid.BindingContext>
<local:KeypadViewModel />
</Grid.BindingContext>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<!-- Internal Grid for top row of items -->
<Grid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Frame Grid.Column="0"
OutlineColor="Accent">
<Label Text="{Binding DisplayText}" />
</Frame>
<Button Text="⇦"
Command="{Binding DeleteCharCommand}"
Grid.Column="1"
BorderWidth="0" />
</Grid>
<Button Text="1"
Command="{Binding AddCharCommand}"
CommandParameter="1"
Grid.Row="1" Grid.Column="0" />
<Button Text="2"
Command="{Binding AddCharCommand}"
CommandParameter="2"
Grid.Row="1" Grid.Column="1" />
<Button Text="3"
Command="{Binding AddCharCommand}"
CommandParameter="3"
Grid.Row="1" Grid.Column="2" />
<Button Text="4"
Command="{Binding AddCharCommand}"
CommandParameter="4"
Grid.Row="2" Grid.Column="0" />
<Button Text="5"
Command="{Binding AddCharCommand}"
CommandParameter="5"
Grid.Row="2" Grid.Column="1" />
<Button Text="6"
Command="{Binding AddCharCommand}"
CommandParameter="6"
Grid.Row="2" Grid.Column="2" />
<Button Text="7"
Command="{Binding AddCharCommand}"
CommandParameter="7"
Grid.Row="3" Grid.Column="0" />
<Button Text="8"
Command="{Binding AddCharCommand}"
CommandParameter="8"
Grid.Row="3" Grid.Column="1" />
<Button Text="9"
Command="{Binding AddCharCommand}"
CommandParameter="9"
Grid.Row="3" Grid.Column="2" />
<Button Text="*"
Command="{Binding AddCharCommand}"
CommandParameter="*"
Grid.Row="4" Grid.Column="0" />
<Button Text="0"
Command="{Binding AddCharCommand}"
CommandParameter="0"
Grid.Row="4" Grid.Column="1" />
<Button Text="#"
Command="{Binding AddCharCommand}"
CommandParameter="#"
Grid.Row="4" Grid.Column="2" />
</Grid>
</ContentPage>
Właściwość Command
pierwszego Button
, która pojawia się w tym znaczniku, jest powiązana DeleteCharCommand
z elementem ; pozostałe są powiązane AddCharCommand
z znakiem , CommandParameter
który jest taki sam jak znak wyświetlany na Button
twarzy. Oto program w działaniu:
Wywoływanie metod asynchronicznych
Polecenia mogą również wywoływać metody asynchroniczne. Jest to osiągane przy użyciu async
słów kluczowych i await
podczas określania Execute
metody:
DownloadCommand = new Command (async () => await DownloadAsync ());
Oznacza to, że DownloadAsync
metoda jest Task
metodą i powinna być oczekiwana:
async Task DownloadAsync ()
{
await Task.Run (() => Download ());
}
void Download ()
{
...
}
Implementowanie menu nawigacji
Przykładowy program zawierający cały kod źródłowy w tej serii artykułów używa modelu ViewModel dla swojej strony głównej. Ten model ViewModel to definicja krótkiej klasy o trzech właściwościach o nazwie Type
, Title
i Description
zawierających typ każdej z przykładowych stron, tytuł i krótki opis. Ponadto model ViewModel definiuje właściwość statyczną o nazwie All
, która jest kolekcją wszystkich stron w programie:
public class PageDataViewModel
{
public PageDataViewModel(Type type, string title, string description)
{
Type = type;
Title = title;
Description = description;
}
public Type Type { private set; get; }
public string Title { private set; get; }
public string Description { private set; get; }
static PageDataViewModel()
{
All = new List<PageDataViewModel>
{
// Part 1. Getting Started with XAML
new PageDataViewModel(typeof(HelloXamlPage), "Hello, XAML",
"Display a Label with many properties set"),
new PageDataViewModel(typeof(XamlPlusCodePage), "XAML + Code",
"Interact with a Slider and Button"),
// Part 2. Essential XAML Syntax
new PageDataViewModel(typeof(GridDemoPage), "Grid Demo",
"Explore XAML syntax with the Grid"),
new PageDataViewModel(typeof(AbsoluteDemoPage), "Absolute Demo",
"Explore XAML syntax with AbsoluteLayout"),
// Part 3. XAML Markup Extensions
new PageDataViewModel(typeof(SharedResourcesPage), "Shared Resources",
"Using resource dictionaries to share resources"),
new PageDataViewModel(typeof(StaticConstantsPage), "Static Constants",
"Using the x:Static markup extensions"),
new PageDataViewModel(typeof(RelativeLayoutPage), "Relative Layout",
"Explore XAML markup extensions"),
// Part 4. Data Binding Basics
new PageDataViewModel(typeof(SliderBindingsPage), "Slider Bindings",
"Bind properties of two views on the page"),
new PageDataViewModel(typeof(SliderTransformsPage), "Slider Transforms",
"Use Sliders with reverse bindings"),
new PageDataViewModel(typeof(ListViewDemoPage), "ListView Demo",
"Use a ListView with data bindings"),
// Part 5. From Data Bindings to MVVM
new PageDataViewModel(typeof(OneShotDateTimePage), "One-Shot DateTime",
"Obtain the current DateTime and display it"),
new PageDataViewModel(typeof(ClockPage), "Clock",
"Dynamically display the current time"),
new PageDataViewModel(typeof(HslColorScrollPage), "HSL Color Scroll",
"Use a view model to select HSL colors"),
new PageDataViewModel(typeof(KeypadPage), "Keypad",
"Use a view model for numeric keypad logic")
};
}
public static IList<PageDataViewModel> All { private set; get; }
}
Plik XAML dla definiuje, którego ItemsSource
właściwość jest ustawiona na tej All
właściwości i która zawiera element TextCell
do wyświetlania Title
właściwości i Description
każdej ListBox
MainPage
strony:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples"
x:Class="XamlSamples.MainPage"
Padding="5, 0"
Title="XAML Samples">
<ListView ItemsSource="{x:Static local:PageDataViewModel.All}"
ItemSelected="OnListViewItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Title}"
Detail="{Binding Description}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
Strony są wyświetlane na liście z możliwością przewijania:
Program obsługi w pliku za kodem jest wyzwalany, gdy użytkownik wybierze element. Procedura obsługi ustawia SelectedItem
właściwość ListBox
wstecz na null
, a następnie tworzy wystąpienie wybranej strony i przechodzi do niej:
private async void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs args)
{
(sender as ListView).SelectedItem = null;
if (args.SelectedItem != null)
{
PageDataViewModel pageData = args.SelectedItem as PageDataViewModel;
Page page = (Page)Activator.CreateInstance(pageData.Type);
await Navigation.PushAsync(page);
}
}
Wideo
Xamarin Evolve 2016: MVVM Made Simple with Xamarin.Forms and Prism
Podsumowanie
XAML to zaawansowane narzędzie do definiowania interfejsów użytkownika w Xamarin.Forms aplikacjach, szczególnie w przypadku użycia maszyn wirtualnych MVVM i powiązania danych. Wynikiem jest czysta, elegancka i potencjalnie narzędziowa reprezentacja interfejsu użytkownika ze wszystkimi obsługą tła w kodzie.
Linki powiązane
- Część 1. Część 1. Wprowadzenie do języka XAML
- Część 2. Podstawowa składnia języka XAML
- Część 3. Rozszerzenia struktury znaczników XAML
- Część 4. Powiązania danych — podstawy
Pokrewne wideo
Więcej filmów na platformie Xamarin można znaleźć w witrynach Channel 9 i YouTube.