Aplikacje mobilne · Dane
Modele danych i walidacja formularzy w .NET MAUI
Aplikacje ciągle pracują z danymi z formularzy: imię, wzrost, waga, e-mail, wiek. Kilkanaście osobnych zmiennych szybko staje się nieczytelne, a niesprawdzone dane psują działanie programu. Poznasz modele danych (klasy grupujące dane) i walidację (sprawdzanie przed użyciem).
- około 55 minut
- .NET MAUI · model · walidacja · double?
- INF.04.6.2
Po tej lekcji: zbudować klasę modelu z typami nullowalnymi, napisać osobny walidator zwracający listę błędów, bezpiecznie zamienić tekst na liczbę i przekazać model na kolejną stronę.
Formularz, który nie przepuszcza błędnych danych
Klawiatura numeryczna nie gwarantuje poprawnej liczby. Najpierw zbierzemy błędy, a dopiero po udanej kontroli stworzymy model i otworzymy podsumowanie.
Najpierw uruchomisz mały ekran. Potem połączysz go z kodem i sprawdzisz konkretne wyniki. Dodatki wykonuj dopiero, gdy wersja podstawowa działa. Quiz i oceniane zadania pozostają w biurku po zalogowaniu.
Laboratorium · ćwiczenie w przeglądarce, do którego odsyłają rozgrzewki na końcu lekcji
1 · Sprawdź w działaniu
Co się stanie przy błędnych danych
Formularz startuje z błędnymi danymi. Kliknij „Wyślij” i zobacz listę błędów. Potem wpisz poprawne dane, aby zobaczyć powstający model. Przełącz też zamianę na double.Parse, a zobaczysz, czemu wywołanie przed walidacją jest ryzykowne.
Najpierw przewidź, potem sprawdź: W modelu walidacji przetestuj puste imię i niepoprawny wzrost jednocześnie. Sprawdź, czy lista błędów pomaga poprawić wszystko za jednym razem.
Najpierw walidacja (TryParse), dopiero potem double.Parse. Błędów zbieraj listę, aby użytkownik poprawił wszystko za jednym razem. Model pomaga zobaczyć mechanizm; właściwą aplikację uruchomisz z plików poniżej.
1. Przygotuj miejsce pracy
Co dodajesz, a co podmieniasz?
Stronę dodaj przez Projekt → Dodaj → Nowy element → .NET MAUI ContentPage (XAML). Powstaje para .xaml i .xaml.cs. Dla ContentView wybierz szablon ContentView; dla modeli i serwisów zwykłą klasę. Jeśli plik już istnieje, podmień wskazaną zawartość — nie twórz drugiej klasy o tej samej nazwie.
| Plik | Rola w ćwiczeniu |
|---|---|
| Models/DaneFormularza.cs | Logika i obsługa danych |
| Logika/WalidatorFormularza.cs | Logika i obsługa danych |
| MainPage.xaml | Wygląd strony lub kontrolki |
| MainPage.xaml.cs | Logika i obsługa danych |
| Podsumowanie.xaml.cs | Logika i obsługa danych |
| Podsumowanie.xaml | Wygląd strony lub kontrolki |
Zanim uruchomisz: trzy połączenia
x:Class="MojaAplikacja.NazwaStrony" w XAML musi zgadzać się z namespace MojaAplikacja i nazwą klasy C#. InitializeComponent() tworzy kontrolki opisane w XAML.
x:Name="PoleImie" daje kodowi C# dostęp przez PoleImie.Text. Z kolei Clicked="Zapisz_Clicked" wskazuje metodę o tej nazwie w pliku .xaml.cs. Sam napis przycisku w Text niczego nie podpina.
Gdy dodajesz XAML z obsługą zdarzeń, od razu uzupełnij też odpowiadający C#. Uruchamiaj dopiero po skończeniu oznaczonej pary plików; brakujący handler nie jest błędem środowiska.
2. Uruchom sam wygląd — jeszcze bez logiki
W świeżym projekcie zastąp MainPage.xaml poniższym ekranem. W MainPage.xaml.cs zachowaj konstruktor z InitializeComponent(); usuń szablonowy licznik i OnCounterClicked, bo nie ma ich już w XAML. Na tym etapie App i AppShell pozostają z szablonu. To pełny, tymczasowy XAML — później świadomie go podmienisz, nie dokleisz do drugiego ContentPage.
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MojaAplikacja.MainPage">
<VerticalStackLayout Padding="24" Spacing="14">
<Label Text="Dane do sprawdzenia" FontSize="24" />
<Entry Placeholder="Imię" />
<Entry Placeholder="Wzrost w cm" Keyboard="Numeric" />
</VerticalStackLayout>
</ContentPage>
- Pierwsza próba Uruchom. Zobaczysz kontrolki, ale przyciski bez Clicked jeszcze nic nie robią. Sprawdź rozmiar i kolejność elementów. Zatrzymaj aplikację przed następnym etapem.
Zacznijmy od teczki
Jedna teczka zamiast kilkunastu karteczek
Model to zwykła klasa C#, która opisuje dane: ma tylko właściwości, bez logiki interfejsu. Dzięki niej dane z formularza trzymasz w jednym obiekcie i możesz przekazać je na inną stronę jednym argumentem konstruktora.
Walidacja to sprawdzenie danych, zanim ich użyjesz (wyświetlisz, zapiszesz, wyślesz). Zgodnie z zasadą z poprzedniej lekcji leży w osobnej klasie, a strona tylko decyduje, jak pokazać błędy.
3 · Co sprawdzać
Typowe reguły w formularzach mobilnych
| Reguła | Jak sprawdzić w C# |
|---|---|
| Pole nie może być puste | string.IsNullOrWhiteSpace(tekst) zwraca true dla null, pustego napisu i samych spacji. |
| Tekst musi być liczbą | double.TryParse(tekst, out var wynik) zwraca false, gdy tekst nie jest liczbą, bez rzucania wyjątku. |
| Liczba w dopuszczalnym zakresie | Zwykłe porównania: if (wzrost <= 0 || wzrost > 300). |
3. Wskaż stronę startową ćwiczenia
Po dodaniu docelowych stron podmień App.xaml.cs na poniższy. App.xaml zostaje z szablonu. Od tej chwili startuje MainPage, a nie wcześniejszy tymczasowy MainPage z AppShell. Uzupełnij jej pliki w kolejnych etapach, zanim ponownie uruchomisz projekt.
namespace MojaAplikacja;
public partial class App : Application
{
public App() => InitializeComponent();
protected override Window CreateWindow(IActivationState? activationState)
=> new Window(new NavigationPage(new MainPage()));
}
CreateWindowTworzy okno aplikacji. NavigationPage jest kontenerem zwykłych stron i daje przycisk powrotu; nie łączymy go z Shell.new MainPage()Tworzy instancję docelowej strony. Aby testować drugi niezależny przykład, zatrzymaj program, zmień tylko ten typ i uruchom ponownie.
4. Połącz wygląd z działaniem
Teraz zbuduj docelowy przykład. Pełne pliki poniżej zastępują swoje wcześniejsze wersje. Przeczytaj opis nad kodem i objaśnienia pod nim; potem sprawdź przepływ danych, nie tylko brak czerwonych podkreśleń.
Gdy przykład ma kilka plików zależnych od siebie, najpierw dodaj wszystkie wskazane typy, a potem uruchom całość. Ustaw breakpoint w obsłudze głównego przycisku i prześledź jedną próbę.
Budowa krok po kroku
DaneFormularza.cs
Dodaj DaneFormularza w folderze Models. Model przenosi wartości między stronami, bez odwołań do kontrolek. double? oznacza liczbę lub brak liczby.
Model ma tylko właściwości. Zwróć uwagę na typy z ?.
namespace MojaAplikacja.Models;
public class DaneFormularza
{
public string Imie { get; set; } = "";
public double? WzrostCm { get; set; }
public double? WagaKg { get; set; }
public string Plec { get; set; } = "Nie podano";
}
double? WzrostCmTyp nullowalny: liczba albo brak wartości (null). Zwykłydoublezawsze ma wartość (domyślnie 0), więc nie odróżnisz „wpisano 0” od „pole jeszcze puste”.string Imie { get; set; } = ""Wartość domyślna pusty napis zapobieganullw tekście.string Plec = "Nie podano"Sensowna wartość domyślna dla niewybranej płci.
Budowa krok po kroku
WalidatorFormularza.cs
Najpierw zbieramy komunikaty do List<string>. Wynik krotki zawiera bool ok oraz listę błędów. TryParse i IsFinite odrzucają błędny zapis, NaN i nieskończoność.
Statyczna klasa zwraca krotkę: czy dane są poprawne oraz listę błędów.
namespace MojaAplikacja.Logika;
public static class WalidatorFormularza
{
public static (bool ok, List<string> bledy) Sprawdz(
string? imieTxt, string? wzrostTxt, string? wagaTxt)
{
var bledy = new List<string>();
if (string.IsNullOrWhiteSpace(imieTxt))
bledy.Add("Podaj imię.");
var wzrost = SprobujLiczbe(wzrostTxt);
if (wzrost is null)
bledy.Add("Wzrost musi być liczbą.");
else if (wzrost <= 0 || wzrost > 300)
bledy.Add("Wzrost w zakresie 1-300 cm.");
var waga = SprobujLiczbe(wagaTxt);
if (waga is null)
bledy.Add("Waga musi być liczbą.");
else if (waga <= 0 || waga > 400)
bledy.Add("Waga w zakresie 1-400 kg.");
return (bledy.Count == 0, bledy);
}
private static double? SprobujLiczbe(string? tekst)
{
if (double.TryParse(tekst, out var wynik) && double.IsFinite(wynik))
return wynik;
return null;
}
}
(bool ok, List<string> bledy)Krotka: dwa wyniki naraz, nazwaneokibledy.var wzrost = SprobujLiczbe(wzrostTxt)Zamienia tekst nadouble?: liczba albonull.if (wzrost is null) ... else if (...)Najpierw sprawdzamy, czy to w ogóle liczba, a dopiero potem zakres.return (bledy.Count == 0, bledy)Brak błędów oznacza dane poprawne.private static double? SprobujLiczbePomocnicza metoda:TryParsew bezpiecznym opakowaniu.
Budowa krok po kroku
MainPage.xaml
Dodaj lub otwórz MainPage.xaml i podmień jego zawartość. Najpierw odszukaj kontener, potem nazwane kontrolki oraz wskazane zdarzenia. Pozostaw zgodne x:Class i nazwę klasy w pliku .xaml.cs.
Formularz w ScrollView, z klawiaturą numeryczną dla liczb.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MojaAplikacja.MainPage" Title="Formularz">
<ScrollView>
<VerticalStackLayout Padding="16" Spacing="12">
<Label Text="Imię" />
<Entry x:Name="PoleImie" Placeholder="Wpisz imię" />
<Label Text="Wzrost (cm)" />
<Entry x:Name="PoleWzrost" Keyboard="Numeric" Placeholder="np. 175" />
<Label Text="Waga (kg)" />
<Entry x:Name="PoleWaga" Keyboard="Numeric" Placeholder="np. 70" />
<Label Text="Płeć" />
<Picker x:Name="PolePlec">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Kobieta</x:String>
<x:String>Mężczyzna</x:String>
<x:String>Nie podano</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>
<Button Text="Wyślij" Clicked="PrzyciskWyslij_Clicked" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>
Keyboard="Numeric"Telefon pokaże klawiaturę z cyframi.Pickerzx:ArrayLista płci zapisana w XAML.
Budowa krok po kroku
MainPage.xaml.cs
Nie twórz modelu przed walidacją. return kończy handler po błędnych danych; tylko poprawna ścieżka dochodzi do PushAsync.
Najpierw walidacja, potem model, potem nowa strona. Kolejność jest zawsze taka sama.
using MojaAplikacja.Logika;
using MojaAplikacja.Models;
namespace MojaAplikacja;
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async void PrzyciskWyslij_Clicked(object sender, EventArgs e)
{
var (ok, bledy) = WalidatorFormularza.Sprawdz(
PoleImie.Text, PoleWzrost.Text, PoleWaga.Text);
if (!ok)
{
await DisplayAlertAsync("Popraw dane", string.Join("\n", bledy), "OK");
return;
}
var dane = new DaneFormularza
{
Imie = PoleImie.Text,
WzrostCm = double.Parse(PoleWzrost.Text),
WagaKg = double.Parse(PoleWaga.Text),
Plec = PolePlec.SelectedItem?.ToString() ?? "Nie podano"
};
await Navigation.PushAsync(new Podsumowanie(dane));
}
}
var (ok, bledy) = WalidatorFormularza.Sprawdz(...)Dekonstrukcja krotki do dwóch zmiennych.string.Join("\n", bledy)Wszystkie błędy w jednym oknie, każdy w nowej linii. Znak\nto koniec linii.double.Parse(PoleWzrost.Text)Bezpieczne dopiero po walidacji, bo wiemy, że tekst jest liczbą.PolePlec.SelectedItem?.ToString() ?? "Nie podano"Ochrona przed brakiem wyboru w pickerze.Navigation.PushAsync(new Podsumowanie(dane))Cały model przechodzi na kolejną stronę jednym argumentem.
Budowa krok po kroku
Podsumowanie.xaml.cs
Ten ekran dostaje gotowy model jako argument konstruktora. BindingContext ustawia źródło dla wszystkich jego etykiet Binding.
Strona dostaje model w konstruktorze i ustawia go jako BindingContext. Etykiety w XAML wiążą się z właściwościami modelu przez {Binding}.
using MojaAplikacja.Models;
namespace MojaAplikacja;
public partial class Podsumowanie : ContentPage
{
public Podsumowanie(DaneFormularza dane)
{
InitializeComponent();
BindingContext = dane; // wiązania {Binding ...} czytają właściwości modelu
}
}
BindingContext = daneWskazuje źródło danych dla wiązań.{Binding WzrostCm, StringFormat='Wzrost: {0} cm'}Wiązanie z formatowaniem tekstu w samym XAML.
Budowa krok po kroku
Widok podsumowania danych
Dodaj lub otwórz Podsumowanie.xaml i podmień jego zawartość. Najpierw odszukaj kontener, potem nazwane kontrolki oraz wskazane zdarzenia. Pozostaw zgodne x:Class i nazwę klasy w pliku .xaml.cs.
BindingContext ustawiony w konstruktorze jest źródłem dla tych właściwości. x:DataType opisuje typ modelu dla kompilowanych wiązań. Formularz uruchom w NavigationPage, jak w lekcji11.
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:models="clr-namespace:MojaAplikacja.Models"
x:Class="MojaAplikacja.Podsumowanie" x:DataType="models:DaneFormularza">
<VerticalStackLayout Padding="20" Spacing="12">
<Label Text="{Binding Imie}" />
<Label Text="{Binding WzrostCm, StringFormat='Wzrost: {0} cm'}" />
<Label Text="{Binding WagaKg, StringFormat='Waga: {0} kg'}" />
<Label Text="{Binding Plec}" />
</VerticalStackLayout>
</ContentPage>
- Przeczytaj powiązanie x:Name identyfikuje kontrolkę, a Binding odczytuje właściwość obiektu ustawionego jako BindingContext. To dwa różne mechanizmy.
5. Odbiór: czy naprawdę działa?
Wykonaj próby w swojej aplikacji, nie tylko w modelu na stronie. Zaznaczaj dopiero po sprawdzeniu wyniku. Funkcje urządzenia wymagają testu na właściwej platformie.
8 · Czego unikać
Cztery błędy z modelami i walidacją
`double` zamiast `double?` w modelu
public double WzrostCm { get; set; }Pole zawsze ma wartość (0), więc nie odróżnisz „wpisano 0” od „nic nie wpisano”.
public double? WzrostCm { get; set; }Typ nullowalny wyraża brak wartości.
`double.Parse` przed walidacją
double wzrost = double.Parse(PoleWzrost.Text);Dla „abc” zgłasza wyjątek i aplikacja się wywraca.
if (double.TryParse(PoleWzrost.Text, out var wzrost)) { ... }Najpierw TryParse, dopiero potem bezpieczny Parse.
Walidacja w code-behind
// 40 linii warunków w PrzyciskWyslij_ClickedLogika miesza się z interfejsem i nie da się jej użyć w innym formularzu.
var (ok, bledy) = WalidatorFormularza.Sprawdz(...);Osobna klasa jest wielokrotnego użytku i łatwiejsza do przetestowania.
Tylko jeden błąd na raz
if (string.IsNullOrWhiteSpace(imie)) return "Podaj imię.";Użytkownik poprawia jedno pole i dostaje kolejny błąd, którego wcześniej nie widział.
bledy.Add("Podaj imię."); // i dalej kolejne regułyLista błędów pozwala pokazać wszystkie problemy od razu.
Ćwiczenie z pomocą · samodzielna praca
6. Mała zmiana, którą zrobisz samodzielnie
Dodaj do danych pole Miasto, bez ograniczeń liczbowych.
Dodaj do danych pole Miasto, bez ograniczeń liczbowych. Dotychczasowe działanie ma pozostać poprawne.
? Potrzebujesz pomocy? Rozwiązuj krok po kroku.
Krok 1 — pokaż podpowiedź
Dodaj string Miasto do modelu i Entry na pierwszej stronie.
Krok 2 — pokaż podpowiedź
Uzupełnij inicjalizator DaneFormularza po udanej walidacji.
Krok 3 — pokaż podpowiedź
Dodaj Label z Binding Miasto na podsumowaniu, którego BindingContext jest tym modelem.
Otwórz tylko tyle podpowiedzi, ile potrzebujesz. Po każdym kroku wróć do zadania i sprawdź wynik.
9 · Sprawdź się
Model, walidacja czy strona?
Cztery sytuacje.
Podsumowanie
Co warto zapamiętać
- Model to klasa z właściwościami bez logiki UI. Typy z
?(double?) wyrażają brak wartości. - Walidator to osobna klasa zwracająca (ok, lista błędów). Pokazanie błędów to zadanie strony.
- Zawsze
TryParse, a dopiero po walidacjiParse. - Zbieraj wszystkie błędy naraz (
string.Join). - Model przekazujesz na kolejną stronę jednym argumentem, a strona ustawia go jako
BindingContext.
Dalej na swoim koncie
Rozgrzewki, quiz i zadania są w biurku ucznia.
Teoria jest otwarta dla wszystkich. Żeby rozwiązywać zadania, zapisywać szkice i widzieć swój postęp, zaloguj się.
Zaloguj się →