Implementacja MVVMLight w Xamarin.Forms
Drogi czytelniku domniemam, że posiadasz już podstawową wiedzę na temat wzorców projektowych oraz wstrzykiwania zależności w aplikacji. Nie będzie tu wpisu dlaczego powinieneś wybrać MVVM zamiast MVC, lub czemu moje rozwiązanie jest lepsze od innych, prawdopodobnie istnieje wiele lepszych więc zachęcam Cię do przeszukania zasobów internetu i porównania kilku innych rozwiązań zanim zastosujesz to co tutaj przedstawię. Wszystkie przykłady kodu pochodzą z mojej autorskiej aplikacji i zostały w niej użyte. Enjoy 🙂
Poniższe przykłady bazują na bibliotece MVVMLight.
Pierwszym krokiem implementacji MVVMLight w aplikacji Xamarin, jest deklaracja ViewModelLocator’a w nadrzędnej klasie App.cs. Klasa ViewModelLocator jest dostarczona przez autorów biblioteki, w której powinna odbywać się deklaracja wszystkich ViewModel’i (w dalszej części będę używał skrótu VM), dzięki czemu programista ma dostęp do instancji VM w każdej części swojej aplikacji. Standardowo po instalacji biblioteki MVVMLight, ViewModelLocator znajduje się w katalogu ViewModel.
Moja propozycja budowy struktury aplikacji:
W poniższym wycinku deklaracja locatora odbywa się w linii 7 oraz 8.
1 2 3 4 5 6 7 8 9 |
public class App : Application { public App() { } private static readonly ViewModelLocator _locator = new ViewModelLocator(); public static ViewModelLocator Locator { get { return _locator; } } } |
Kolejnym krokiem, jest dodanie nowego VM. Na potrzeby tego artykułu będzie to BaseViewModel. W moim przypadku jest to nadrzędny VM po którym dziedziczą wszystkie inne ViewModele. Umieściłem w nim funkcje które moim zdaniem mogłyby się powielić w innych ViewModelach (zasada DRY [Don’t repeat yourself]), takie jak sprawdzenie czy użytkownik dalej jest zalogowany, pobieranie i zapisywanie uprawnień przypisanych do konta czy bardzo ważna z mojego punktu widzenia implementacja funkcji OnPropertyChanged, która jest wywoływana za każdym razem gdy zmieni się właściwość po stronie kodu i chcemy ją odświeżyć w widoku.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class BaseViewModel : ViewModelBase, INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public string WelcomeText {get; set;} public BaseViewModel() { SetText(); } private void SetText() { WelcomeText = "Hello World"; OnPropertyChanged("WelcomeText"); } protected void OnPropertyChanged(string name) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } } |
Po stworzeniu pierwszego ViewModel’u, należy go zarejestrować w kontenerze IOC (SimpleIOC), w tym momencie powinieneś posiadać podstawową wiedzę na temat wstrzykiwania zależności, o której wspominałem na początku artykułu. Kontener IOC został dostarczony przez autorów biblioteki MVVMLight. Sama rejestracja obiektu jest dość prostą sprawą, możesz zobaczyć to w 9 linii poniższego przykładu. Po rejestracji obiektu, należy dodać możliwość odwołania się do owego obiektu. Jest to zwyczajny getter z wywołaniem metody GetInstance z ServiceLocator’a z nazwą obiektu, linia 12-15.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class ViewModelLocator { /// <summary> /// Initializes a new instance of the ViewModelLocator class. /// </summary> public ViewModelLocator() { ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); SimpleIoc.Default.Register<BaseViewModel>(); } public BaseViewModel Base { get { return ServiceLocator.Current.GetInstance<BaseViewModel>(); } } public static void Cleanup() { // TODO Clear the ViewModels } } |
Po zadeklarowaniu VM w ViewModelLocator, należy dodać nowy widok w katalogu Views, w którym zostanie wyświetlony tekst zadeklarowany w BaseViewModel. W przypadku tego artykułu widok będzie nazywał się BasePage. Dobrą zasadą jest nazywać podobnie ViewModele oraz Widoki, przykładowo:
ViewModel: BaseViewModel, Widok: BasePage
ViewModel: LoginViewModel, Widok: LoginPage
Po dodaniu widoku, powinieneś otrzymać podobny plik XAML jak w przykładzie niżej. Jest to „prawie” pusty widok, jego jedynym zadaniem jest wyświetlenie napisu powitalnego zadeklarowanego, z wcześniej zadeklarowanego ViewModelu.
1 2 3 4 5 6 7 |
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="AssemblyName.Views.BasePage"> <Label Text="{Binding WelcomeText}" VerticalOptions="Center" HorizontalOptions="Center" /> </ContentPage> |
Przejdźmy teraz do warstwy kodu widoku (BasePage.xaml.cs), w której następuje powiązanie widoku z ViewModelem. Wiązanie odbywa się poprzez przypisanie obiektu wcześniej zadeklarowanego ViewModelu do właściwości BindingContext w tym przykładzie jest to linia 6.
1 2 3 4 5 6 7 8 |
public partial class BasePage : ContentPage { public BasePage() { InitializeComponent(); BindingContext = App.Locator.Base; } } |
Po wykonaniu wszystkich czynności które opisałem, zostało jedynie ustawić BasePage jako strony startowej aplikacji. Robi się to przez przypisanie obiektu BasePage do właściwości MainPage w klasie App.cs, finalna wersja pliku App.cs:
1 2 3 4 5 6 7 8 9 10 |
public class App : Application { public App() { MainPage = new BasePage(); } private static readonly ViewModelLocator _locator = new ViewModelLocator(); public static ViewModelLocator Locator { get { return _locator; } } } |
Po przebudowaniu oraz deploy’u projektu, na emulatorze powinna ukazać się strona startowa z tekstem powitalnym „Hello world”. Mam nadzieję, że rozjaśniłem Ci trochę proces implementacji MVVMLight w aplikacjach Xamarin.Forms. Jeżeli masz jakieś uwagi, pytania lub propozycje lepszego rozwiązania, napisz koniecznie o tym w komentarzu.
W kolejnych artykule planuję opisać działanie Hamburger menu czyli MasterDetailPage w Xamarin.Forms.