Oddzielenie logiki od UI w .NET MAUI

Zrozumiesz, po co rozdzielać widok (UI) od logiki i jak to zrobić w praktyce na prostym przykładzie: formularz z Imię + Nazwisko → przycisk Wyślij → etykieta z wynikiem.

Dlaczego to robimy?

  • Porządek: w MainPage.xaml.cs zostaje tylko obsługa przycisku i odczyt/zapis do kontrolek. Obliczenia/zasady idą do osobnej klasy.
  • Łatwiejsze testowanie: logikę można sprawdzić bez uruchamiania aplikacji.
  • Rozszerzalność: dodajesz kolejne funkcje w klasie logiki, UI się nie sypie.

Co oddzielamy?

  • UI (widok) – układ i wygląd kontrolek: MainPage.xaml.
  • Łączenie z UI – minimalny kod, który reaguje na kliknięcia i przekazuje dane: MainPage.xaml.cs.
  • Logika – czysta klasa z metodami, bez wiedzy o przyciskach/Entry/Label: Logika/Formater.cs.

Jak to się łączy (mechanika)

  1. x:Name w XAML nadaje kontrolce nazwę (np. PoleImie). Dzięki temu ta kontrolka jest widoczna w MainPage.xaml.cs jako pole.
  2. Clicked="PrzyciskWyslij_Click" podłącza zdarzenie przycisku do metody w MainPage.xaml.cs.
  3. InitializeComponent() w konstruktorze MainPage „wczytuje” XAML i tworzy kontrolki.
  4. using MojaApp.Logika; pozwala użyć klasy z osobnego pliku/folderu.
  5. Tworzymy obiekt logiki (new Formater()) i wołamy jego metody.

Drzewo aplikacji

MojaApp
 ┣ App.xaml
 ┣ App.xaml.cs
 ┣ MainPage.xaml            ← UI (widok)
 ┣ MainPage.xaml.cs         ← połączenie z UI (eventy, odczyt/zapis)
 ┣ Logika
 ┃   ┗ Formater.cs         ← czysta logika
 ┗ ... (inne pliki)

Folder Logika nie jest obowiązkowy, ale pomaga utrzymać porządek. Możesz też umieścić Formater.cs obok MainPage.xaml.cs.


Krok po kroku w Visual Studio (PL interfejs)

  1. Dodaj widok: projekt MAUI już ma MainPage.xaml.
  2. Dodaj folder: PPM na projekt → DodajNowy folderLogika.
  3. Dodaj klasę logiki: PPM na LogikaDodajKlasa…Formater.cs.
  4. Połącz przycisk z metodą: w XAML ustaw Clicked="PrzyciskWyslij_Click", a w MainPage.xaml.cs dopisz metodę PrzyciskWyslij_Click.

Pliki i pełne kody (z objaśnieniami linia po linii)

1) MainPage.xaml (UI)

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MojaApp.MainPage"
             Title="Formularz">

    <VerticalStackLayout Padding="20" Spacing="12">
        <Entry x:Name="PoleImie" Placeholder="Imię" />
        <Entry x:Name="PoleNazwisko" Placeholder="Nazwisko" />
        <Button Text="Wyślij" Clicked="PrzyciskWyslij_Click" />
        <Label x:Name="WynikLabel" Text="Tu pojawi się wynik" FontAttributes="Bold" />
    </VerticalStackLayout>
</ContentPage>

Wyjaśnienia:

  • <ContentPage ...> – strona widoku.
  • xmlns="...maui" – standardowe „adresy” opisujące język XAML dla MAUI.
  • xmlns:x=".../xaml" – dodatkowe funkcje XAML (np. x:Name).
  • x:Class="MojaApp.MainPage" – pełna nazwa klasy, która łączy ten XAML z plikiem MainPage.xaml.cs.
  • Title="Formularz" – tytuł strony.
  • <VerticalStackLayout ...> – układ, który ustawia elementy pionowo.
  • Padding="20" – wewnętrzny margines kontenera (odstęp od krawędzi).
  • Spacing="12" – odstęp między elementami wewnątrz.
  • <Entry x:Name="PoleImie" .../> – pole tekstowe; x:Name nadaje mu nazwę widoczną w C#.
  • <Entry x:Name="PoleNazwisko" .../> – drugie pole.
  • <Button Text="Wyślij" Clicked="PrzyciskWyslij_Click"/> – przycisk; Clicked wskazuje metodę w C#, która wykona się po kliknięciu.
  • <Label x:Name="WynikLabel" .../> – etykieta na wynik; też ma x:Name, więc można zmieniać Text z C#.

2) MainPage.xaml.cs (połączenie z UI)

using MojaApp.Logika; // pozwala odwołać się do klasy Formater z folderu Logika

namespace MojaApp;

public partial class MainPage : ContentPage
{
    private readonly Formater _formater = new(); // tworzymy obiekt logiki raz i używamy go w zdarzeniach

    public MainPage()
    {
        InitializeComponent(); // wczytuje XAML, tworzy kontrolki i łączy je z polami (np. PoleImie)
    }

    private void PrzyciskWyslij_Click(object sender, EventArgs e)
    {
        // 1) Odczyt tekstu z pól Entry
        string imie = PoleImie.Text;       // to jest string (ciąg znaków) z kontrolki Entry
        string nazwisko = PoleNazwisko.Text;

        // 2) Logika w osobnej klasie – przekazujemy dane i dostajemy wynik
        string wynik = _formater.PolaczImieNazwisko(imie, nazwisko);

        // 3) Wyświetlenie wyniku w etykiecie
        WynikLabel.Text = wynik;
    }
}

Wyjaśnienia:

  • using MojaApp.Logika; – import przestrzeni nazw, gdzie jest klasa Formater.
  • namespace MojaApp; – przestrzeń nazw (żeby klasy miały unikalne „adresy”).
  • public partial class MainPage : ContentPagepartial oznacza, że definicja klasy jest podzielona między XAML i ten plik C#.
  • Formater _formater = new(); – tworzymy obiekt logiki do użycia.
  • InitializeComponent();najważniejsza linia łącząca XAML z C#: tworzy kontrolki, nadaje im referencje (np. PoleImie).
  • PrzyciskWyslij_Click(...) – metoda wywoływana po kliknięciu (bo wskazaliśmy ją w XAML w Clicked="...").
  • PoleImie.Text / PoleNazwisko.Text – odczyt tekstu wpisanego przez użytkownika.
  • _formater.PolaczImieNazwisko(...) – przekazujemy dane do logiki i dostajemy gotowy wynik.
  • WynikLabel.Text = wynik; – aktualizujemy UI.

3) Logika/Formater.cs (czysta logika)

namespace MojaApp.Logika;

public class Formater
{
    public string PolaczImieNazwisko(string imie, string nazwisko)
    {
        if (string.IsNullOrWhiteSpace(imie) && string.IsNullOrWhiteSpace(nazwisko))
            return "Proszę podać imię i nazwisko.";

        if (string.IsNullOrWhiteSpace(imie))
            return $"Nazwisko: {nazwisko}";

        if (string.IsNullOrWhiteSpace(nazwisko))
            return $"Imię: {imie}";

        return $"{imie} {nazwisko}";
    }
}

Wyjaśnienia:

  • namespace MojaApp.Logika; – ta klasa żyje w przestrzeni nazw MojaApp.Logika.
  • public class Formater – zwykła klasa, nie wie nic o przyciskach, Entry czy Label.
  • PolaczImieNazwisko(...) – metoda przyjmuje dwa napisy i zwraca jeden wynik.
  • string.IsNullOrWhiteSpace(...) – sprawdza, czy tekst jest pusty lub tylko ze spacjami. Dzięki temu ładnie obsługujemy brak danych.
  • Zwracane komunikaty są już „gotowe do wyświetlenia” – UI nie musi nic liczyć ani formować.

Co warto zapamiętać

  • XAML – opisuje co i jak wygląda.
  • code-behind (xaml.cs)zbiera dane z UI i wywołuje logikę.
  • osobny plik klasyrobi przeliczenia/zasady i zwraca gotowy wynik.
  • Spina to wszystko: x:Name, Clicked="Metoda", InitializeComponent(), using <TwojaPrzestrzen>, new Klasa() i wywołanie metod.

Ćwiczenie:

  1. Zmień logikę tak, aby wynik był w formacie: NAZWISKO, Imię (np. Kowalski, Jan).
  2. Dodaj walidację: jeśli imię jest krótsze niż 2 znaki, pokaż komunikat: Imię jest za krótkie.
  3. Dodaj drugi przycisk „Wyczyść”, który czyści oba pola i etykietę.

Podsumowanie

Rozdzielenie logiki od UI to fundament większych aplikacji. Dzięki temu:

  • łatwiej utrzymasz porządek,
  • szybciej znajdziesz błędy,
  • łatwiej przygotujesz się do testów jednostkowych (następny temat).