Xamarin Forms cu Reactive UI

Xamarin Forms cu Reactive UIXamarin Forms este suficient de versatil pe cont propriu. Combinați-l cu Reactive UI și obțineți aplicațiile cele mai elegante și modulare pe orice platformă mobilă sau desktop.

Am început să scriu aplicații Android folosind Xamarin Forms acum câteva luni și m-am îndrăgostit imediat de această platformă. Totodată, deoarece aveam experiență cu MVVM, am decis că ar fi o abordare bună și de această dată. Prin urmare, am ales să lucrez cu Reactive UI, un framework MVVM care permite utilizarea Reactive Extensions pentru .NET Framework în Xamarin Forms. În opinia mea, Reactive UI schimbă total modul de lucru și deschide o abordare diferită și interesantă.

Nu voi intra în detalii despre MVVM sau Reactive Programming, veți găsi link-uri în ultimul paragraf al acestui articol pentru mai multe informații. Luați-le ca și concepte care vor face tutorialul meu mai ușor de urmărit. Cu toate acestea, înțelegerea completă a acestor concepte nu este o cerință.

Xamarin Forms permite dezvoltarea pentru mai multe platforme începând cu Android, iOS, diferite versiuni de Windows Phone și Windows UWP. Eu voi pune accentul pe Android. Am luat această decizie doar pentru a păstra lucrurile cât mai simple. În plus, aceasta este o platformă în care Xamarin Forms se comportă relativ bine.

Aplicația demo expusa in acest tutorial dorește să fie o listă simplă de persoane, cu posibilitatea de a vizualiza detaliile unei anumite persoane. Aceasta va acoperi elementele MVVM de bază ale Reactive UI și modul în care funcționează navigația.

Ca întotdeauna, codul sursă complet pentru acest tutorial este disponibil la https://github.com/levifuksz/xamarin-reactiveui

Înainte de a începe

Am încercat să păstrez lucrurile cât mai simple și am îndreptat acest tutorial spre începători. Totuși, sunt necesare câteva cunoștințe de bază. Presupun că știți un pic de C# și că aveți habar de interfețele cu utilizatorul bazate pe XAML. Experiență prealabilă în dezvoltarea de aplicații Android nu este necesară. Puteți începe să scrieți direct cod sursă în C# fără a cunoaște Java.

De asemenea, presupun că ați instalat Visual Studio împreună cu platforma Xamarin. Puteți descărca versiunea Visual Studio 2017 Community, gratuit, de pe site-ul Microsoft.
https://www.visualstudio.com/

Asigurați-vă că, atunci când instalați Visual Studio 2017, instalați de asemenea platforma Xamarin așa cum se vede în imaginea de mai jos.

Visual Studio cu Xamarin

Platforma Xamarin se poate instala și separat în cazul în care aveți deja Visual Studio. Luați-o de pe site-ul lor de la adresa https://www.xamarin.com/platform

Inițializarea proiectului Xamarin Forms

Deschideți Visual Studio și mergeți la File > New > Project. De asemenea, puteți apăsa combinația de taste Ctrl + Shift + N pentru a obține același rezultat.

În fereastra nouă, pe ierarhia din partea stângă, navigați la Installed > Templates > Visual C# > Cross-Platform și selectați Cross Platform App (Xamarin Forms or Native). De asemenea, nu uitați să alegeți un nume pentru proiect. Continuați făcând clic pe OK . Vedeți imaginea de mai jos.

Proiect nou Xamarin Forms

În următorul dialog, asigurați-vă că alegeți Portable Class Library la setarea Code Sharing Strategy. Lăsați restul setărilor neschimbate ca în imagine. La sfârșit, dați clic pe OK.

Setari proiect Xamarin Forms

În funcție de alte platforme de dezvoltare pe care le-ați instalat, pot apărea câteva ferestre suplimentare în timpul creării unui proiect. Este în regulă să ignorați orice setări care țin de iOS, Windows Phone sau Universal Windows Platform. Lăsați aceste setări neschimbate, deoarece ne vom concentra doar pe Android.

Trecerea la Reactive UI

După generarea proiectului, putem adăuga pachetul NuGet „Reactive UI” la soluție. Știu că tocmai am presupus că sunteți familiarizat cu NuGet Package Manager, dar nu e vorba de nimic complicat. Este doar un instrument pentru adăugarea pachetelor, sau a bibliotecilor de program, dacă preferați această denumire, la proiectele pe care lucrați. Vom folosi acest instrument pentru a instala Reactive UI.

În timp ce există un mod „vizual” de a adăuga pachete NuGet la proiectele noastre, aleg modul de lucru „consolă”, pentru a menține acest articol mai scurt. Prin urmare, accesați Tools > NuGet Package Manager > Package Manager Console. În noua fereastră de consolă, care a apărut în partea de jos a ferestrei Visual Studio, scrieți:

Get-Project -All | Install-Package reactiveui-xamforms

Această comandă PowerShell va enumera toate proiectele din soluție și va instala pachetul NuGet „reactiveui-xamforms” în ele.

Crearea datelor

Toate fișierele pe care le adăugăm sau le modificăm în acest articol vor fi amplasate în proiectul Portable. Nu vom lucra deloc în proiectul Android. Puțină atenție când se vor adăuga fișiere

Înainte de a putea afișa persoanele pentru exemplul nostru, trebuie să le modelăm. De asemenea, datele trebuie să vină de undeva. În mod normal aceste datele vin de la un serviciu dar în cazul nostru vor fi hard-codate în aplicație.

Începeți prin adăugarea unei noi clase numite Person la proiectul Portable. O persoană va avea un nume, o descriere, un site Web și un handle de Twitter. Adăugați aceste proprietăți la clasă. În final, clasa ar trebui să arate ca și codul de mai jos.

public class Person
{
    public string Name { get; set; }        
    public string Description { get; set; }
    public string Website { get; set; }
    public string TwitterHandle { get; set; }
}

Lista Persoanelor, așa cum am menționat, va fi hard-codată de dragul simplității. Să creăm în acest scop o clasă numită PersonsProvider. Această clasă trebuie să aibă o listă de persoane ca cea de mai jos. Nu uitați să adăugați mai multe persoane. Fiți inventivi.

private List<Person> _persons = new List<Person>()
{   
	new Person()
	{
		Name = "Levente Fuksz",
		Description = "Coder and InfoSec Enthusiast",
		TwitterHandle = "@levifuksz",
		Website = "https://blog.iamlevi.net"
	}
};

În cele din urmă, PersonsProvider ar trebui să aibă o metodă care va returna lista de persoane de mai sus. De asemenea, ar trebui să suporte filtrarea după numele persoanei. Metoda este prezentată mai jos.

public async Task<List<Person>> GetPersons(string search)
{
	// Simulate some loading time
	return await Task.Delay(800)
		.ContinueWith((task) =>
		{
			return string.IsNullOrEmpty(search) ?
				_persons :
				_persons.Where(p => p.Name.IndexOf(search, StringComparison.OrdinalIgnoreCase) != -1).ToList();
		});
}

Întregul cod pentru PersonsProvider se poate găsi la PersonsProvider.cs

Viewmodel pentru pagina principală

Codul sursă pentru acest fișier poate fi găsit la MainPageViewModel.cs

Hai să adăugăm viewmodel-ul pentru pagina principală. Faceți acest lucru adăugând o clasă numită MainPageViewModel. Această clasă ar trebui să moștenească ReactiveObject și să implementeze interfața IRoutableViewModel. Prin moștenirea clasei ReactiveObject putem accesa un set de metode ajutătoare din pachetul reactive, iar implementarea IRoutableViewModel face posibilă navigarea către acest viewmodel in stil Reactive UI.

Proprietăți

Interfața IRoutableViewModel declară două proprietăți pe care trebuie să le implementăm. Primul este „titlul” pentru viewmodel, în timp ce cealaltă proprietate este o referință către un IScreen. Putem folosi această referință pentru navigare. Vom ajunge la acest aspect mai târziu în paragraful de navigare. Pentru moment doar adăugați aceste proprietăți.

public string UrlPathSegment => "Persons";

public IScreen HostScreen { get; set; }

Adăugați un câmp și o proprietate pentru a memora filtrul folosit pentru a căuta persoane.

private string _searchTerm;
public string SearchTerm
{
    get { return _searchTerm; }
    set { this.RaiseAndSetIfChanged(ref _searchTerm, value); }
}

Metoda RaiseAndSetIfChanged declanșează un eveniment când se modifică valoarea proprietății SearchTerm și notifică toți abonații la acest eveniment. Notificarea se întâmplă numai atunci când se modifică valoarea în sine. Setarea aceleiași valori de două ori nu va declanșa acest eveniment. Construcția de mai sus este utilă pentru a propaga schimbări către interfața cu utilizatorul.

Căutarea se va face printr-o comandă numită Search

public ReactiveCommand<string, List<Person>> Search { get; set; }

Comandă are un șir de caractere ca parametru, în cazul nostru termenul de căutare, și returnează o listă de persoane.

Avem nevoie de două proprietăți pentru a gestiona datele de ieșire din comanda Search. Prima este un ObservableAsPropertyHelper care ajută în implementarea „proprietăților de ieșire”. Se abonează la un IObservable și se ocupă de notificarea modificărilor. A doua proprietate este doar o listă de persoane. Aceasta este defapt „proprietatea de ieșire” care este legată de ObservableAsPropertyHelper.

private ObservableAsPropertyHelper<List<Person>> _searchResults;

public List<Person> Persons => _searchResults.Value;

De asemenea, avem nevoie de un câmp și o proprietate pentru a memora persoana selectată.

private Person _selectedPerson;

public Person SelectedPerson
{
    get { return _selectedPerson; }
    set { this.RaiseAndSetIfChanged(ref _selectedPerson, value); }
}

În cele din urmă avem nevoie de o proprietate care ne spune când lista persoanelor este în curs de încărcare.

private ObservableAsPropertyHelper<bool> _isSearching;

public bool IsSearching => _isSearching.Value;

Logica

Am declarat mai sus o proprietate HostScreen care are nevoie de o valoare. Este timpul să povestim despre funcționalitatea de tip Service Locator care vine împreună cu Reactive UI. Dacă nu sunteți familiarizat cu conceptul de „Service Locator”, am un link la un articol la sfârșitul acestei pagini. Acest model de lucru înregistrează „servicii” în Service Locator și bucățile de cod care au nevoie de aceste servicii le pot solicita. Obținerea unei instanțe IScreen pentru proprietatea HostScreen se face după cum urmează.

this.HostScreen = Locator.Current.GetService<IScreen>();

În continuare, definim logica din spatele comenzii Search.

this.Search = ReactiveCommand.CreateFromTask<string, List<Person>>(async (search) =>
{
    var personsProvider = new PersonsProvider();
    return await personsProvider.GetPersons(search);
});

Comanda Search este declanșată atunci când proprietatea SearchTerm se modifică. Acest lucru se realizează utilizând metoda WhenAnyValue din clasa ReactiveObject pe care o moștenim. De asemenea, limităm în timp execuția succesivă a acestei comenzi în scopuri de performanță.

this.WhenAnyValue(vm => vm.SearchTerm)
    .Throttle(TimeSpan.FromMilliseconds(500), RxApp.MainThreadScheduler)
    .Select(s => s?.Trim())
    .DistinctUntilChanged()
    .InvokeCommand(this.Search);

Starea execuției comenzii și rezultatul sunt atribuite proprietăților corespunzătoare.

this._isSearching = this.Search.IsExecuting
    .ToProperty(this, vm => vm.IsSearching, false);

_searchResults = this.Search.ToProperty(this, vm => vm.Persons, new List<Person>());

Ultimul lucru pe care trebuie să îl faceți este să declanșați o căutare cu un sir de caractere gol pentru a lista toate persoanele.

this.Search.Execute(string.Empty).Subscribe();

Rețineți că în acest caz trebuie să apelați metoda Subscribe după execuția comenzii pentru a o excuta sincron. În mod normal, am folosi cuvântul cheie async la apel, dar în constructor nu putem face acest lucru.

Pagina principală

Xamarin Forms utilizează XAML pentru a descrie interfața cu utilizatorul. Interfața pentru pagina principală constă dintr-un control Entry care va fi folosit pentru a căuta persoane și un ListView pentru afișarea acestora. Ambele controale sunt childs ai unui control StackLayout care le afișează unul sub altul.

Codul sursă al paginii principale poate fi vizualizat la MainPage.xaml. Am să-l descriu in continuare.

După cum puteți vedea în cod, proprietatea Text a controlului Entry este legată de proprietatea SearchTerm din viewmodel.

Pentru ListView avem legături la trei proprietăți. Mai întâi legăm ItemSource la Persons și apoi SelectedItem la SelectedPerson din viewmodel. De asemenea, proprietatea IsRefreshing, utilizată pentru a afișa un indicator de încărcare, este legată la IsSearching.

În continuare, trebuie să lucram în codul din spatele paginii principale. Puteți accesa acest cod numit code-behind din Solution Explorer sub fișierul MainPage.xaml. Vedeți mai jos.

Solution Explorer

Aici este codul complet: MainPage.xaml.cs, îl explic în continuare.

Mai întâi trebuie să implementăm interfața IViewFor. Acesta este o interfață generică și definește MainPage ca și view pentru viewmodel-ul MainPageViewModel. Această construcție este caracteristică Reactive UI.

public partial class MainPage : ContentPage, IViewFor<MainPageViewModel>

Pentru a implementa această interfață, trebuie să declarăm proprietatea ViewModel. Cel mai bun mod de a face acest lucru este să-l transformăm într-un BindableProperty. Puteți citi mai multe despre aceste tipuri de proprietăți într-un link la sfârșitul articolului.

BindableProperty ViewModelPoperty = BindableProperty.Create(nameof(ViewModel), typeof(MainPageViewModel), typeof(MainPage));

public MainPageViewModel ViewModel
{
    get { return (MainPageViewModel)GetValue(ViewModelPoperty); }
    set { SetValue(ViewModelPoperty, value); }
}

object IViewFor.ViewModel
{
    get { return ViewModel; }
    set { ViewModel = (MainPageViewModel)value; }
}

În cele din urmă, deoarece Xamarin Forms utilizează proprietatea BindingContext pentru a furniza date către legături în XAML, trebuie să o legăm de proprietatea ViewModel. Facem asta în constructorul paginii.

this.WhenAnyValue(v => v.ViewModel).BindTo(this, v => v.BindingContext);

Cu această ultimă linie de cod, pagina noastră principală este gata. Mai avem însă de lucru până ca aplicația noastră să funcționeze.

Viewmodel pentru detalii

Acest viewmodel este unul simplu. Pe lângă proprietățile impuse de implementarea interfeței IRoutableViewModel are o singură proprietate. Aceasta memorează persoana selectată și este setată în constructor cu valoarea unui parametru. Tot ce trebuie să faceți este să adăugați o clasă numită DetailsPageViewModel și să inserați codul de mai jos.

public class DetailsPageViewModel : ReactiveObject, IRoutableViewModel
{
    public Person CurrentPerson { get; private set; }

    public string UrlPathSegment => $"{CurrentPerson.Name} - Details";
    public IScreen HostScreen { get; set; }

    public DetailsPageViewModel(Person currentPerson)
    {
        this.CurrentPerson = currentPerson;
        this.HostScreen = Locator.Current.GetService<IScreen>();
    }
}

Același cod este și pe GitHub la DetailsPageViewModel.cs.

Pagina cu detalii

Nu voi intra prea mult în codul XAML din DetailsPage. Creați-o din meniu accesând Project > Add New Item … sau apăsând combinația de taste Ctrl + Shift + A. Asigurați-vă însă că adăugați o pagina de tipul Forms Content Page Xaml ca în imagine.

Xamarin Forms Content Page

Codul XAML complet pentru această pagină este disponibil la DetailsPage.xaml.
Acesta conține doar un TableView care afișează proprietățile unei persoane.

În cele din urmă, codul din spatele acestei pagini urmează același model ca cel din spatele paginii principale. Luați-l de la DetailsPage.xaml.cs.

Navigarea

Acum, că avem atât pagina principală cât și vizualizarea de detalii, este timpul să le legăm. Vrem să vedem detaliile unei persoane de fiecare dată când alegem una din listă. Hai să implementăm navigarea în stil Reactive UI.

Accesați codul din MainPageViewModel și adăugați următoarea comandă:

public ReactiveCommand ShowDetails { get; set; }

Va fi folosită pentru a naviga la pagina cu detalii.

Puneți de asemenea următorul cod la sfârșitul constructorului.

this.ShowDetails = ReactiveCommand.CreateFromTask<Person>(async (person) =>
{
    await this.HostScreen.Router.Navigate.Execute(new DetailsPageViewModel(person));
    this.SelectedPerson = null;
});

Executarea acestei comenzi va face navigarea utilizând comanda Navigate din proprietatea Router a referinței de tip IScreen. Rețineți că în Reactive UI navigăm de la viewmodel la viewmodel, nu de la pagină la pagină. După navigare, setam SelectedPerson pe null astfel încât să putem selecta aceeași persoană din nou.

Cea mai bună modalitate de a declanșa navigarea este să vă abonați la modificarea proprietății SelectedPerson. Adăugați codul următor la sfârșitul constructorului.

this.WhenAnyValue(vm => vm.SelectedPerson)
    .Where(p => p != null)
    .InvokeCommand(this.ShowDetails);

Să pornim aplicația

Pentru a putea porni aplicația, trebuie să scriem niște cod de configurare. Prin asta înțelegem declararea instanței IScreen, înregistrarea unor servicii și legarea fiecărei pagini de un viewmodel corespunzător. Începeți prin a deschide codul din spatele App.xaml.

Vizualizați codul complet la App.xaml.cs.

Definiți clasa App ca și implementare pentru interfața IScreen și adăugați o proprietate de tip RoutingState numită Router.

public partial class App : Application, IScreen
{
    public RoutingState Router { get; set; }
...

Mergeți în constructor și instanțiați Router.

this.Router = new RoutingState();

Următorul pas este înregistrarea serviciilor, în cazul nostru doar instanța IScreen. Apoi vom face legătura între pagini și viewmodels.

Locator.CurrentMutable.RegisterConstant(this, typeof(IScreen));

Locator.CurrentMutable.Register(() => new MainPage(), typeof(IViewFor<MainPageViewModel>));
Locator.CurrentMutable.Register(() => new DetailsPage(), typeof(IViewFor<DetailsPageViewModel>)); 

Apoi navigăm la pagina principală prin Reactive UI.

this.Router.Navigate.Execute(new MainPageViewModel());

Singurul pas rămas, legat de navigare, este ca proprietatea MainPage să fie un RoutedViewHost. Acesta este tipul utilizat de Reactive UI pentru a gestiona navigarea din RoutingState.

MainPage = new RoutedViewHost();

Am terminat. Aici este sfârșitul tutorialul Xamarin Forms. Porniți aplicația și vedeți ce am obținut.

Legături utile

Un articol bun despre modelul MVVM poate fi găsit la
Model-View-ViewModel (MVVM) Explained

Puteți găsi documentația completă pentru Reactive UI pe GitBook. Din păcate încă se lucrează la ea.
ReactiveUI Documentation

Dacă doriți să explorați programarea reactivă, există un articol excelent și pentru asta.
The introduction to Reactive Programming you’ve been missing

Citiți mai multe despre Shared Projects versus Portable Class Libraries la
Sharing Code Options

Pentru cei care nu au nicio idee despre ce este un Service Locator.
Service Locator Design Pattern

Documentația pentru BindableProperty din Xamarin Forms poate fi găsită la adresa:
Bindable Properties

În cele din urmă, puteți găsi un alt articol scris de mine, care acoperă un pic de Xamarin Forms la.
Control a Raspberry Pi with Android Over Bluetooth

T3ZlciBBbmQgT3V0IQ==

Articole recente

Be First to Comment

Lasă un răspuns

Acest site folosește Akismet pentru a reduce spamul. Află cum sunt procesate datele comentariilor tale.