XAML – język opisu interfejsu
Poznasz składnię XAML – języka, w którym opisujesz jak wygląda Twoja aplikacja. Dowiesz się jak działają elementy, atrybuty i jak XAML łączy się z kodem C#.
Czym jest XAML?
XAML (czytaj: „zaml”, ang. eXtensible Application Markup Language) to język, w którym opisujesz jak wygląda Twoja aplikacja – jakie ma przyciski, pola tekstowe, gdzie są rozmieszczone i jakiego są koloru.
Jeśli kiedykolwiek widziałeś HTML, XAML będzie Ci znajomy. Tak jak HTML opisuje co jest na stronie internetowej, XAML opisuje co jest w oknie aplikacji WPF. C# to logika – co się dzieje gdy użytkownik coś kliknie.
| Strona WWW | Aplikacja WPF |
|---|---|
| HTML – struktura i treść | XAML – wygląd okna |
| CSS – style i kolory | XAML Styles – style i kolory |
| JavaScript – logika | C# – logika |
XAML opiera się na XML – jeśli znasz HTML, składnia będzie Ci bliska.
Elementy mają otwierający znacznik <Nazwa>, zamykający
</Nazwa>, i mogą być samozamykające: <Nazwa />.
Składnia XAML
Elementy i atrybuty
Każda kontrolka w XAML to element – zapisany w nawiasach trójkątnych.
Właściwości kontrolki to atrybuty – zapisane wewnątrz tagu w formacie
Nazwa="Wartość".
<!-- element ↓ atrybut="wartość" --> <Button Content="Kliknij mnie" Width="120" Height="35" Background="SteelBlue" Foreground="White" /> <!-- ↑ samozamykający -->
Samozamykający znacznik /> na końcu stosujemy wtedy,
gdy element nie ma dzieci (nie zawiera innych elementów w środku).
<!-- Jeśli element ma zawartość – używamy pary znaczników --> <StackPanel> <Button Content="Przycisk 1" /> <Button Content="Przycisk 2" /> <Button Content="Przycisk 3" /> </StackPanel> <!-- ↑ StackPanel "zawiera" trzy przyciski -->
Zagnieżdżanie elementów
Elementy XAML mogą być zagnieżdżane – jeden wewnątrz drugiego. Tworzy to drzewo elementów. Każdy element ma swojego rodzica i może mieć dzieci.
<Window> <!-- korzeń – okno --> <Grid> <!-- Grid jest dzieckiem Window --> <StackPanel> <!-- StackPanel jest dzieckiem Grid --> <TextBlock Text="Witaj!" /> <!-- dziecko StackPanel --> <Button Content="OK" /> <!-- dziecko StackPanel --> </StackPanel> </Grid> </Window>
Większość kontenerów layoutu (Grid, StackPanel, DockPanel) może mieć
wiele dzieci. Ale samo okno <Window>
i wiele kontrolek (np. <Button>) może mieć
tylko jedno dziecko.
Dlatego wewnątrz okna zawsze musi być najpierw kontener (np. Grid),
a dopiero w nim reszta elementów.
Komentarze w XAML
Komentarze zapisujemy tak samo jak w HTML:
<Grid> <!-- To jest komentarz – nie pojawi się w aplikacji --> <Button Content="OK" /> <!-- Komentarz może być wieloliniowy --> </Grid>
Dwa sposoby zapisu właściwości
Właściwości kontrolki możesz zapisać na dwa sposoby. Oba dają ten sam efekt – różnią się tylko składnią.
Sposób 1 – Atrybut (krótki zapis)
Najprostszy i najczęściej używany. Właściwość to atrybut bezpośrednio wewnątrz tagu – wartość zawsze w cudzysłowie.
<Button Content="Zapisz" Width="100" Height="35" Background="SteelBlue" Foreground="White" />
Sposób 2 – Element właściwości (rozbudowany zapis)
Gdy wartość właściwości jest złożona – np. gradient kolorów,
lub gdy nie zmieści się w jednej linii jako tekst – używamy zapisu elementowego.
Wygląda jak zagnieżdżony element z kropką w nazwie:
<NazwaKontrolki.NazwaWłaściwości>.
<Button Width="150" Height="50"> <!-- Właściwość Content zapisana jako element --> <Button.Content> <StackPanel Orientation="Horizontal"> <TextBlock Text="💾 Zapisz" /> </StackPanel> </Button.Content> <!-- Właściwość Background zapisana jako gradient --> <Button.Background> <LinearGradientBrush> <GradientStop Color="SteelBlue" Offset="0" /> <GradientStop Color="DarkBlue" Offset="1" /> </LinearGradientBrush> </Button.Background> </Button>
| Sytuacja | Użyj |
|---|---|
| Prosta wartość: tekst, liczba, kolor po nazwie | Atrybutu: Background="Red" |
| Złożona wartość: gradient, szablon, powiązanie danych | Elementu właściwości |
| Zawartość kontrolki to inna kontrolka | Elementu właściwości |
Na początku kursu prawie zawsze wystarczy zapis atrybutowy. Elementowego będziesz używać częściej w zaawansowanych tematach (Style, Data Binding, animacje).
Nazywanie elementów – x:Name
Aby móc odwołać się do kontrolki z kodu C#, musisz nadać jej nazwę
za pomocą atrybutu x:Name.
Bez nazwy kontrolka jest anonimowa – widać ją w oknie,
ale nie możesz jej dotknąć z C#.
<Grid> <!-- Przycisk bez nazwy – nie można go obsłużyć z C# --> <Button Content="Anonimowy" /> <!-- Przycisk z nazwą – można go używać z C# --> <Button x:Name="btnZapisz" Content="Zapisz" Click="btnZapisz_Click" /> <!-- Pole tekstowe z nazwą --> <TextBox x:Name="txtImie" /> <!-- Etykieta wynikowa z nazwą --> <TextBlock x:Name="lblWynik" Text="Wynik: –" /> </Grid>
private void btnZapisz_Click(object sender, RoutedEventArgs e) { // Dzięki x:Name możemy odczytać tekst z pola string imie = txtImie.Text; // I wyświetlić go w etykiecie lblWynik.Text = $"Witaj, {imie}!"; // Możemy też wyłączyć przycisk po kliknięciu btnZapisz.IsEnabled = false; }
Nadawaj nazwy tylko tym kontrolkom, do których będziesz odwoływać się z C#. Layoutów (Grid, StackPanel) zwykle nie nazywamy, chyba że są potrzebne w kodzie. Nie nazywaj wszystkiego z przyzwyczajenia – niepotrzebne nazwy zaśmiecają kod.
Name vs x:Name
W WPF można użyć zarówno x:Name jak i Name – dla kontrolek
działają identycznie. Przyjęło się używać x:Name, bo jest bardziej
jawne i działa wszędzie (np. przy własnych kontrolkach).
XAML ↔ C# – jak to działa?
XAML i C# to dwie strony tej samej monety. XAML opisuje wygląd, C# opisuje zachowanie. Razem tworzą jedno okno.
Kiedy kompilujesz projekt, Visual Studio zamienia Twój plik XAML
na kod C# – tworzy metodę InitializeComponent(),
która buduje wszystkie kontrolki w pamięci.
Twój plik MainWindow.xaml.cs i ten wygenerowany kod
to razem jedna klasa (słowo kluczowe partial).
Odczytywanie właściwości z C#
<StackPanel> <TextBox x:Name="txtImie" Text="Jan" /> <TextBox x:Name="txtWiek" Text="18" /> <TextBlock x:Name="lblWynik" Text="–" /> <Button x:Name="btnSprawdz" Content="Sprawdź" Click="btnSprawdz_Click" /> </StackPanel>
private void btnSprawdz_Click(object sender, RoutedEventArgs e) { // Odczytujemy Text z TextBox string imie = txtImie.Text; // Konwersja tekstu na liczbę int wiek; if (!int.TryParse(txtWiek.Text, out wiek)) { lblWynik.Text = "Wiek musi być liczbą!"; return; } // Zapisujemy wynik do TextBlock lblWynik.Text = $"Cześć {imie}, masz {wiek} lat."; }
Zmiana właściwości z C#
Każdy atrybut z XAML ma odpowiadającą mu właściwość w C#. Możesz je zmieniać w dowolnym momencie działania programu.
// Zmiana tekstu lblWynik.Text = "Nowy tekst"; // Włączenie / wyłączenie kontrolki btnZapisz.IsEnabled = false; // Ukrycie kontrolki btnZapisz.Visibility = Visibility.Hidden; // niewidoczna, ale zajmuje miejsce btnZapisz.Visibility = Visibility.Collapsed; // niewidoczna, nie zajmuje miejsca btnZapisz.Visibility = Visibility.Visible; // widoczna (domyślne) // Zmiana koloru tła btnZapisz.Background = new SolidColorBrush(Colors.Green); // Zmiana rozmiaru czcionki lblWynik.FontSize = 18; // Zmiana szerokości i wysokości btnZapisz.Width = 200; btnZapisz.Height = 50;
| Wartość | Widoczna? | Zajmuje miejsce? | Kiedy używać? |
|---|---|---|---|
Visible | ✅ Tak | ✅ Tak | Normalny stan |
Hidden | ❌ Nie | ✅ Tak | Ukrywanie bez przesuwania innych |
Collapsed | ❌ Nie | ❌ Nie | Ukrywanie z usuniętym miejscem |
Tworzenie elementów w C# (bez XAML)
Wszystko co możesz zrobić w XAML, możesz też zrobić w C#. Przydaje się gdy chcesz dynamicznie tworzyć kontrolki (np. w pętli).
// To samo co <Button Content="Kliknij" Width="100" Background="Red"/> Button btn = new Button(); btn.Content = "Kliknij"; btn.Width = 100; btn.Background = new SolidColorBrush(Colors.Red); btn.Click += MojPrzycisk_Click; // Dodanie do istniejącego StackPanel o nazwie panelGlowny panelGlowny.Children.Add(btn); // Tworzenie wielu przycisków w pętli for (int i = 1; i <= 5; i++) { Button nowyBtn = new Button(); nowyBtn.Content = $"Przycisk {i}"; nowyBtn.Margin = new Thickness(5); panelGlowny.Children.Add(nowyBtn); }
Kontrolki tworzone w C# musisz dodać do kontenera
(Children.Add()), żeby pojawiły się na ekranie.
Sam fakt new Button() nic nie wyświetla.
Konwencja nazewnictwa kontrolek
Dobrą praktyką jest nazywanie kontrolek z przedrostkiem oznaczającym ich typ. Dzięki temu od razu wiesz z jakiej kontrolki korzystasz w kodzie C# – bez zaglądania do XAML.
| Kontrolka | Przedrostek | Przykłady nazw |
|---|---|---|
Button | btn | btnZapisz, btnAnuluj, btnUsun |
TextBox | txt | txtImie, txtEmail, txtHaslo |
TextBlock | lbl lub tb | lblWynik, lblBladStanu |
Label | lbl | lblTytul, lblOpis |
CheckBox | chk | chkZapisz, chkZalogowany |
RadioButton | rb | rbMezczyzna, rbKobieta |
ComboBox | cmb | cmbKategoria, cmbRok |
ListBox | lst | lstProdukty, lstUzytkownicy |
ListView | lv | lvZamowienia, lvWyniki |
DataGrid | dg | dgPracownicy, dgTowary |
Image | img | imgAwatar, imgLogo |
Slider | sld | sldGlosnosc, sldJasnosc |
ProgressBar | pb | pbPostep, pbLadowanie |
PasswordBox | pwd | pwdHaslo |
TabControl | tab | tabGlowny |
Canvas | cvs | cvsPlanszaGry |
W projektach edukacyjnych możesz używać polskich nazw (bez polskich liter!)
– btnZapisz, txtImie, lblWynik.
W projektach zawodowych dominuje angielski – btnSave,
txtName, lblResult.
Najważniejsza zasada: bądź konsekwentny – wybierz jeden język
i trzymaj się go w całym projekcie.
Częste błędy w XAML
❌ Błąd 1: Niezamknięty znacznik
❌ Źle – brak zamknięcia
<Button Content="OK"> <!-- Brak </Button> lub /> --> <TextBlock Text="Witaj" />
Błąd kompilacji: XAML jest nieprawidłowy.
✅ Dobrze
<Button Content="OK" /> <TextBlock Text="Witaj" />
❌ Błąd 2: Dwa dzieci w Window
❌ Źle – Window ma dwa dzieci
<Window>
<Button Content="Jeden" />
<Button Content="Dwa" />
<!-- Błąd! Window może mieć
tylko jedno dziecko -->
</Window>
✅ Dobrze – kontener jako jedyne dziecko
<Window>
<StackPanel>
<Button Content="Jeden" />
<Button Content="Dwa" />
</StackPanel>
</Window>
❌ Błąd 3: Wielkość liter w nazwach
❌ Źle – błędna wielkość liter
<button content="OK" />
<!-- XAML rozróżnia
wielkie i małe litery!
"button" ≠ "Button" -->
✅ Dobrze – PascalCase
<Button Content="OK" />
<!-- Nazwy elementów i
atrybutów zawsze
z wielkiej litery -->
❌ Błąd 4: Odwołanie do kontrolki bez nazwy
❌ Źle – brak x:Name
// XAML: <TextBox Text="coś" /> // C# – błąd kompilacji: txtImie.Text = "Jan"; // skąd? // Kontrolka nie ma nazwy!
✅ Dobrze – z x:Name
// XAML: <TextBox x:Name="txtImie" /> // C# – działa: txtImie.Text = "Jan"; // OK!
❌ Błąd 5: Brak cudzysłowu przy atrybucie
❌ Źle
<Button Width=100 />
<!-- Brak cudzysłowu
= błąd XAML -->
✅ Dobrze
<Button Width="100" />
<!-- Wartości ZAWSZE
w cudzysłowie -->
Zadania do wykonania
W pliku XAML napisz okno z StackPanel zawierającym:
- Trzy
TextBlockz różnymi tekstami - Trzy
Buttonz różnymi napisami i kolorami tła
Każdy element zapisz najpierw jako jeden atrybut (krótki zapis), a potem spróbuj przepisać go na zapis elementowy.
Stwórz okno z TextBox (pole na imię) i Button (przycisk „Powitaj”).
Po kliknięciu przycisku odczytaj imię z TextBox
i wyświetl powitanie w TextBlock.
Pamiętaj o nadaniu nazw przez x:Name!
Dodaj do okna z zadania 2 drugi przycisk „Wyczyść”.
Po jego kliknięciu niech czyści pole TextBox
oraz tekst w TextBlock, a siebie samego wyłączy
(IsEnabled = false).
Po wpisaniu czegokolwiek w TextBox przycisk „Wyczyść” ma się włączyć
z powrotem (TextChanged).
W obsłudze zdarzenia Window_Loaded dynamicznie utwórz
w C# (bez XAML) 5 przycisków i dodaj je do StackPanel.
Każdy przycisk ma mieć napis „Przycisk 1″, „Przycisk 2″… itd. Po kliknięciu
dowolnego przycisku TextBlock ma wyświetlić który przycisk
został kliknięty.
// Podpowiedź: aby wiedzieć który przycisk kliknięto, // użyj parametru sender i rzutowania: Button klikniety = (Button)sender; lblWynik.Text = $"Kliknięto: {klikniety.Content}";