Daj się poznać 2017

Dynamiczne generowanie UI Xamarin Forms

Jedną z funkcji aplikacji Codziennik, którą chciałbym widzieć w ostatecznej wersji jest możliwość ustawiania własnych pytań przez użytkownika. Nie chcę ustalać odgórnie kilku zestawów, bo ludzie są różni. To co pasuje 99% nie koniecznie musi pasować pozostałym.

Tutaj pojawił się problem. Skąd mam wiedzieć ile pytań ustali sobie użytkownik? Równie dobrze może być to 1, 2 a nawet i kilkanaście pytań. Najprostszym rozwiązaniem(dla mnie jako programisty) byłoby poproszenie użytkownika o wpisanie ich liczby w odpowiednie pole. Jednak jest to mało użyteczne. Dla użytkownika dużo łatwiej jest po prostu wpisać te pytania. Resztą niech zajmie się aplikacja. Na szczęście jest na to rozwiązanie.

W Xamarin Forms istnieją dwie „składnie” tworzenia widoków. Jedna opiera się na języku znaczników – XAMLu. Natomiast druga możliwość to tworzenie widoków w C#. Obie składnie nie różnią się pod względem możliwości dotyczących kontrolek. W obu tak samo możemy tworzyć nowe obiekty, ustawiać ich właściwości i robić to wszystko na co pozwalają widoki w Xamarin Forms.

Jednak to na co pozwala tworzenie widoków w C# a czego nie ma w XAMLu to pisanie „code behind” w tym samym pliku. Więc możemy używać tu całego dobrodziejstwa C#. Nie tracąc czasu przechodzę do przykładów. Pochodzą one z kodu projektu Codziennik.

Ale jak to zrobić?

Na początek utworzę stack layout, do którego będę dodawał potrzebne kontrolki i utworzę nową instancję klasy Entry:

var layout = new StackLayout();
Models.Entry entry = new Models.Entry();
entry.SetQuestions();

Na czymś będziemy musieli się oprzeć przy tworzeniu elementów UI. W tym przypadku w momencie tworzenia nowego wpisu, znana jest liczba pytań. Są one bowiem wczytywane do instancji modelu Entry za pomocą metody SetQuestions(). Trzymane są one w kolekcji Questions. Jako, że podstawą jest Lista, użyję w tym przypadku pętli foreach:

foreach (string question in entry.Questions)
{
	var questionLabel = new Label { Text = question };
	var answerEditor = new Editor 
    { 
        HorizontalOptions = LayoutOptions.Fill, 
        HeightRequest = 150 
    };
	layout.Children.Add(questionLabel);
	layout.Children.Add(answerEditor);
}

Dla większego zrozumienia opiszę każda linijkę po kolei. W pierwszej linii mamy zainicjowaną pętlę foreach, która za zmienną przyjmuje każde pytanie – questionw kolekcji entry.Questions. Przy każdym takim wystąpieniu pętla wykonuje następujące polecenia: Najpierw tworzona jest etykieta o nazwie questionLabel, która jako tekst przyjmuje question wyłuskany przez pętle foreach.

Następnie tworzony jest nowy edytor. To właśnie do niego będzie wprowadzany tekst. HorizontalOptions = LayoutOptions.Fill mówi, żeby ta kontrolka wypełniła całe dostępne miejsce na szerokość. Została też ustawiona wartość propertisa HeightRequest jako 150. Po to, żeby każdy z nich miał ustaloną wysokość, a nie wyświetlał tylko jednej linii wprowadzanego tekstu.

Następne 2 linie przypisują utworzone kontrolki do naszego layoutu, który stworzyliśmy na początku.

To właśnie powyższy kod odpowiada za dynamiczne tworzenie elementów interfejsu. Zostanie stworzonych tyle etykiet z pytaniami i edytorów na odpowiedzi na te pytania ile jest elementów(pytań) w kolekcji entry.Questions. Na koniec warto jeszcze stworzyć ScrollView, aby tworzone przez nas elementy nie znalazły się poza ekranem, a użytkownik nie mógł przewinąć do nich ekranu. Trzeba również przypisać do niego StackLayout, który stworzyliśmy na początku wpisu. Jednak to nie koniec. Żeby to co stworzyliśmy wyświetliło się na ekranie urządzenia, należy ustawić tenże scrollview jako Content tworzonej strony:

var scrollview = new ScrollView()
{
     Content = layout
};
this.Content = scrollview;

Kod całej klasy

Kod całej klasy, którą stworzyliśmy podczas tego wpisu, wygląda następująco:

public class NewEntryPage : ContentPage
{
	public NewEntryPage()
	{
		var layout = new StackLayout();
		Models.Entry entry = new Models.Entry();
		entry.SetQuestions();
		
		foreach (string question in entry.Questions)
		{
			var questionLabel = new Label { Text = question };
			var answerEditor = new Editor 
            { 
                 HorizontalOptions = LayoutOptions.Fill, 
                 HeightRequest = 150 
            };
			layout.Children.Add(questionLabel);
			layout.Children.Add(answerEditor);
		}
		
		
		var scrollview = new ScrollView()
		{
			Content = layout
		};
		this.Content = scrollview;
	}
}

Przechwytywanie wartości z dynamicznie tworzonych Edytorów

Na koniec tego wpisu chciałem jeszcze wtrącić jedną rzecz związaną z dynamicznym tworzeniem widoków. W aplikacji Codziennik muszę zapisywać wartości z Edytorów, które utworzyliśmy powyżej. Ale jak to zrobić? Myślałem chwil kilka nad rozwiązaniem i wymyśliłem. Otóż, każdy z takich edytorów dodaję do Listy – kolekcji generycznej. Później odczytuję ich zawartość w pętli foreach i zapisuję do pliku.

Jak zawsze kod źródłowy można znaleźć na moim Githubie w tym repozytorium. Ta wersja została oznaczona tagiem 03-dynamic-layout. Przechodząc pod niego można zobaczyć jak wyglądał kod podczas pisania tego posta.

PS.

To jest dokładnie 11 wpis w kategorii Daj się poznać 2017 na moim blogu. A to oznacza, że jestem już za połową wymaganej liczby 20 postów konkursowych. Yay! Bardzo się z tego cieszę i dziękuję Tobie drogi czytelniku. Bo o wiele przyjemniej pisze się wiedząc, że ktoś to przeczyta. Nawet jeśli to będzie 5 osób. A nawet niech będzie i 1. Ważna jest dla mnie myśl, że nie piszę do szuflady i przeczyta to tylko ktoś, kto zbłądzi w otchłaniach Internetu. Dziękuję 🙂

Zostaw odpowiedź

Twój adres email nie zostanie upubliczniony.* Pola wymagane *