Nauczysz się rozdzielać widok (UI) od logiki w dwóch technologiach desktopowych:
- WPF (nowocześniejszy XAML na Windows)
- Windows Forms (klasyczny, najprostszy do startu)
Zrobimy prosty formularz: Imię + Nazwisko → Wyślij → wynik. Logika łączenia napisów będzie w osobnej klasie.
Dlaczego to robimy?
- Porządek: UI (przyciski, pola) nie miesza się z obliczeniami/zasadami.
- Czytelność: łatwiej znaleźć miejsce, gdzie zmienić wygląd lub regułę działania.
- Testowalność: logikę można sprawdzić bez uruchamiania okienka.
- Skalowalność: rośnie projekt, a Ty dodajesz kolejne funkcje do klasy logiki.
Co oddzielamy?
- UI (widok) – XAML (WPF) albo Designer (WinForms).
- Klej (code-behind) – metody obsługi przycisków i wymiana danych z UI.
- Logika – zwykła klasa C# (
Formater), nie zna kontrolek.
Mechanika łączenia (ogólny schemat)
- UI nadaje kontrolkom nazwy (WPF:
x:Name, WinForms:Name). - Przycisk wywołuje zdarzenie (WPF:
Click, WinForms:Click). - W code-behind odczytujesz pola → wołasz metodę logiki → ustawiasz wynik na etykiecie.
- Logika to osobna klasa z metodami – przyjmuje dane, zwraca tekst.
CZĘŚĆ A – WPF
Struktura projektu (drzewo)
MojaAppWpf
┣ App.xaml
┣ App.xaml.cs
┣ MainWindow.xaml ← UI (widok)
┣ MainWindow.xaml.cs ← code-behind (zdarzenia, odczyt/zapis)
┣ Logika
┃ ┗ Formater.cs ← czysta logika
Folder
Logikaułatwia porządek (można też bez folderu).
Krok po kroku (Visual Studio – interfejs PL)
- Nowy projekt → „Aplikacja WPF (.NET 6/7/8)” → nazwa:
MojaAppWpf. - PPM na projekt → Dodaj → Nowy folder →
Logika. - PPM na folder
Logika→ Dodaj → Klasa… →Formater.cs. - Otwórz
MainWindow.xamli wklej XAML poniżej. - Otwórz
MainWindow.xaml.csi wklej code-behind. - Zbuduj i uruchom.
1) MainWindow.xaml (UI)
<Window x:Class="MojaAppWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Formularz" Height="280" Width="420">
<StackPanel Margin="20">
<TextBox x:Name="PoleImie" Margin="0,0,0,8"/>
<TextBox x:Name="PoleNazwisko" Margin="0,0,0,8"/>
<Button Content="Wyślij" Click="PrzyciskWyslij_Click" Margin="0,0,0,8"/>
<TextBlock x:Name="WynikLabel" FontWeight="Bold" Text="Tu pojawi się wynik"/>
</StackPanel>
</Window>
Wyjaśnienia (linia po linii):
<Window …>– główne okno aplikacji WPF.x:Class="MojaAppWpf.MainWindow"– wiąże ten XAML z klasąMainWindoww.xaml.cs.xmlns=…ixmlns:x=…– standardowe przestrzenie nazw XAML (potrzebne do składni).Title/Height/Width– tytuł i rozmiar okna.<StackPanel>– układ pionowy; elementy są jeden pod drugim.Margin– odstępy.<TextBox x:Name="PoleImie" …/>– pole do wpisania imienia;x:Namenadaje nazwę widoczną w C#.<TextBox x:Name="PoleNazwisko" …/>– pole na nazwisko.<Button Content="Wyślij" Click="PrzyciskWyslij_Click"/>– przycisk; po kliknięciu wykona metodę w C#.<TextBlock x:Name="WynikLabel" …/>– napis do pokazania wyniku.
2) MainWindow.xaml.cs (code-behind)
using MojaAppWpf.Logika; // pozwala użyć klasy Formater
using System.Windows; // typy WPF (Window, RoutedEventArgs)
namespace MojaAppWpf;
public partial class MainWindow : Window
{
private readonly Formater _formater = new(); // obiekt logiki
public MainWindow()
{
InitializeComponent(); // tworzy kontrolki z XAML i łączy je z polami (PoleImie, WynikLabel)
}
private void PrzyciskWyslij_Click(object sender, RoutedEventArgs e)
{
string imie = PoleImie.Text; // odczyt z TextBox
string nazwisko = PoleNazwisko.Text;
string wynik = _formater.PolaczImieNazwisko(imie, nazwisko); // logika w osobnej klasie
WynikLabel.Text = wynik; // wyświetlenie wyniku w UI
}
}
Wyjaśnienia:
using MojaAppWpf.Logika;– import przestrzeni nazw z klasąFormater.partial class MainWindow– definicja klasy jest „w dwóch plikach” (XAML + CS).InitializeComponent()– kluczowa metoda, która „wczytuje” XAML.PrzyciskWyslij_Click– metoda wywoływana po kliknięciu.- UI ↔ logika: odczyt →
_formater→ ustawienie wyniku.
3) Logika/Formater.cs (czysta logika)
namespace MojaAppWpf.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:
- Zwykła klasa C#, bez żadnych odniesień do kontrolek.
- Metoda przyjmuje dwa napisy i zwraca gotowy komunikat.
- Walidacja pustych wartości – przyjazne komunikaty dla użytkownika.
CZĘŚĆ B – Windows Forms
Struktura projektu (drzewo)
MojaAppWinForms
┣ Program.cs ← punkt startowy aplikacji
┣ Form1.cs ← code-behind okna
┣ Form1.Designer.cs ← plik generowany przez projektant (UI)
┣ Logika
┃ ┗ Formater.cs ← czysta logika
W WinForms UI budujesz w Designerze przeciągając kontrolki. Kod XAML nie występuje.
Krok po kroku (Visual Studio – interfejs PL)
- Nowy projekt → „Aplikacja Windows Forms (.NET 6/7/8)” →
MojaAppWinForms. - Na formularz Form1 przeciągnij z Toolbox:
TextBox(Imię),TextBox(Nazwisko),Button(Wyślij),Label(wynik). UstawName:txtImie,txtNazwisko,btnWyslij,lblWynik. - Kliknij dwa razy przycisk „Wyślij”, aby Visual Studio wygenerowało zdarzenie
btnWyslij_ClickwForm1.cs. - PPM na projekt → Dodaj → Nowy folder →
Logika. W nim Dodaj → Klasa… →Formater.cs. - Wklej kody poniżej.
1) Form1.cs (code-behind formularza)
Ten plik ma już sekcję
partial class Form1 : Form. Poniżej pokazuję fragmenty, które są istotne.
using MojaAppWinForms.Logika; // pozwala odwołać się do Formater
namespace MojaAppWinForms;
public partial class Form1 : Form
{
private readonly Formater _formater = new(); // obiekt logiki
public Form1()
{
InitializeComponent(); // tworzy kontrolki z Designera i łączy je z polami (txtImie, lblWynik)
}
private void btnWyslij_Click(object sender, EventArgs e)
{
string imie = txtImie.Text; // odczyt z TextBox
string nazwisko = txtNazwisko.Text;
string wynik = _formater.PolaczImieNazwisko(imie, nazwisko); // logika w osobnej klasie
lblWynik.Text = wynik; // wyświetlenie wyniku w UI
}
}
Wyjaśnienia:
InitializeComponent()– generowany kod, który tworzy i rozmieszcza kontrolki (wForm1.Designer.cs).btnWyslij_Click– metoda wywoływana po kliknięciu przycisku (Designer sam podłączy zdarzenie).
2) Form1.Designer.cs (UI – generowany)
Visual Studio wygeneruje to za Ciebie, gdy dodasz kontrolki w Designerze. Najważniejsze, żeby ustawić Name kontrolek tak, jak używasz w kodzie (
txtImie,txtNazwisko,btnWyslij,lblWynik).
3) Logika/Formater.cs (wspólna z WPF)
namespace MojaAppWinForms.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: identyczne jak w WPF – i o to chodzi. Logika jest uniwersalna.
Porównanie: WPF vs Windows Forms (w skrócie)
| Element | WPF | WinForms |
|---|---|---|
| UI | XAML | Designer (kod generowany w .Designer.cs) |
| Etykieta | TextBlock/Label | Label |
| Pole tekstowe | TextBox | TextBox |
| Zdarzenie przycisku | Click | Click |
| Łączenie UI↔kod | x:Name, InitializeComponent() | Name, InitializeComponent() |
| Logika | osobna klasa | osobna klasa |
Najczęstsze pytania (FAQ)
Czy muszę mieć folder Logika?
Nie. To dobra praktyka. Możesz dać Formater.cs obok okna.
Czy mogę użyć tej samej klasy logiki w obu projektach?
Tak – najlepiej wynieść ją do biblioteki klas (projekt .csproj) i odwołać się z WPF/WinForms.
Co jeśli chcę więcej akcji (np. „Nazwisko, Imię”)?
Dodaj kolejną metodę w Formater (np. FormatujNazwiskoImie) i drugi przycisk, który ją wywoła.
Ćwiczenia:
- Dodaj drugi przycisk „Wyczyść”, który czyści oba pola i etykietę.
- Dodaj walidację: Imię min. 2 znaki; jeśli krótsze – pokaż komunikat.
- Dodaj metodę
FormatujNazwiskoImie(wynik:Kowalski, Jan) i podłącz do nowego przycisku. - (Ambitne) Przenieś klasę
Formaterdo osobnej biblioteki klas i podłącz do projektu WPF/WinForms.
Podsumowanie (do zapamiętania)
- UI ≠ logika – rozdzielaj od początku.
- WPF i WinForms różnią się sposobem tworzenia UI, ale schemat pracy jest ten sam.
InitializeComponent()łączy UI z kodem.- Zdarzenia (Click) to „most” między UI a logiką.
- Osobna klasa z metodami = porządek, testowalność, skalowalność.
Gotowe. Materiał możesz wkleić na stronę jako lekcję. Jeśli chcesz, dorzucę jeszcze wersję PDF/DOCX z tym materiałem i obrazkami ekranu z Visual Studio (krok po kroku).

