Wstęp
W dzisiejszym poście chciałbym skupić się na opisie Xamarin.Forms oraz biblioteki Prism. Przedstawię przykład użycia bindingu oraz zastosowanie interfejsu ICommand.
Xamarin.Forms
Xamarin.Forms, warstwa abstrakcji do tworzenia aplikacji pod Android, iOS, Windows oraz macOS. Kod piszemy głównie w dzielonym projekcie a projekty poszczególnych platform tylko go wywołują. Interfejs graficzny piszemy w XAML (jest też możliwość pisania w c#), osoby które miały styczność z WPF, bardzo szybko się odnajdą w Xamarin.Forms, reszta na pewno nie będzie miała większych problemów żeby go opanować. Język XAML sam w sobie niczego nie narzuca ale wspiera wzorzec MVVM i właśnie on jest zalecany podczas pisania aplikacji.
Prism
Pisanie aplikacji w czystym XAML, z użyciem MVVM, jest troszkę skomplikowane na dłuższą metę, dlatego powstały różne frameworki wspomagające MVVM. Jednym z nich jest Prism, framework z początku wykorzystywany do pisania aplikacji w WPF, także projekt ten jest już długo rozwijany. Korzystając z Prism możemy pisać testowalny kod, dodatkowo możemy podzielić naszą aplikację na mniejsze moduły, co pozwala na łatwiejsze zarządzanie kodem.
Dodanie Prism do projektu
Pierwszy krok to utworzenie projektu. Następnie musimy dodać odpowiedni pakiet nuget. Prism korzysta z dependency injection dlatego musimy zdecydować z jakiego kontenera skorzystamy, ja dla przykładu wykorzystam Unity więc potrzebuję nuget Prism.Forms.Unity. O dependency injection na pewno powstanie jakiś post niedługo. Po instalacji pakietu musimy wprowadzić trochę zmian.
App.xaml – Główny znacznik to musi być PrismApplication, żeby to osiągnąć musimy najpierw zaimportować Prism.Unity.Forms
<?xml version="1.0" encoding="utf-8" ?>
<prism:PrismApplication xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Unity;assembly=Prism.Unity.Forms"
x:Class="PrismExample.App">
App.xaml.cs – App musi dziedziczyć po PrismApplication, implementować klasę abstrakcyjną, oraz wyświetlić jakiś widok, najlepiej korzystając z NavigationService.
public partial class App : PrismApplication
{
protected async override void OnInitialized()
{
InitializeComponent();
await NavigationService.NavigateAsync(nameof(HomeView));
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
Kolejnym krokiem jest utworzenie pierwszego view modelu, dla porządku dodajmy katalog ViewModels i utwórzmy w nim klasę HomeViewModel. Prism opiera się na konwencji nazewniczej, jeśli mamy HomeView i utworzymy HomeViewModel to one się razem sparują. Rozwiązanie to jest elastyczne więc jeśli chcemy podpiąć inny view model to też jesteśmy w stanie to zrobić. Mając utworzony widok oraz view model musimy go zarejestrować w aplikacji, wchodzimy do pliku App.xaml.cs i rozszerzamy ciało metody RegisterTypes:
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<HomeView>(); // wersja przez konwencję
containerRegistry.RegisterForNavigation<HomeView, MyViewModel>(); // wersja z podaniem konkretnego typu view modelu
}
Przejdźmy do HomeViewModel.cs. Komunikacja pomiędzy widokiem a view modelem odbywa się korzystając z powiązań – bindingu. Wiążemy jakąś właściwość z view modelu z właściwością XAML, na przykład Label.Text. Powiązania korzystają z interfejsu INotifyPropertyChanged który składa się z eventu PropertyChanged. Prism udostępnia nam klasę która implementuje ten interfejs BindableBase i z niej skorzystamy. Tworzymy właściwość Count przechowującą ilość kliknięć przycisku, ważne jest to aby w setterze wywołać event PropertyChanged aby poinformować widok że wartość się zmieniła, do tego służy metoda RaisePropertyChanged(). Potrzebujemy jeszcze funkcję która będzie wywołana po naciśnięciu przycisku, która będzie inkrementować nasz licznik. XAML korzysta z interfejsu ICommand do przekazywania metod, więc musimy utworzyć własciwość typu ICommand i przypisać do niej metodę. Z pomocą przychodzi nam Prism który implementację ICommand zawarł w klasie DelegateCommand.
public class HomeViewModel : BindableBase
{
private int _count;
public int Count
{
get { return _count; }
set
{
_count = value;
RaisePropertyChanged();
}
}
public ICommand IncrementCountCommand => new DelegateCommand(IncrementCount);
private void IncrementCount()
{
Count++;
}
}
Pozostaje nam jeszcze powiązać powyższe właściwości z widokiem. Przejdźmy do HomeView.xaml. Tworzymy Label żeby wyświetlić ilość tapnięć oraz przycisk żeby zwiększać licznik. Pierwsze powiązanie będzie z Labelem, do właściwości Text dowiążemy właściwość Count view modelu. Korzystamy tutaj ze słówka Binding. Podobnie sytuacja wygląda z przyciskiem, który posiada właściwość Command.
<Label Text="{Binding Count}" />
<Button Text="Increment" Command="{Binding IncrementCountCommand}"></Button>
Podsumowując mamy dwa elementy niezależne od siebie. Widok którego nie interesuje jaki view model jest do niego podpięty, tylko wie że view model ma dostarczyć właściwość Count oraz IncrementCountCommand. Mamy też view model który nic nie wie o tym do jakiego widoku jest podpięty, dzięki takiemu podejściu możemy pisać testy jednostkowe do view modelu.
Kod dostępny na github –
https://github.com/brzooz/Blog/tree/master/Xamarin.Forms/PrismExample