Salvarea rețetelor bunicii cu Xamarin.Forms

Publicat: 2022-03-10
Rezumat rapid ↬ Când creați aplicații mobile, trebuie să creați și să mențineți separat interfața de utilizator și logica aplicației atât pentru iOS, cât și pentru Android: Objective-C/Swift cu XCode și Java cu Android Studio. Asta se poate transforma rapid într-o durere. Cu Xamarin.Forms, totuși, interfața de utilizare și logica aplicației pentru aplicația dvs. rezidă într-o singură bază de cod și puteți utiliza un singur IDE pentru a le menține pe toate - economisind timp și dureri de cap. În acest articol, faceți o tură în jurul Xamarin.Forms pentru a vedea ce poate face pentru dvs.

Bunica mea face cele mai bune, cele mai pufoase, chifle slabe în genunchi pe care oricine le-a gustat vreodată. Problema este că există o mulțime de ingrediente secrete (și nu vorbesc doar de dragoste) care intră în acele chifle, iar acele ingrediente și indicații sunt toate stocate în capul bunicii mele.

Cu toții avem astfel de rețete de familie și, în loc să le uităm, în acest articol vom crea o aplicație mobilă pentru iOS și Android folosind Xamarin.Forms, care le va salva pentru mine și pentru generațiile viitoare ale familiei mele!

Desen de chifle cu creșterea aburului
Chifle calde delicioase (Previzualizare mare)

Deci, dacă sunteți interesat să scrieți aplicații mobile, dar nu aveți timp să scrieți aceeași aplicație din nou și din nou pentru fiecare platformă, acest articol este pentru dvs.! Nu vă faceți griji dacă nu cunoașteți C# dintr-o salată de covrigi cu căpșuni; Am scris aplicații Xamarin de peste 8 ani, iar acest articol este un tur prin Xamarin.Forms, care intenționează să vă ofere suficiente informații pentru a începe să învățați pe cont propriu.

Ce este acest lucru Xamarin?

Mai mult decât un cuvânt distractiv de spus, Xamarin permite dezvoltatorilor să creeze aplicații native iOS și Android folosind exact aceleași SDK-uri și comenzi UI disponibile ca în Swift și XCode pentru iOS sau Java și Android Studio pentru Android.

Mai multe după săritură! Continuați să citiți mai jos ↓
Desen cu figurine care se întrebă dacă ar trebui să se dezvolte pentru iOS sau Android
Pentru ce platformă ar trebui să dezvolt? (Previzualizare mare)

Diferența este că aplicațiile sunt dezvoltate cu C# folosind .NET Framework și Visual Studio sau Visual Studio pentru Mac. Aplicațiile care rezultă, totuși, sunt exact aceleași. Ele arată, simt și se comportă la fel ca aplicațiile native scrise în Objective-C, Swift sau Java.

Xamarin strălucește când vine vorba de partajarea codului. Un dezvoltator își poate crea și personaliza interfața de utilizare pentru fiecare platformă folosind controale native și SDK-uri, dar apoi poate scrie o bibliotecă de logica de aplicație partajată care este partajată între platforme.

Desen de stick figure cu ideea de a dezvolta simultan pentru ambele platforme folosind Xamarin
Aha! Voi alege Xamarin! (Previzualizare mare)

În acest schimb de cod se pot realiza economii extraordinare de timp.

Și la fel ca chiflele delicioase pe care le coace bunica mea, odată dat fiind gustul de a partaja codul — este greu să nu poftesc mai mult — și aici intervine Xamarin.Forms.

Xamarin.Forms

Xamarin.Forms preia conceptul de dezvoltare tradițională Xamarin și îi adaugă un strat de abstractizare.

În loc să dezvolte separat interfața de utilizator pentru iOS și Android, Xamarin.Forms introduce un set de instrumente UI care vă permite să scrieți aplicații mobile native dintr-o singură bază de cod.

Gândiți-vă la asta astfel: aveți o aplicație care are nevoie de un buton. Fiecare platformă are conceptul unui buton. De ce ar trebui să scrieți interfața cu utilizatorul de mai multe ori când știți că tot ce trebuie să facă utilizatorul aplicației dvs. este să atingă un buton?

Aceasta este una dintre problemele pe care le rezolvă Xamarin.Forms.

Oferă un set de instrumente cu cele mai frecvent utilizate controale și evenimente de interacțiune cu utilizatorul pentru acestea, așa că trebuie să scriem interfețele de utilizator pentru aplicațiile noastre o singură dată. De remarcat, totuși, nu sunteți limitat la controalele oferite de Xamarin.Forms - puteți utiliza în continuare controalele găsite într-o singură platformă în cadrul unei aplicații Xamarin.Forms. De asemenea, putem partaja logica aplicației între platforme ca și înainte.

Statisticile de partajare a codului pentru aplicațiile dezvoltate cu Xamarin.Forms pot fi în afara topurilor. O aplicație de organizare a conferințelor are 93% din cod partajat pe iOS și 91% pe Android. Aplicația este open source. Aruncă o privire la cod.

Xamarin.Forms oferă mai mult decât controale UI. De asemenea, conține un cadru MVVM, un serviciu de mesagerie pub/sub, un API de animație și un serviciu de dependență, plus altele.

Dar astăzi, ne vom concentra pe capacitățile UI pentru construirea aplicației noastre de manager de rețete.

Aplicația pe care o vom construi

Aplicația de gestionare a rețetelor va avea o interfață de utilizator simplă. Vom lucra în bucătărie, așa că trebuie să fie ușor de folosit!

Acesta va fi format din 3 ecrane . Prima va afișa o listă cu toate rețetele încărcate în prezent în aplicație.

Captură de ecran a ecranului cu lista de rețete pe iOS
(Previzualizare mare)

Apoi, atingând una dintre aceste rețete, veți putea vedea detaliile acesteia pe un al doilea ecran:

Captură de ecran a ecranului cu detaliile rețetei pe iOS
Ecranul cu detaliile rețetei pe iOS (previzualizare mare)

De acolo puteți atinge un buton de editare pentru a face modificări rețetei pe al treilea ecran:

Captură de ecran a ecranului de editare a rețetei pe iOS
Ecranul de editare a rețetei pe iOS (previzualizare mare)

De asemenea, puteți ajunge la acest ecran atingând butonul de adăugare din ecranul listei de rețete.

Mediul de dezvoltare

Aplicațiile Xamarin sunt construite cu C# și .NET, folosind Visual Studio pe Windows sau Visual Studio pentru Mac pe Mac, dar trebuie să aveți instalate, de asemenea, SDK-urile și instrumentele iOS sau Android. Instalarea totul, în ordinea corectă, ar putea fi o problemă, cu toate acestea, instalatorii Visual Studio vor avea grijă doar să instaleze IDE-ul, dar și instrumentele platformei.

Deși este întotdeauna necesar un Mac pentru a construi aplicații iOS, cu Xamarin puteți în continuare să dezvoltați și să depanați acele aplicații din Visual Studio pe Windows! Deci, dacă Windows este blocajul tău, nu este nevoie să-ți schimbi cu totul mediile.

Acum să vedem cum ne poate ajuta Xamarin.Forms să salvăm câteva rețete de familie dintr-o bază de cod!

Pagina cu listă de rețete: stabilirea interfeței de utilizare

Să începem cu a vorbi despre modul în care vom configura interfața de utilizare pentru aplicația noastră de salvare a rețetelor!

În general, fiecare ecran din Xamarin.Forms este compus din 3 elemente. O Page . Cel puțin un element numit Layout . Și cel puțin un Control .

Pagina

Pagina este acel lucru care găzduiește tot ce este afișat pe ecran la un moment dat. Page este, de asemenea, centrală în navigarea în cadrul unei aplicații.

Desen reprezentând o pagină în Xamarin.Forms
Pagina (previzualizare mare)

Îi spunem lui Xamarin.Forms ce Page să fie afișată printr-un serviciu de navigare . Serviciul respectiv se va ocupa apoi de afișarea oricărei pagini într-un mod adecvat și nativ pentru sistemul de operare.

Cu alte cuvinte, codul de navigare între ecrane a fost și el abstras!

În cele din urmă, deși nu este singurul mod de a face acest lucru, codez interfața de utilizare a Page mele în XAML. (Calaltă modalitate ar fi să folosiți C#.) XAML este un limbaj de marcare care descrie cum arată o pagină. Și deocamdată, este suficient să spunem că este cam asemănător cu HTML.

Schema

Toate comenzile dintr-o pagină sunt aranjate de ceva numit aspect.

Desen reprezentând unele aspecte în Xamarin.Forms
Aspectele (previzualizare mare)

Una sau mai multe aspecte pot fi adăugate la o pagină.

Desen al modului în care machetele interacționează cu pagina
Aspecte pe pagină (previzualizare mare)

Există mai multe tipuri diferite de Aspecte în Formulare. Unele dintre cele mai comune includ aspectele Stivă, Absolută, Relativă, Grilă, Scroll și Flex.

Desenul mai multor layout-uri Xamarin.Forms și modul în care își aranjează elementele copil.
Aspecte comune Xamarin.Forms (previzualizare mare)

Controalele

Apoi, în sfârșit, sunt controalele. Acestea sunt widget-urile aplicației dvs. cu care interacționează utilizatorul.

Desenul a câtorva controale Xamarin.Forms, fiecare desenat ca o casetă
Unele dintre comenzi (Previzualizare mare)

Formularele vin cu multe comenzi care vor fi folosite indiferent de tipul de aplicație pe care o construiți. Lucruri precum etichete, butoane, casete de introducere, imagini și, desigur, vizualizări de listă.

Când adăugați un control la un ecran, îl adăugați la un aspect. Este aspectul care se ocupă de a afla unde exact pe ecran ar trebui să apară controlul.

Desenul unei pagini care conține 2 machete, iar acele machete aranjează controalele în funcție de tipul de aspect.
Totul se potrivește! (Previzualizare mare)

Deci, pentru a genera următoarele ecrane pe iOS și respectiv Android:

Captură de ecran a ecranului cu lista de rețete pe iOS și Android
Liste de rețete pe iOS (stânga) și Android (dreapta) (previzualizare mare)

Am folosit acest XAML:

 <?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="https://xamarin.com/schemas/2014/forms" xmlns:x="https://schemas.microsoft.com/winfx/2009/xaml" x:Class="SmashingRecipe.RecipeListPage" Title="Recipes"> <ContentPage.Content> <StackLayout> <ListView x:Name="recipesList"> <ListView.ItemTemplate> <DataTemplate> <TextCell Text="{Binding Name}"/> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </ContentPage.Content> <ContentPage.ToolbarItems> <ToolbarItem Text="Add" /> </ContentPage.ToolbarItems> </ContentPage>

Se întâmplă câteva lucruri importante aici.

Primul este <StackLayout> . Acest lucru îi spune Forms să aranjeze toate controalele care urmează într-o stivă.

Se întâmplă să fie un singur control în aspect, și acesta este un <ListView> și îi vom da un nume pentru a-l putea face referire mai târziu.

Apoi, există un pic de ceremonie generală pentru ListView înainte de a ajunge la ceea ce căutăm: <TextCell> . Acest lucru îi spune Forms să afișeze text simplu în fiecare celulă a listei.

Spunem <TextCell> textul pe care vrem să îl afișeze printr-o tehnică numită Data Binding . Sintaxa arată ca Text="{Binding Name}" . Unde Name este o proprietate a unei clase de Recipe care modelează... Ei bine, Rețete.

Deci, cum se adaugă rețetele pe listă?

Împreună cu fiecare fișier XAML, există un fișier „cod din spatele”. Acest cod subteran ne permite să facem lucruri precum gestionarea evenimentelor de interacțiune cu utilizatorul, configurarea sau efectuarea unei alte aplicații logice.

Există o funcție care poate fi suprascrisă în fiecare Page numită OnAppearing - care, după cum sunt sigur că ați ghicit - este apelată atunci când apare Page .

 protected override void OnAppearing() { base.OnAppearing(); recipesList.ItemsSource = null; recipesList.ItemsSource = App.AllRecipes; }

Observați recipesList.ItemsSource = AllRecipes;

Acest lucru îi spune ListView - „Hei! Toate datele dvs. se găsesc în App.AllRecipes enumerabil (o variabilă la nivelul aplicației) și puteți utiliza oricare dintre proprietățile obiectului său copil pentru a le lega!”.

O listă de rețete este foarte bună - dar nu poți coace nimic fără să vezi mai întâi detaliile rețetei - și ne vom ocupa de asta în continuare.

Gestionarea evenimentelor

Fără a răspunde la atingerile utilizatorului, aplicația noastră nu este altceva decât o listă de rețete delicioase. Sună bine, dar fără să știi să le gătești, nu prea folosește!

Să facem ca fiecare celulă din ListView să răspundă la atingeri, astfel încât să vedem cum să facem rețeta!

În fișierul cod-behind RecipeListPage , putem adăuga handler de evenimente la controale pentru a asculta și a reacționa la evenimentele de interacțiune cu utilizatorul.

Gestionați evenimentele de atingere din vizualizarea listă, apoi:

 recipesList.ItemSelected += async (sender, eventArgs) => { if (eventArgs.SelectedItem != null) { var detailPage = new RecipeDetailPage(eventArgs.SelectedItem as Recipe); await Navigation.PushAsync(detailPage); recipesList.SelectedItem = null; } };

Se întâmplă niște lucruri frumoase acolo.

Ori de câte ori cineva selectează un rând, ItemSelected este declanșat în ListView .

Dintre argumentele care sunt transmise în handler, obiectul eventArgs are o proprietate SelectedItem care se întâmplă să fie orice este legat de ListView de mai înainte.

În cazul nostru, aceasta este clasa Recipe . (Deci nu trebuie să căutăm obiectul în sursa principală - acesta ne este transmis.)

Pagina cu detalii rețete

Desigur, există o pagină care ne arată ingredientele secrete și direcțiile despre cum să facem fiecare rețetă, dar cum este afișată acea pagină?

Desenul unei figurine îmbrăcate în bucătar care este gata să înceapă să coace.
Să ne apucăm de gătit! (Previzualizare mare)

Observați await Navigation.PushAsync(detailPage); linie de sus. Obiectul Navigation este un obiect independent de platformă care gestionează tranzițiile de pagină într-un mod nativ pentru fiecare platformă.

Acum să aruncăm o privire la pagina cu detaliile rețetei:

Captură de ecran a ecranului cu detaliile rețetei pe iOS și Android
Ecrane cu detalii de rețetă pe iOS (stânga) și Android (dreapta) (previzualizare mare)

Această pagină este construită și cu XAML. Cu toate acestea, Layout folosit (FlexLayout) este destul de cool, deoarece este inspirat de CSS Flexbox.

 <ContentPage.Content> <ScrollView> <FlexLayout AlignItems="Start" AlignContent="Start" Wrap="Wrap"> <Image Source="buns.png" FlexLayout.Basis="100%" /> <Label Text="{Binding Name}" HorizontalTextAlignment="Center" TextColor="#01487E" FontAttributes="Bold" FontSize="Large" Margin="10, 10" /> <BoxView FlexLayout.Basis="100%" HeightRequest="0" /> <Label Text="Ingredients" FontAttributes="Bold" FontSize="Medium" TextColor="#EE3F60" Margin="10,10" HorizontalOptions="FillAndExpand" /> <BoxView FlexLayout.Basis="100%" HeightRequest="0" /> <Label Text="{Binding Ingredients}" Margin="10,10" FontSize="Small" /> <BoxView FlexLayout.Basis="100%" HeightRequest="0" /> <Label Text="Directions" FontAttributes="Bold" FontSize="Medium" TextColor="#EE3F60" Margin="10,10" HorizontalOptions="FillAndExpand" /> <BoxView FlexLayout.Basis="100%" HeightRequest="0" /> <Label Text="{Binding Directions}" Margin="10,10" FontSize="Small" /> </FlexLayout> </ScrollView> </ContentPage.Content>

FlexLayout își va aranja controalele fie în rânduri, fie în coloane. Marele beneficiu vine totuși cu faptul că poate detecta automat cât spațiu mai rămâne pe ecran pentru a plasa un control, iar dacă nu este suficient, atunci poate crea automat un nou rând sau coloană pentru a o găzdui!

Acest lucru ajută foarte mult atunci când aveți de-a face cu diferite dimensiuni de ecran, care există o mulțime în dezvoltarea mobilă.

Ei bine, având în vedere că FlexLayout ne ajută să menținem ecranul cu detalii bine, trebuie totuși să edităm acele rețete, nu?

Probabil ai observat asta:

 <ToolbarItem Text="Edit" Clicked="Edit_Clicked" />

Linia respectivă este responsabilă pentru introducerea unui buton în bara de instrumente a aplicației. Clicked="Edit_Clicked" îi spune butonului că atunci când este apăsat, căutați în codul din spate o funcție cu acel nume și apoi executați codul acesteia.

Care, în acest caz, ar fi instanțiarea paginii de editare a rețetei și împingerea acesteia în stiva noastră de navigare folosind obiectul Navigation menționat anterior.

Pagina Editare rețete

O pagină cu o listă de rețete: verificați! O pagina cu toate detaliile pentru a face retetele: verifica! Tot ce mai rămâne acum este să creăm pagina pe care o folosim pentru a introduce sau a schimba o rețetă în timp ce o vedem pe bunica făcându-și magia!

Mai întâi, verifică ecranele:

Captură de ecran a ecranului de editare a rețetei pe iOS și Android
Ecrane de editare a rețetei pe iOS (stânga) și Android (dreapta) (previzualizare mare)

Și acum codul:

 <ContentPage.Content> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TableView Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Intent="Form" HasUnevenRows="true"> <TableSection Title="General"> <EntryCell x:Name="recipeNameCell" Label="Name" /> </TableSection> <TableSection Title="Ingredients"> <ViewCell> <StackLayout Padding="15"> <Editor x:Name="ingredientsCell" /> </StackLayout> </ViewCell> </TableSection> <TableSection Title="Directions"> <ViewCell> <StackLayout Padding="15"> <Editor x:Name="directionsCell" /> </StackLayout> </ViewCell> </TableSection> </TableView> <Button Text="Save" Grid.Row="1" Grid.Column="0" BackgroundColor="#EE3F60" TextColor="White" x:Name="saveButton" /> <Button Text="Cancel" Grid.Row="1" Grid.Column="1" BackgroundColor="#4CC7F2" TextColor="White" x:Name="cancelButton" /> </Grid> </ContentPage.Content>

Există puțin mai mult cod aici și asta pentru că folosesc aspectul Grid pentru a specifica modul în care totul ar trebui să fie aranjat într-un model 2-Dimensional.

Și, de asemenea, observați nicio legătură de date aici. Pentru că am vrut să dau un exemplu despre modul în care s-ar popula controalele doar din codul din spatele fișierului:

 void InitializePage() { Title = TheRecipe.Name ?? "New Recipe"; recipeNameCell.Text = TheRecipe.Name; ingredientsCell.Text = TheRecipe.Ingredients; directionsCell.Text = TheRecipe.Directions; saveButton.Clicked += async (sender, args) => { SaveRecipe(); await CloseWindow(); }; cancelButton.Clicked += async (sender, args) => { await CloseWindow(); }; }

Vezi acea proprietate TheRecipe ? Este la nivel de pagină, deține toate datele pentru o anumită rețetă și este setată în constructorul paginii.

În al doilea rând, Clicked de evenimente Clicked pentru saveButton și cancelButton sunt complet .NET (și da, îmi fac propriile cuvinte destul de des.)

Eu spun că sunt .NET, deoarece sintaxa pentru a gestiona acel eveniment nu este nativă pentru Java și nici pentru Objective-C. Când aplicația rulează pe Android sau iOS, comportamentul va fi exact ca un Android Click sau iOS TouchUpInside.

Și după cum puteți vedea, fiecare dintre acești gestionatori de evenimente clic invocă funcții adecvate care fie salvează rețeta și închid pagina, fie doar închide pagina.

Iată – avem interfața de utilizare oprită pentru a salva rețetele de acum până la sfârșitul timpului!

CSS Ce?!? Sau a face aplicația drăguță

Salvând ce este mai bun pentru final: Xamarin.Forms 3.0 ne oferă, printre altele, capacitatea de a stila controalele folosind CSS!

CSS Xamarin.Forms nu este 100% ceea ce ați putea fi obișnuit din dezvoltarea web. Dar este suficient de aproape încât oricine familiarizat cu CSS se va simți ca acasă. La fel ca mine la bunica!

Deci, să luăm pagina Detalii rețetă și să o refactorăm, astfel încât să folosească Foi de stil în cascadă pentru a seta elementele vizuale în loc să seteze totul direct în XAML.

Primul pas este să creați documentul CSS! În acest caz, va arăta astfel:

 .flexContent { align-items: flex-start; align-content: flex-start; flex-wrap: wrap; } image { flex-basis: 100%; } .spacer { flex-basis: 100%; height: 0; } .foodHeader { font-size: large; font-weight: bold; color: #01487E; margin: 10 10; } .dataLabel { font-size: medium; font-weight: bold; color: #EE3F60; margin: 10 10; } .data { font-size: small; margin: 10 10; }

În cea mai mare parte, arată ca CSS. Sunt clase acolo. Există un singur selector pentru un tip de clasă, Image . Și apoi o grămadă de stabilitori de proprietăți.

Unii dintre acești setatori de proprietăți, cum ar fi flex-wrap sau flex-basis sunt specifici pentru Xamarin.Forms. În continuare, echipa îi va prefix pe cei cu xf- pentru a urma practicile standard.

Următorul va fi să îl aplicați controalelor XAML.

 <ContentPage.Resources> <StyleSheet Source="../Styles/RecipeDetailStyle.css" /> </ContentPage.Resources> <ContentPage.Content> <ScrollView> <FlexLayout StyleClass="flexContent"> <Image Source="buns.png" /> <Label Text="{Binding Name}" StyleClass="foodHeader" /> <BoxView StyleClass="spacer" /> <Label Text="Ingredients" StyleClass="dataLabel" HorizontalOptions="FillAndExpand" /> <BoxView StyleClass="spacer" /> <Label Text="{Binding Ingredients}" StyleClass="data" /> <BoxView StyleClass="spacer" /> <Label Text="Directions" StyleClass="dataLabel" HorizontalOptions="FillAndExpand" /> <BoxView StyleClass="spacer" /> <Label Text="{Binding Directions}" StyleClass="data" /> </FlexLayout> </ScrollView> </ContentPage.Content>

Iată cum arăta înainte.

În Xamarin.Forms, pentru a face referire la documentul CSS, adăugați un <StyleSheet Source="YOUR DOC PATH" /> . Apoi puteți face referire la clasele din fiecare control prin proprietatea StyleClass .

Cu siguranță curăță XAML și face și mai clară intenția controlului. De exemplu, acum este destul de evident ce fac acele <BoxView StyleClass="spacer" /> !

Și Image este stilată, deoarece este o Image și modul în care am definit selectorul în CSS.

Cu siguranță, CSS în Xamarin.Forms nu este la fel de complet implementat ca vărul său web, dar este încă destul de grozav. Aveți selectoare, clase, puteți seta proprietăți și, bineînțeles, tot acel lucru în cascadă se întâmplă!

rezumat

Trei ecrane, două platforme, un articol și rețete nesfârșite salvate! Și știi ce altceva? Puteți crea aplicații cu Xamarin.Forms pentru mai mult decât Android și iOS. Puteți construi platforme UWP, macOS și chiar Samsung Tizen!

Desen de chifle cu creșterea aburului
Delicios! (Previzualizare mare)

Xamarin.Forms este un set de instrumente UI care vă permite să creați aplicații prin scrierea interfeței cu utilizatorul o dată și având interfața de utilizare redată nativ pe platformele majore.

Face acest lucru furnizând un SDK care este o abstractizare a controalelor cele mai frecvent utilizate pe platforme. În plus față de bunătatea UI, Xamarin.Forms oferă, de asemenea, un cadru MVVM cu funcții complete, un serviciu de mesagerie pub/sub, un API de animație și un serviciu de dependență.

Xamarin.Forms vă oferă, de asemenea, aceleași beneficii de cod pe care le oferă dezvoltarea tradițională Xamarin. Orice logică a aplicației este partajată pe toate platformele. Și poți să-ți dezvolți toate aplicațiile cu un singur IDE folosind o singură limbă - asta este destul de grozav!

Incotro acum? Descărcați codul sursă pentru această aplicație Xamarin.Forms pentru a o învârti singur. Apoi, pentru a afla mai multe despre Xamarin.Forms, inclusiv despre capacitatea de a crea o aplicație în browser, consultați acest tutorial online!