Lekcja 13. Modele danych i walidacja formularzy
Trudny / egzaminacyjnyPo co się tego uczymy?
Formularze w prawdziwych aplikacjach mają zwykle wiele pól, a dane z nich trzeba jakoś zorganizować i sprawdzić, zanim zostaną użyte. Ta lekcja łączy dwie powiązane umiejętności: tworzenie modeli danych (klas grupujących powiązane informacje) i walidację (sprawdzanie poprawności danych wpisanych przez użytkownika) — obie kluczowe dla solidnych aplikacji desktopowych.
Teoria
Wyobraź sobie aplikację "Formularz zdrowia", gdzie użytkownik wpisuje imię, wzrost, wagę i płeć. Można by trzymać te wartości w osobnych zmiennych rozsianych po kodzie — ale to bałagan. Znacznie lepiej stworzyć model, czyli klasę grupującą wszystkie powiązane dane w jednym obiekcie — jak teczka pacjenta w przychodni zamiast trzech luźnych karteczek.
Model powinien zawierać właściwości (properties) odpowiadające danym z formularza, i ewentualnie metody bezpośrednio związane z tymi danymi (np. ObliczBmi() w klasie modelu danych zdrowotnych). Model NIE powinien zawierać kodu obsługującego interfejs (np. MessageBox.Show) ani walidacji — to osobna odpowiedzialność (nawiązanie do zasady z poprzedniej lekcji o oddzieleniu logiki od UI).
Walidacja to sprawdzanie, czy dane wpisane przez użytkownika są poprawne i sensowne, ZANIM ich użyjemy. Typowe reguły: pole nie może być puste, liczba musi być większa od zera, wartość musi mieścić się w rozsądnym zakresie, wybór musi być jedną z dostępnych opcji. Dobra walidacja zabezpiecza przed błędami w działaniu programu (np. dzieleniem przez zero) i od razu informuje użytkownika, co wpisał źle, zamiast pozwolić programowi się "wywrócić".
Sensowna organizacja projektu z modelami i walidacją wygląda np. tak: folder Modele/ z klasami przechowującymi dane, folder Walidacja/ z klasami/metodami sprawdzającymi te dane, a MainWindow.xaml/.cs jako warstwa interfejsu, która korzysta z obu — dokładnie rozwijając ideę z poprzedniej lekcji.
Przykład z życia
Formularz rejestracji na dowolnej stronie czy w aplikacji: model DaneUzytkownika grupuje imię, email, hasło, datę urodzenia w jednym obiekcie zamiast osobnych zmiennych. Osobna klasa Walidator sprawdza każde pole (czy email ma poprawny format, czy hasło ma min. 8 znaków, czy użytkownik ma ukończone 13 lat) i zwraca listę konkretnych błędów do wyświetlenia — dokładnie tak, jak dzieje się to w każdym profesjonalnym formularzu, który widziałeś w internecie czy aplikacji mobilnej.
Kod (C#)
// Modele/DaneFormularza.cs - model grupujacy dane z formularza
namespace FormularzZdrowia.Modele
{
public class DaneFormularza
{
public string Imie { get; set; } = "";
public double WzrostCm { get; set; }
public double WagaKg { get; set; }
// Metoda ZWIAZANA z danymi modelu - to jest OK w modelu
public double ObliczBmi()
{
double wzrostM = WzrostCm / 100.0;
return WagaKg / (wzrostM * wzrostM);
}
}
}
// Walidacja/WalidatorFormularza.cs - osobna klasa sprawdzajaca dane
using System.Collections.Generic;
using FormularzZdrowia.Modele;
namespace FormularzZdrowia.Walidacja
{
public class WalidatorFormularza
{
public List<string> Sprawdz(DaneFormularza dane)
{
var bledy = new List<string>();
if (string.IsNullOrWhiteSpace(dane.Imie))
{
bledy.Add("Imie nie moze byc puste.");
}
if (dane.WzrostCm <= || dane.WzrostCm > 250)
{
bledy.Add("Wzrost musi byc liczba z zakresu 1-250 cm.");
}
if (dane.WagaKg <= || dane.WagaKg > 300)
{
bledy.Add("Waga musi byc liczba z zakresu 1-300 kg.");
}
return bledy; // pusta lista = dane sa poprawne
}
}
}
// MainWindow.xaml.cs - fragment uzycia obu klas
private void PrzyciskOblicz_Click(object sender, RoutedEventArgs e)
{
var dane = new DaneFormularza
{
Imie = PoleImie.Text,
WzrostCm = double.TryParse(PoleWzrost.Text, out var w) ? w : ,
WagaKg = double.TryParse(PoleWaga.Text, out var m) ? m :
};
var walidator = new WalidatorFormularza();
var bledy = walidator.Sprawdz(dane);
if (bledy.Count > )
{
EtykietaWynik.Text = "Bledy: " + string.Join(" ", bledy);
return;
}
EtykietaWynik.Text = $"BMI dla {dane.Imie}: {dane.ObliczBmi():F1}";
}
Komentarz i wyjaśnienie kodu
DaneFormularza to prosty model — trzy właściwości i jedna metoda (ObliczBmi) bezpośrednio związana z tymi danymi. Model NIE wie nic o polach tekstowych ani oknie — to zwykła klasa C#.
WalidatorFormularza.Sprawdz przyjmuje gotowy obiekt modelu i zwraca LISTĘ błędów (pustą, jeśli wszystko jest poprawne) — dzięki zwróceniu listy, a nie pojedynczego bool, można pokazać użytkownikowi WSZYSTKIE problemy naraz, zamiast tylko pierwszego napotkanego.
W code-behind okna: najpierw budujemy obiekt modelu z danych wpisanych przez użytkownika (double.TryParse bezpiecznie próbuje zamienić tekst na liczbę, zwracając 0 jeśli się nie uda — to zapobiega awarii przy niepoprawnym wpisie), potem wołamy walidator, i dopiero jeśli lista błędów jest pusta, wykonujemy właściwe obliczenie przez dane.ObliczBmi().
Ćwiczenie samodzielne
Utwórz projekt z folderami Modele i Walidacja, wklej powyższe klasy, zbuduj prosty formularz z polami Imię/Wzrost/Waga i przyciskiem "Oblicz BMI", i sprawdź działanie zarówno dla poprawnych, jak i błędnych danych (np. wzrost = 0, puste imię).
Zadania do pracy własnej
Rozbuduj
WalidatorFormularzao sprawdzenie, czy imię ma przynajmniej 2 znaki i nie zawiera cyfr.Stwórz model
DaneZamowienia(nazwa produktu, ilość, cena jednostkowa) z metodąObliczWartoscCalkowita(), oraz osobnyWalidatorZamowieniasprawdzający, że ilość i cena są dodatnimi liczbami, a nazwa produktu nie jest pusta.Zaprojektuj formularz rejestracji z modelem
DaneUzytkownika(login, hasło, powtórz hasło, wiek) i walidatorem sprawdzającym: login min. 3 znaki, hasło min. 8 znaków, hasła identyczne, wiek między 13 a 120 lat. Wyświetl WSZYSTKIE błędy naraz w jednej etykiecie (każdy w nowej linii), a po poprawnych danych pokaż komunikat sukcesu.
Typowe błędy
Umieszczanie logiki UI (np. MessageBox.Show) wewnątrz klasy modelu lub walidatora — łamie zasadę oddzielenia z poprzedniej lekcji; walidator powinien tylko ZWRACAĆ informację o błędach, a to okno decyduje, jak je pokazać.
Sprawdzanie tylko pierwszego błędu i przerywanie walidacji — użytkownik poprawia jedno pole, klika ponownie, i widzi KOLEJNY błąd, którego wcześniej nie było widać — frustrujące doświadczenie. Lepiej zbierać wszystkie błędy naraz (jak w przykładzie z listą).
Brak zabezpieczenia przy konwersji tekstu na liczbę — bezpośrednie użycie double.Parse(PoleWzrost.Text) bez TryParse spowoduje awarię programu, jeśli użytkownik wpisze coś, co nie jest liczbą (np. literki).
Nawiązanie do egzaminu zawodowego
To rozwinięcie INF.04.5.3 w kontekście projektowania solidnego interfejsu formularza oraz naturalne połączenie z zasadami programowania obiektowego z działu "Programowanie konsolowe" (klasy, właściwości, hermetyzacja) — modele danych to nic innego, jak zwykłe klasy C#, których użycie poznałeś już wcześniej.