Lekcja 4. Pierwsza aplikacja WPF — struktura projektu i podstawy XAML
PodstawowyPo co się tego uczymy?
Czas napisać pierwszą prawdziwą aplikację okienkową. Ta lekcja pokazuje, z jakich plików składa się projekt WPF, jak wygląda podstawowa składnia XAML i jak połączyć wygląd okna (XAML) z kodem C# obsługującym logikę (tzw. code-behind) — fundament pod wszystko, co będzie się działo w tym dziale.
Teoria
Krok po kroku — tworzenie nowego projektu WPF:
- Visual Studio → Utwórz nowy projekt (albo Plik → Nowy → Projekt, jeśli VS jest już otwarte).
- W wyszukiwarce wpisz
WPFi wybierz szablon "Aplikacja WPF (.NET)" — NIE mylić z podobnie nazwanym, starszym szablonem "WPF App (.NET Framework)". - Kliknij Dalej, podaj nazwę projektu i lokalizację na dysku, kliknij Dalej ponownie.
- Wybierz Framework (.NET 8.0 lub nowszy) i kliknij Utwórz.
- Visual Studio otworzy gotowy plik
MainWindow.xamlz podglądem okna po prawej stronie i kodem XAML po lewej — to Twój punkt startowy. - Uruchom aplikację klawiszem F5 — powinno pojawić się puste okno o tytule "MainWindow".
Jeśli szablon WPF nie jest widoczny na liście, otwórz Visual Studio Installer i zaznacz komponent ".NET desktop development".
Nowy projekt WPF w Visual Studio ("Aplikacja WPF (.NET)") tworzy kilka kluczowych plików:
- App.xaml i App.xaml.cs — punkt startowy całej aplikacji, definiuje globalne zasoby (np. style współdzielone przez wszystkie okna) i wskazuje, które okno otworzyć jako pierwsze;
- MainWindow.xaml — opisuje WYGLĄD głównego okna w języku XAML (rozmieszczenie kontrolek, kolory, czcionki);
- MainWindow.xaml.cs — tzw. code-behind, czyli kod C# powiązany z tym oknem — tutaj piszemy logikę reagującą na kliknięcia, wpisywany tekst itd.
Ten podział na plik XAML (wygląd) i plik .cs (logika) to fundamentalna zasada WPF — nazywana separation of concerns (rozdzieleniem odpowiedzialności). Każdy element w XAML, który ma być używany z poziomu kodu C#, dostaje atrybut x:Name (unikalną nazwę), dzięki czemu można się do niego odwołać w code-behind, np. NazwaEtykiety.Content = "Nowy tekst";.
Podstawowe elementy składni XAML: każdy znacznik to jeden obiekt (kontrolka albo kontener), atrybuty znacznika ustawiają jego właściwości (np. Width, Height, Background), a zagnieżdżanie znaczników odzwierciedla hierarchię wizualną — kontener na zewnątrz, kontrolki wewnątrz.
Żeby coś się faktycznie DZIAŁO po kliknięciu przycisku, trzeba podpiąć obsługę zdarzenia: w XAML dodajemy atrybut Click="NazwaMetody" do przycisku, a w code-behind piszemy metodę o tej samej nazwie, która wykona się przy kliknięciu.
Schemat
Struktura plikow projektu WPF:
MojaAplikacja/
├── App.xaml <- punkt startowy, zasoby globalne
├── App.xaml.cs
├── MainWindow.xaml <- WYGLAD okna (XAML)
├── MainWindow.xaml.cs <- LOGIKA okna (C#, "code-behind")
└── MojaAplikacja.csproj
Polaczenie XAML <-> C#:
MainWindow.xaml: MainWindow.xaml.cs:
<Button x:Name="PrzyciskOK" private void Przycisk_Click(
Content="OK" object sender, RoutedEventArgs e)
Click="Przycisk_Click" /> {
// kod wykonywany po kliknieciu
}
Przykład z życia
To rozdzielenie wyglądu od logiki działa podobnie jak w tworzeniu stron internetowych: HTML/CSS (jak wygląda strona) jest oddzielone od JavaScript/PHP (co się dzieje po kliknięciu). W dużych zespołach projektant UI/UX może pracować nad plikiem XAML (rozmieszczenie, kolory, style), podczas gdy programista równolegle pisze logikę biznesową w C#, nie przeszkadzając sobie nawzajem.
Kod (XAML)
<!-- MainWindow.xaml -->
<Window x:Class="MojaPierwszaAplikacja.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Moja pierwsza aplikacja WPF" Height="250" Width="400">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock x:Name="EtykietaPowitania"
Text="Witaj w WPF!"
FontSize="20"
Margin="0,0,0,20" />
<Button x:Name="PrzyciskPrzywitaj"
Content="Kliknij mnie"
Width="140"
Height="35"
Click="PrzyciskPrzywitaj_Click" />
</StackPanel>
</Grid>
</Window>
Komentarz i wyjaśnienie kodu
W pliku MainWindow.xaml mamy okno (Window) zawierające Grid (kontener), a wewnątrz niego StackPanel (układa elementy jeden pod drugim), w którym są: etykieta tekstowa (TextBlock) z nazwą EtykietaPowitania oraz przycisk (Button) z nazwą PrzyciskPrzywitaj i atrybutem Click="PrzyciskPrzywitaj_Click" — to właśnie ta nazwa metody musi pojawić się w pliku C# poniżej.
W pliku MainWindow.xaml.cs (code-behind) napiszemy:
private void PrzyciskPrzywitaj_Click(object sender, RoutedEventArgs e)
{
EtykietaPowitania.Text = "Kliknięto przycisk!";
}
Metoda PrzyciskPrzywitaj_Click ma zawsze dwa parametry: sender (obiekt, który wywołał zdarzenie — tutaj przycisk) i RoutedEventArgs e (szczegóły zdarzenia). Wewnątrz odwołujemy się do etykiety po jej nazwie (x:Name) i zmieniamy jej właściwość Text — dokładnie tak, jak w JavaScript zmienia się zawartość elementu HTML po jego id.
Ćwiczenie samodzielne
Utwórz nowy projekt "Aplikacja WPF (.NET)" w Visual Studio, wklej powyższy XAML do MainWindow.xaml, dopisz metodę PrzyciskPrzywitaj_Click w code-behind (zmieniającą tekst etykiety) i uruchom aplikację (F5). Sprawdź, czy kliknięcie przycisku rzeczywiście zmienia tekst.
Zadania do pracy własnej
Dodaj drugi przycisk, który po kliknięciu zmienia kolor tła okna (
Grid.Background) na dowolny inny kolor.Dodaj pole tekstowe (
TextBox) i przycisk "Przywitaj się". Po kliknięciu przycisku etykieta powinna wyświetlić "Cześć, [to co wpisano w polu tekstowym]!".Zbuduj okno z trzema przyciskami reprezentującymi kolory (Czerwony/Zielony/Niebieski), gdzie kliknięcie każdego zmienia zarówno tło okna, jak i tekst etykiety informujący, który kolor został wybrany — wszystko obsłużone w jednej wspólnej metodzie C# rozróżniającej przyciski przez parametr
sender.
Typowe błędy
Literówka w nazwie metody obsługi zdarzenia — jeśli w XAML jest Click="Zrob_Cos", a w C# metoda nazywa się inaczej, aplikacja nie skompiluje się (błąd "nie znaleziono metody"). Nazwy muszą być IDENTYCZNE.
Zapominanie o x:Name — bez tego atrybutu nie da się odwołać do kontrolki z poziomu C#, bo nie ma ona żadnej nazwy w kodzie.
Mylenie właściwości Content i Text — Button i Label używają Content, natomiast TextBlock i TextBox używają Text. To częste źródło błędów kompilacji dla początkujących.
Nawiązanie do egzaminu zawodowego
To bezpośrednia realizacja INF.04.5.3 — "programuje desktopowe aplikacje okienkowe: okno, kontrolki, XAML, obsługa zdarzeń myszy/klawiatury". Rozdzielenie XAML/code-behind poznane tutaj to fundament, do którego wrócimy w lekcji o oddzieleniu logiki od UI (wzorzec MVVM).