INF.04 · .NET MAUI · przed egzaminem
Zbuduj dowolna aplikacje MAUI
w jednym schemacie
Ten artykul to sciagazka na dzien przed egzaminem. Jeden schemat dziala w kazdym zadaniu INF.04. Znajdziesz tu gotowe fragmenty kodu, tabele kontrolek, dwie kompletne aplikacje i checkjiste przed oddaniem.
Visual Studio 2022|
.NET MAUI App|
C# + XAML|
Android Emulator / Windows
Podstawa
Kazda aplikacja MAUI = te same 3 warstwy
Zanim zaczniesz pisac kod – zapamietaj ten schemat. Egzaminator sprawdza czy go stosujesz.
01 / Widok
MainPage.xaml
Layout + kontrolki. Tu NIE ma zadnej logiki obliczeniowej. Tylko uklad ekranu.
02 / Polaczenie
MainPage.xaml.cs
Obsluga zdarzen (Clicked, TextChanged). Pobiera dane z kontrolek, wywoluje metode z klasy Logika, pokazuje wynik.
03 / Logika
Logika/Kalkulator.cs
Czysta klasa C# z metodami obliczeniowymi. Nie wie nic o przyciskach ani polach. Mozna ja testowac MSTest.
Zlota zasada INF.04: metoda obliczeniowa nigdy nie siedzi w obsludze przycisku. Code-behind tylko pobiera dane i pokazuje wynik. Obliczenia = osobna klasa.
Krok 1
Utworz projekt i uruchom aplikacje
1
Visual Studio 2022 -> Utworz nowy projekt
Wyszukaj .NET MAUI App (nie Blazor, nie Xamarin). Wybierz i kliknij Dalej.
2
Nazwij projekt bez spacji i polskich znakow
Np. RejestracjaApp, ListaSortowaniaApp. Spacje w nazwie = bledy przy kompilacji.
3
Wybierz urzadzenie docelowe
Na pasku gornym wybierz Android Emulator lub Windows Machine.
4
Uruchom (F5) – upewnij sie ze dziala pusty szablon
Zanim cokolwiek zmienisz – uruchom domyslny projekt. Jezeli cos sie nie kompiluje od razu, problem jest w srodowisku.
5
Dodaj folder Logika
Prawy klik na projekt -> Dodaj -> Nowy folder -> nazwij Logika. Tu beda klasy z obliczeniami.
Krok 2
3 layouty – zapamietaj te trzy
Na egzaminie uzywasz jednego lub kombinacji. Wszystkie wstawiasz jako zawartosc ContentPage w MainPage.xaml.
Layout 1 – VerticalStackLayout (najczestszy)
Elementy jeden pod drugim. Domyslny wybor gdy nie wiesz czego uzyc.
<!– Zawartosc ContentPage –>
<VerticalStackLayout Padding=„20” Spacing=„12”>
<Label Text=„Imie” />
<Entry x:Name=„PoleImie” Placeholder=„wpisz imie” />
<Button Text=„Zatwierdz” Clicked=„Btn_Click” />
<Label x:Name=„WynikLabel” />
</VerticalStackLayout>
Layout 2 – Grid (etykiety obok pol)
Uzywaj gdy arkusz egzaminacyjny ma tabelaryczny uklad z polami i etykietami obok siebie.
<Grid Padding=„20” RowSpacing=„10” ColumnSpacing=„12”>
<Grid.RowDefinitions>
<RowDefinition Height=„Auto” />
<RowDefinition Height=„Auto” />
<RowDefinition Height=„Auto” />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=„120” />
<ColumnDefinition Width=„*” />
</Grid.ColumnDefinitions>
<!– Grid.Row=wiersz (od 0), Grid.Column=kolumna (od 0) –>
<Label Text=„Imie:” Grid.Row=„0” Grid.Column=„0” VerticalOptions=„Center” />
<Entry x:Name=„PoleImie” Grid.Row=„0” Grid.Column=„1” />
<Label Text=„Wiek:” Grid.Row=„1” Grid.Column=„0” VerticalOptions=„Center” />
<Entry x:Name=„PoleWiek” Grid.Row=„1” Grid.Column=„1” Keyboard=„Numeric” />
<Button Text=„Zatwierdz” Grid.Row=„2” Grid.ColumnSpan=„2”
Clicked=„Btn_Click” />
</Grid>
Layout 3 – ScrollView + VerticalStackLayout (dlugi formularz)
Gdy jest duzo kontrolek i aplikacja musi przewijac zawartosc na malym ekranie.
<ScrollView>
<VerticalStackLayout Padding=„20” Spacing=„14”>
<Label Text=„Formularz rejestracji” FontSize=„22” FontAttributes=„Bold”/>
<Entry x:Name=„PoleImie” Placeholder=„Imie”/>
<!– … kolejne kontrolki … –>
</VerticalStackLayout>
</ScrollView>
Kontrolki
Wszystkie kontrolki – tabela podreczna
x:Name daje dostep do kontrolki z poziomu C#. Text i inne wlasciwosci ustawiasz w XAML lub z kodu.
| Kontrolka | Do czego sluzy | Jak odczytac wartosc w C# |
| Label | Wyswietla tekst (etykieta, wynik) | NazwaLabel.Text = "wynik"; |
| Entry | Pole tekstowe (jeden wiersz) | NazwaEntry.Text – zawsze string! |
| Editor | Pole tekstowe (wiele wierszy) | NazwaEditor.Text |
| Button | Przycisk – wywoluje zdarzenie | Clicked="Metoda_Click" |
| CheckBox | Pole zaznaczenia (tak/nie) | NazwaCheckBox.IsChecked (bool) |
| Switch | Przelacznik (wlacz/wylacz) | NazwaSwitch.IsToggled (bool) |
| Slider | Suwak (wartosc liczbowa) | (int)NazwaSlider.Value – zakres Minimum/Maximum |
| Picker | Lista wyboru (dropdown) | NazwaPicker.SelectedItem?.ToString() |
| DatePicker | Wybor daty | NazwaDatePicker.Date (DateTime) |
| Stepper | Wartosc +/-1 krok (gora/dol) | (int)NazwaStepper.Value |
| Image | Wyswietla obraz | NazwaImage.Source = sciezka; |
| CollectionView | Lista obiektow (wiele rekordow) | NazwaCv.ItemsSource = lista; |
| BoxView | Prostokat z kolorem (demo, separator) | NazwaBoxView.Color = Color.FromRgb(r,g,b); |
Entry.Text to zawsze string! Zeby dostac liczbe musisz skonwertowac: double.TryParse(PoleWaga.Text, out double waga). Jezeli konwersja sie nie uda – TryParse zwraca false i to jest Twoja walidacja.
Krok 3
Zdarzenia – podpiecie i odczyt danych
Szablon kazdego zdarzenia Clicked
// MainPage.xaml.cs – kazde zdarzenie wygada tak samo:
private void Btn_Click(object sender, EventArgs e)
{
// 1. POBIERZ dane z kontrolek
string imie = PoleImie.Text ?? „”;
string wagaTxt = PoleWaga.Text ?? „”;
// 2. SKONWERTUJ i ZWALIDUJ
if (string.IsNullOrWhiteSpace(imie))
{
WynikLabel.Text = „Podaj imie!”;
return;
}
if (!double.TryParse(wagaTxt, out double waga) || waga <= 0)
{
WynikLabel.Text = „Waga musi byc liczba wieksza od 0!”;
return;
}
// 3. WYWOLAJ metode z klasy Logika
var kalk = new Logika.Kalkulator();
double wynik = kalk.ObliczBMI(waga, 1.75);
// 4. POKAZ wynik
WynikLabel.Text = $„BMI: {wynik:F1}”;
}
Odczyt z roznych kontrolek – gotowe fragmenty
// Slider – suwak
int r = (int)SliderR.Value; // 0-255
// CheckBox
bool zgoda = CheckZgoda.IsChecked;
// Switch
bool aktywny = SwitchAktywny.IsToggled;
// Picker
string plec = PickerPlec.SelectedItem?.ToString() ?? „”;
// DatePicker
DateTime data = DatePickerUrodzenia.Date;
int wiek = DateTime.Today.Year – data.Year;
// Stepper – liczba calkowita
int ilosc = (int)StepperIlosc.Value;
Slider -> zmiana koloru BoxView w czasie rzeczywistym
Zdarzenie ValueChanged odpala sie za kazdym ruchem suwaka – bez klikania przycisku.
<!– XAML –>
<Label Text=„Czerwony (R)”/>
<Slider x:Name=„SliderR” Minimum=„0” Maximum=„255” Value=„128”
ValueChanged=„Slider_Changed”/>
<Label Text=„Zielony (G)”/>
<Slider x:Name=„SliderG” Minimum=„0” Maximum=„255” Value=„128”
ValueChanged=„Slider_Changed”/>
<Label Text=„Niebieski (B)”/>
<Slider x:Name=„SliderB” Minimum=„0” Maximum=„255” Value=„128”
ValueChanged=„Slider_Changed”/>
<!– BoxView pokazujacy aktualny kolor –>
<BoxView x:Name=„PokazKolor” HeightRequest=„80” CornerRadius=„8”/>
<Label x:Name=„LabelKolor”/>
<Label x:Name=„NapisKolorem” Text=„Ten napis zmienia kolor” FontSize=„18”/>
// C# – wspolna metoda dla wszystkich 3 suwaków:
private void Slider_Changed(object sender, ValueChangedEventArgs e)
{
int r = (int)SliderR.Value;
int g = (int)SliderG.Value;
int b = (int)SliderB.Value;
var kolor = Color.FromRgb(r, g, b);
PokazKolor.Color = kolor; // tlo prostokata
NapisKolorem.TextColor = kolor; // kolor napisu
LabelKolor.Text = $„RGB({r}, {g}, {b})”;
}
Krok 4
Osobna klasa logiki – szablon
To jest to za co dostaje sie punkty na egzaminie. Czysta klasa C# – zero importow MAUI.
Logika/Kalkulator.cs
namespace MojaApp.Logika;
public class Kalkulator
{
/// <summary>
/// Oblicza BMI na podstawie wagi i wzrostu.
/// </summary>
/// <param name=”wagaKg”>Waga w kilogramach</param>
/// <param name=”wzrostM”>Wzrost w metrach</param>
/// <returns>Wartosc BMI zaokraglona do 2 miejsc</returns>
// POWYZSZY KOMENTARZ XML JEST WYMAGANY W DOKUMENTACJI!
public double ObliczBMI(double wagaKg, double wzrostM)
{
if (wzrostM <= 0) throw new ArgumentException(„Wzrost musi byc wiekszy od 0”);
return Math.Round(wagaKg / (wzrostM * wzrostM), 2);
}
public string InterpretujBMI(double bmi) => bmi switch
{
< 18.5 => „Niedowaga”,
< 25.0 => „Norma”,
< 30.0 => „Nadwaga”,
_ => „Otylosc”
};
}
Jak uzywasz klasy Logika w MainPage.xaml.cs: na poczatku pliku dodaj
using MojaApp.Logika; albo uzyj pelnej nazwy: var k = new Logika.Kalkulator();
Krok 5
Lista danych + sortowanie
CollectionView to podstawa kazdej aplikacji z wieloma rekordami. Model – lista – wyswietlanie – sortowanie.
Model danych
Models/Osoba.cs
namespace MojaApp.Models;
public class Osoba
{
public string Imie { get; set; } = „”;
public string Nazwisko { get; set; } = „”;
public int Wiek { get; set; }
// Wlasciwosc wyliczana – nie trzeba jej zapisywac
public string PelneNazwisko => $„{Imie} {Nazwisko}”;
}
XAML – CollectionView z DataTemplate
<CollectionView x:Name=„ListaOsob”>
<CollectionView.ItemTemplate>
<DataTemplate>
<VerticalStackLayout Padding=„10,6”>
<Label Text=„{Binding PelneNazwisko}” FontSize=„16” FontAttributes=„Bold”/>
<Label Text=„{Binding Wiek, StringFormat=’Wiek: {0} lat’}”/>
</VerticalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<HorizontalStackLayout Spacing=„8”>
<Button Text=„A-Z” Clicked=„SortujAZ_Click”/>
<Button Text=„Z-A” Clicked=„SortujZA_Click”/>
<Button Text=„Wiek rosnaco” Clicked=„SortujWiek_Click”/>
</HorizontalStackLayout>
C# – dane z tablicy + sortowanie
using MojaApp.Models;
public partial class MainPage : ContentPage
{
private List<Osoba> _osoby = new();
public MainPage()
{
InitializeComponent();
// Dane z tablicy
_osoby = new List<Osoba>
{
new() { Imie = „Anna”, Nazwisko = „Kowalska”, Wiek = 28 },
new() { Imie = „Bartek”, Nazwisko = „Nowak”, Wiek = 34 },
new() { Imie = „Celina”, Nazwisko = „Wisniewska”, Wiek = 22 },
new() { Imie = „Dawid”, Nazwisko = „Zajac”, Wiek = 41 },
new() { Imie = „Ewa”, Nazwisko = „Adamczyk”, Wiek = 19 },
};
OdswiezListe(_osoby);
}
void OdswiezListe(List<Osoba> lista)
{
ListaOsob.ItemsSource = null; // wymuz odswiezenie
ListaOsob.ItemsSource = lista;
}
private void SortujAZ_Click(object s, EventArgs e) =>
OdswiezListe(_osoby.OrderBy(o => o.Nazwisko).ToList());
private void SortujZA_Click(object s, EventArgs e) =>
OdswiezListe(_osoby.OrderByDescending(o => o.Nazwisko).ToList());
private void SortujWiek_Click(object s, EventArgs e) =>
OdswiezListe(_osoby.OrderBy(o => o.Wiek).ToList());
}
Krok 6
Zapis i odczyt danych – TXT i JSON
Sciezka pliku w MAUI = zawsze FileSystem.AppDataDirectory!
Nigdy nie uzywaj C:\Users\… ani sciezek desktopowych – na Androidzie nie istnieja i aplikacja sie wywali.
Zapis i odczyt pliku TXT
string _sciezkaTxt = Path.Combine(FileSystem.AppDataDirectory, „dane.txt”);
private async void Zapisz_Click(object s, EventArgs e)
{
await File.WriteAllTextAsync(_sciezkaTxt, PoleEditor.Text ?? „”);
StatusLabel.Text = „Zapisano!”;
}
private async void Wczytaj_Click(object s, EventArgs e)
{
if (File.Exists(_sciezkaTxt))
{
PoleEditor.Text = await File.ReadAllTextAsync(_sciezkaTxt);
StatusLabel.Text = „Wczytano!”;
}
else
StatusLabel.Text = „Plik nie istnieje – najpierw zapisz.”;
}
Zapis i odczyt listy obiektow jako JSON
using System.Text.Json;
using MojaApp.Models;
string _sciezkaJson = Path.Combine(FileSystem.AppDataDirectory, „osoby.json”);
private async void ZapiszJson_Click(object s, EventArgs e)
{
string json = JsonSerializer.Serialize(_osoby);
await File.WriteAllTextAsync(_sciezkaJson, json);
StatusLabel.Text = $„Zapisano {_osoby.Count} osob.”;
}
private async void WczytajJson_Click(object s, EventArgs e)
{
if (!File.Exists(_sciezkaJson))
{ StatusLabel.Text = „Brak pliku.”; return; }
string json = await File.ReadAllTextAsync(_sciezkaJson);
_osoby = JsonSerializer.Deserialize<List<Osoba>>(json) ?? new();
OdswiezListe(_osoby);
StatusLabel.Text = $„Wczytano {_osoby.Count} osob.”;
}
Przepis
Picker – lista rozwijana z danymi z kodu
Picker nie wypelnia sie w XAML – dane wkladasz z C# w konstruktorze.
<!– XAML –>
<Picker x:Name=„PickerPlec” Title=„Wybierz plec”/>
// Konstruktor MainPage
PickerPlec.ItemsSource = new List<string> { „Kobieta”, „Mezczyzna”, „Inne” };
// Odczyt:
string plec = PickerPlec.SelectedItem?.ToString() ?? „”;
Przepis
Nawigacja – przejscie do drugiego ekranu z danymi
1
Dodaj nowa strone
Prawy klik na projekt -> Dodaj -> Nowy element -> .NET MAUI ContentPage (XAML) -> nazwij np. PodsumowaniePage.xaml
2
W App.xaml.cs ustaw NavigationPage
Zamiast AppShell: MainPage = new NavigationPage(new MainPage()); – dzieki temu Navigation.PushAsync zadziala.
3
Przejdz do drugiej strony z danych formularza
W obsludze przycisku wywolaj Navigation.PushAsync i przekaz model.
// PodsumowaniePage.xaml.cs – konstruktor przyjmuje dane
public partial class PodsumowaniePage : ContentPage
{
public PodsumowaniePage(Osoba osoba)
{
InitializeComponent();
LabelImie.Text = $„Witaj, {osoba.Imie} {osoba.Nazwisko}!”;
LabelWiek.Text = $„Wiek: {osoba.Wiek} lat”;
}
}
// MainPage.xaml.cs – przejscie
private async void Dalej_Click(object s, EventArgs e)
{
var osoba = new Osoba
{
Imie = PoleImie.Text ?? „”,
Nazwisko = PoleNazwisko.Text ?? „”,
Wiek = int.TryParse(PoleWiek.Text, out int w) ? w : 0
};
await Navigation.PushAsync(new PodsumowaniePage(osoba));
}
Aplikacja 1
Kompletna aplikacja: Rejestracja z kolorem
Formularz ze wszystkimi podstawowymi kontrolkami: Entry, Picker, Stepper, DatePicker, CheckBox i 3 suwaki RGB zmieniajace kolor napisu. Dane zapisywane do JSON.
MainPage.xaml
<?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=„RejestracjaApp.MainPage”
Title=„Rejestracja”>
<ScrollView>
<VerticalStackLayout Padding=„20” Spacing=„14”>
<Label Text=„Formularz rejestracji” FontSize=„22” FontAttributes=„Bold”
HorizontalOptions=„Center”/>
<Label Text=„Imie i nazwisko”/>
<Entry x:Name=„PoleImie” Placeholder=„Jan Kowalski”/>
<Label Text=„Plec”/>
<Picker x:Name=„PickerPlec” Title=„Wybierz plec”/>
<Label x:Name=„LabelWiek” Text=„Wiek: 18”/>
<Stepper x:Name=„StepperWiek” Minimum=„10” Maximum=„99”
Value=„18” Increment=„1” ValueChanged=„Stepper_Changed”/>
<Label Text=„Data urodzenia”/>
<DatePicker x:Name=„DataUrodzenia” Format=„yyyy-MM-dd”/>
<HorizontalStackLayout Spacing=„10”>
<CheckBox x:Name=„CheckZgoda”/>
<Label Text=„Akceptuje regulamin” VerticalOptions=„Center”/>
</HorizontalStackLayout>
<BoxView HeightRequest=„1” Color=„Gray” Opacity=„0.3”/>
<Label Text=„Wybierz kolor napisu” FontAttributes=„Bold”/>
<Label Text=„Czerwony (R)”/>
<Slider x:Name=„SliderR” Minimum=„0” Maximum=„255”
Value=„100” ValueChanged=„Slider_Changed”/>
<Label Text=„Zielony (G)”/>
<Slider x:Name=„SliderG” Minimum=„0” Maximum=„255”
Value=„150” ValueChanged=„Slider_Changed”/>
<Label Text=„Niebieski (B)”/>
<Slider x:Name=„SliderB” Minimum=„0” Maximum=„255”
Value=„200” ValueChanged=„Slider_Changed”/>
<BoxView x:Name=„PokazKolor” HeightRequest=„60” CornerRadius=„8”/>
<Label x:Name=„LabelKolorRgb” HorizontalOptions=„Center”/>
<Label x:Name=„NapisPowitania” Text=„Tu pojawi sie powitanie”
FontSize=„18” HorizontalOptions=„Center”/>
<Button Text=„Zarejestruj” Clicked=„Rejestruj_Click”/>
<Button Text=„Zapisz do JSON” Clicked=„ZapiszJson_Click”/>
<Button Text=„Wczytaj JSON” Clicked=„WczytajJson_Click”/>
<Label x:Name=„StatusLabel” TextColor=„Gray”/>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
MainPage.xaml.cs
using System.Text.Json;
using RejestracjaApp.Models;
namespace RejestracjaApp;
public partial class MainPage : ContentPage
{
private List<Osoba> _lista = new();
private string _sciezka = Path.Combine(FileSystem.AppDataDirectory, „rejestracja.json”);
public MainPage()
{
InitializeComponent();
PickerPlec.ItemsSource = new List<string> { „Kobieta”, „Mezczyzna”, „Inne” };
AktualizujKolor();
}
private void Stepper_Changed(object s, ValueChangedEventArgs e) =>
LabelWiek.Text = $„Wiek: {(int)StepperWiek.Value}”;
private void Slider_Changed(object s, ValueChangedEventArgs e) => AktualizujKolor();
private void AktualizujKolor()
{
int r = (int)SliderR.Value;
int g = (int)SliderG.Value;
int b = (int)SliderB.Value;
var kolor = Color.FromRgb(r, g, b);
PokazKolor.Color = kolor;
NapisPowitania.TextColor = kolor; // kolor napisu zmienia sie na zywo
LabelKolorRgb.Text = $„RGB({r}, {g}, {b})”;
}
private void Rejestruj_Click(object s, EventArgs e)
{
string imie = PoleImie.Text?.Trim() ?? „”;
if (string.IsNullOrEmpty(imie)) { StatusLabel.Text = „Podaj imie!”; return; }
if (PickerPlec.SelectedItem == null) { StatusLabel.Text = „Wybierz plec!”; return; }
if (!CheckZgoda.IsChecked) { StatusLabel.Text = „Zaakceptuj regulamin!”; return; }
_lista.Add(new Osoba
{
Imie = imie,
Plec = PickerPlec.SelectedItem.ToString()!,
Wiek = (int)StepperWiek.Value,
DataUrodzenia = DataUrodzenia.Date,
KolorR = (int)SliderR.Value,
KolorG = (int)SliderG.Value,
KolorB = (int)SliderB.Value
});
NapisPowitania.Text = $„Zarejestrowano: {imie}!”;
StatusLabel.Text = $„Na liscie: {_lista.Count} osob.”;
}
private async void ZapiszJson_Click(object s, EventArgs e)
{
await File.WriteAllTextAsync(_sciezka, JsonSerializer.Serialize(_lista));
StatusLabel.Text = $„Zapisano {_lista.Count} rekordow.”;
}
private async void WczytajJson_Click(object s, EventArgs e)
{
if (!File.Exists(_sciezka)) { StatusLabel.Text = „Brak pliku.”; return; }
string json = await File.ReadAllTextAsync(_sciezka);
_lista = JsonSerializer.Deserialize<List<Osoba>>(json) ?? new();
StatusLabel.Text = $„Wczytano {_lista.Count} rekordow.”;
}
}
// Models/Osoba.cs
namespace RejestracjaApp.Models;
public class Osoba
{
public string Imie { get; set; } = „”;
public string Plec { get; set; } = „”;
public int Wiek { get; set; }
public DateTime DataUrodzenia { get; set; }
public int KolorR { get; set; }
public int KolorG { get; set; }
public int KolorB { get; set; }
}
Aplikacja 2
Kompletna aplikacja: Lista + sortowanie + JSON
Dane z tablicy -> CollectionView -> sortowanie alfabetyczne i po wartosci -> eksport JSON. Wzorzec na kazde zadanie egzaminacyjne z lista.
MainPage.xaml
<?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=„SortowanieApp.MainPage”
Title=„Lista i sortowanie”>
<VerticalStackLayout Padding=„16” Spacing=„10”>
<Label Text=„Dodaj osobe” FontAttributes=„Bold”/>
<Entry x:Name=„PoleImie” Placeholder=„Imie”/>
<Entry x:Name=„PoleNazwisko” Placeholder=„Nazwisko”/>
<Entry x:Name=„PoleWiek” Placeholder=„Wiek” Keyboard=„Numeric”/>
<Button Text=„+ Dodaj” Clicked=„Dodaj_Click”/>
<HorizontalStackLayout Spacing=„6”>
<Button Text=„A-Z” Clicked=„SortAZ_Click”/>
<Button Text=„Z-A” Clicked=„SortZA_Click”/>
<Button Text=„Wiek” Clicked=„SortWiek_Click”/>
<Button Text=„Reset” Clicked=„Reset_Click”/>
</HorizontalStackLayout>
<HorizontalStackLayout Spacing=„6”>
<Button Text=„Zapisz JSON” Clicked=„Zapisz_Click”/>
<Button Text=„Wczytaj JSON” Clicked=„Wczytaj_Click”/>
</HorizontalStackLayout>
<Label x:Name=„StatusLabel” TextColor=„Gray”/>
<CollectionView x:Name=„ListaOsob”>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding=„10,6” ColumnDefinitions=„*,Auto”>
<VerticalStackLayout Grid.Column=„0”>
<Label Text=„{Binding PelneNazwisko}” FontAttributes=„Bold”/>
<Label Text=„{Binding Wiek, StringFormat=’Wiek: {0} lat’}”
TextColor=„Gray”/>
</VerticalStackLayout>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ContentPage>
MainPage.xaml.cs
using System.Text.Json;
using SortowanieApp.Models;
namespace SortowanieApp;
public partial class MainPage : ContentPage
{
private List<Osoba> _oryginalna = new();
private string _sciezka = Path.Combine(FileSystem.AppDataDirectory, „osoby.json”);
public MainPage()
{
InitializeComponent();
_oryginalna = new List<Osoba>
{
new() { Imie=„Anna”, Nazwisko=„Kowalska”, Wiek=28 },
new() { Imie=„Bartek”, Nazwisko=„Nowak”, Wiek=34 },
new() { Imie=„Celina”, Nazwisko=„Wisniewska”, Wiek=22 },
new() { Imie=„Dawid”, Nazwisko=„Zajac”, Wiek=41 },
new() { Imie=„Ewa”, Nazwisko=„Adamczyk”, Wiek=19 },
};
Odswierz(_oryginalna);
}
void Odswierz(List<Osoba> lista)
{
ListaOsob.ItemsSource = null;
ListaOsob.ItemsSource = lista;
StatusLabel.Text = $„Rekordow: {lista.Count}”;
}
private void Dodaj_Click(object s, EventArgs e)
{
string imie = PoleImie.Text?.Trim() ?? „”;
string nazw = PoleNazwisko.Text?.Trim() ?? „”;
if (string.IsNullOrEmpty(imie) || string.IsNullOrEmpty(nazw))
{ StatusLabel.Text = „Podaj imie i nazwisko!”; return; }
if (!int.TryParse(PoleWiek.Text, out int wiek) || wiek < 1)
{ StatusLabel.Text = „Wiek musi byc liczba!”; return; }
_oryginalna.Add(new() { Imie = imie, Nazwisko = nazw, Wiek = wiek });
PoleImie.Text = PoleNazwisko.Text = PoleWiek.Text = „”;
Odswierz(_oryginalna);
}
private void SortAZ_Click(object s, EventArgs e) =>
Odswierz(_oryginalna.OrderBy(o => o.Nazwisko).ToList());
private void SortZA_Click(object s, EventArgs e) =>
Odswierz(_oryginalna.OrderByDescending(o => o.Nazwisko).ToList());
private void SortWiek_Click(object s, EventArgs e) =>
Odswierz(_oryginalna.OrderBy(o => o.Wiek).ToList());
private void Reset_Click(object s, EventArgs e) =>
Odswierz(_oryginalna);
private async void Zapisz_Click(object s, EventArgs e)
{
await File.WriteAllTextAsync(_sciezka, JsonSerializer.Serialize(_oryginalna));
StatusLabel.Text = $„Zapisano {_oryginalna.Count} rekordow.”;
}
private async void Wczytaj_Click(object s, EventArgs e)
{
if (!File.Exists(_sciezka)) { StatusLabel.Text = „Brak pliku.”; return; }
string json = await File.ReadAllTextAsync(_sciezka);
_oryginalna = JsonSerializer.Deserialize<List<Osoba>>(json) ?? new();
Odswierz(_oryginalna);
}
}
// Models/Osoba.cs
namespace SortowanieApp.Models;
public class Osoba
{
public string Imie { get; set; } = „”;
public string Nazwisko { get; set; } = „”;
public int Wiek { get; set; }
public string PelneNazwisko => $„{Imie} {Nazwisko}”;
}
Testy
Test jednostkowy MSTest – schemat
1
Dodaj projekt testowy do solucji
Prawy klik na solucie (nie projekt!) -> Dodaj -> Nowy projekt -> MSTest Test Project (.NET)
2
Dodaj referencje do projektu MAUI
Prawy klik na projekt testowy -> Dodaj -> Odwolanie do projektu -> zaznacz projekt MAUI
3
Napisz test i uruchom (Ctrl+R, A)
Testujesz metode z klasy Logika – nie testujesz UI.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MojaApp.Logika;
[TestClass]
public class KalkulatorTests
{
// Schemat AAA: Arrange – Act – Assert
[TestMethod]
public void ObliczBMI_PoprawneWartosci_ZwracaPoprawneBMI()
{
// Arrange
var kalk = new Kalkulator();
// Act
double wynik = kalk.ObliczBMI(70, 1.75);
// Assert
Assert.AreEqual(22.86, wynik, delta: 0.01);
}
[TestMethod]
public void ObliczBMI_ZeroWzrost_RzucaWyjatek()
{
var kalk = new Kalkulator();
Assert.ThrowsException<ArgumentException>(() => kalk.ObliczBMI(70, 0));
}
}
Przed oddaniem
Checklista – sprawdz zanim spakujesz ZIP
Aplikacja
- Projekt kompiluje sie bez bledow
- Aplikacja uruchamia sie na emulatorze
- Interfejs zgodny z projektem z arkusza
- Logika obliczeniowa w osobnej klasie
- Walidacja pokazuje komunikat przy zlych danych
- Zapis uzywa FileSystem.AppDataDirectory
- Odczyt dziala po ponownym uruchomieniu
- Brak sciezek bezwzglednych (C:\Users\…)
Dokumentacja i testy
- Zrzuty ekranu z dzialajcej aplikacji (min 2)
- Opis srodowiska: VS 2022, .NET MAUI, Android
- Komentarz XML nad metoda z numerem zdajacego
- Tabela przypadkow testowych (dane – wynik)
- Projekt MSTest z co najmniej jednym testem
- Test uruchamia sie i przechodzi (zielony)
- Projekt spakowany do ZIP
- Nazwa ZIP zgodna z numerem zdajacego
Najczestsze straty punktow: brak komentarza XML do metody, sciezka bezwzgledna do pliku, logika obliczeniowa w obsludze przycisku zamiast w osobnej klasie, brak zrzutow ekranu. To sa punkty do wziecia bez zadnego wysilku – tylko pamietaj o nich przed oddaniem.
.NET MAUI · INF.04 · bitedu.pl · technik programisty