Po co lista i jak to działa
Gdy masz wiele elementów (np. imiona, produkty), nie dodajesz 100 etykiet. Używasz listy – w MAUI najnowszą kontrolką jest CollectionView.
Jej mechanizm jest prosty:
- ItemsSource – źródło danych (np. lista imion),
- ItemTemplate – szablon jak narysować jeden element listy,
- Binding – „połączenie” pól obiektu z kontrolkami w szablonie,
- SelectionChanged – reakcja na zaznaczenie elementu.
Dla początkujących polecam ObservableCollection zamiast
List. Gdy coś dodasz/usuńsz, UI odświeża się samo.
Co tworzymy w projekcie (VS 2022)
Na start zrobimy wersję podstawową (lista stringów – zero modeli).
Potem wersję + (opcjonalnie): lista obiektów z dwoma polami.
- Dodaj stronę widoku:
Solution Explorer → prawy klik na projekt → Add → New Item… → .NET MAUI ContentPage (XAML)
Nazwa: ListaPage.xaml
To utworzy dwa pliki:ListaPage.xaml(UI) iListaPage.xaml.cs(logika). - (Wersja +) Jeśli chcesz listę obiektów z polami, dodamy prostą Class później.
Wersja podstawowa — lista stringów (najprostsza)
ListaPage.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="TwojProjekt.ListaPage"
Title="Lista imion">
<VerticalStackLayout Padding="16" Spacing="12">
<!-- Dodawanie nowej pozycji -->
<HorizontalStackLayout Spacing="8">
<Entry x:Name="NoweImieEntry" Placeholder="Wpisz imię"/>
<Button Text="Dodaj" Clicked="Dodaj_Click"/>
</HorizontalStackLayout>
<!-- Lista: gdy pusta, pokaż wiadomość -->
<CollectionView x:Name="Lista"
SelectionMode="Single"
SelectionChanged="Lista_SelectionChanged">
<CollectionView.EmptyView>
<Label Text="Brak elementów. Dodaj coś powyżej." TextColor="Gray"/>
</CollectionView.EmptyView>
<!-- Szablon jednego elementu (tu: zwykły string) -->
<CollectionView.ItemTemplate>
<DataTemplate>
<!-- {Binding} bez nazwy = cały string -->
<Label Text="{Binding}" FontSize="18" Padding="6,10"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!-- Operacje na liście -->
<HorizontalStackLayout Spacing="8">
<Button Text="Usuń zaznaczone" Clicked="UsunZaznaczone_Click"/>
<Button Text="Wyczyść listę" Clicked="Wyczysc_Click"/>
</HorizontalStackLayout>
<!-- Informacje dla użytkownika -->
<Label x:Name="Status" TextColor="Gray"/>
</VerticalStackLayout>
</ContentPage>
ListaPage.xaml.cs
using System.Collections.ObjectModel; // ObservableCollection
namespace TwojProjekt;
public partial class ListaPage : ContentPage
{
// 1) Kolekcja, która "powiadamia" UI o zmianach (dodanie/usunięcie)
private ObservableCollection<string> _imiona = new();
public ListaPage()
{
InitializeComponent(); // 2) Tworzy kontrolki z XAML
Lista.ItemsSource = _imiona; // 3) Podpinamy źródło danych do listy
}
// Dodaj nowy element
private void Dodaj_Click(object sender, EventArgs e)
{
if (!string.IsNullOrWhiteSpace(NoweImieEntry.Text))
{
_imiona.Add(NoweImieEntry.Text.Trim()); // 4) Dodanie do kolekcji automatycznie odświeży UI
NoweImieEntry.Text = ""; // 5) Wyczyść pole
Status.Text = "Dodano element.";
}
else
{
Status.Text = "Wpisz imię przed dodaniem.";
}
}
// Usuwanie zaznaczonego elementu
private void UsunZaznaczone_Click(object sender, EventArgs e)
{
if (Lista.SelectedItem is string wybrane)
{
_imiona.Remove(wybrane);
Status.Text = $"Usunięto: {wybrane}";
}
else
{
Status.Text = "Najpierw zaznacz element na liście.";
}
}
// Czyści całą listę
private void Wyczysc_Click(object sender, EventArgs e)
{
_imiona.Clear();
Status.Text = "Wyczyszczono listę.";
}
// Reakcja na zaznaczenie w liście
private void Lista_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection.FirstOrDefault() is string wybrane)
Status.Text = $"Zaznaczono: {wybrane}";
}
}
Co się stało (wersja podstawowa)
- Użyliśmy ObservableCollection – UI samo wie, że doszedł/odpadł element.
ItemsSourcełączy listę danych zCollectionView.ItemTemplaterysuje jeden element. Dla stringówText="{Binding}"to cały tekst.- Zdarzenia:
Dodaj_Click– dopisuje do_imiona,UsunZaznaczone_Click– usuwa zaznaczony,Wyczysc_Click– czyści całość,SelectionChanged– pokazuje, co zaznaczono.
To jest najprostszy schemat INF.04 na „lista + operacje na liście”.
Wersja + (opcjonalnie) — lista obiektów z polami
Chcesz pokazać dwa pola (np. nazwa i cena)? Potrzebny jest prosty model – zwykła klasa.
Dodaj model (Class)
Solution Explorer → prawy klik na projekt → Add → Class → nazwa Produkt.cs
Wklej:
namespace TwojProjekt;
// Prosty model: 2 publiczne właściwości
public class Produkt
{
public string Nazwa { get; set; } = "";
public decimal Cena { get; set; } = 0m;
}
Zmień UI, by wyświetlać 2 pola
W ListaPage.xaml podmień część z polami i szablonem listy na taki kod:
<!-- Formularz dodawania produktu -->
<HorizontalStackLayout Spacing="8">
<Entry x:Name="NazwaEntry" Placeholder="Nazwa produktu" WidthRequest="180"/>
<Entry x:Name="CenaEntry" Placeholder="Cena (np. 19,99)" Keyboard="Numeric" WidthRequest="120"/>
<Button Text="Dodaj" Clicked="DodajProdukt_Click"/>
</HorizontalStackLayout>
<!-- Lista produktów z 2 kolumnami -->
<CollectionView x:Name="ListaProd"
SelectionMode="Single"
SelectionChanged="ListaProd_SelectionChanged">
<CollectionView.EmptyView>
<Label Text="Brak produktów." TextColor="Gray"/>
</CollectionView.EmptyView>
<CollectionView.ItemTemplate>
<DataTemplate>
<!-- Grid: 2 kolumny: nazwa i cena -->
<Grid Padding="6,10" ColumnDefinitions="*,Auto">
<Label Text="{Binding Nazwa}" Grid.Column="0" FontSize="18"/>
<Label Text="{Binding Cena, StringFormat='{}{0:C}'}" Grid.Column="1" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!-- Operacje na produktach -->
<HorizontalStackLayout Spacing="8">
<Button Text="Usuń zaznaczony" Clicked="UsunProdukt_Click"/>
<Button Text="Wyczyść listę" Clicked="WyczyscProdukty_Click"/>
</HorizontalStackLayout>
<Label x:Name="Status2" TextColor="Gray"/>
Logika dla produktów – ListaPage.xaml.cs
Dodaj drugi zestaw pól i metod (nie kasuj wersji podstawowej, możesz mieć obie sekcje na jednej stronie):
using System.Collections.ObjectModel;
public partial class ListaPage : ContentPage
{
// ...
private ObservableCollection<Produkt> _produkty = new();
public ListaPage()
{
InitializeComponent();
// ... (podstawowa lista stringów)
Lista.ItemsSource = _imiona;
// Lista produktów (dla wersji +)
ListaProd.ItemsSource = _produkty;
}
private void DodajProdukt_Click(object sender, EventArgs e)
{
// Prosta walidacja i parsowanie ceny
if (string.IsNullOrWhiteSpace(NazwaEntry.Text))
{
Status2.Text = "Podaj nazwę.";
return;
}
if (!decimal.TryParse(CenaEntry.Text?.Replace(',', '.'), System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture, out var cena))
{
Status2.Text = "Podaj poprawną cenę (np. 19,99).";
return;
}
_produkty.Add(new Produkt { Nazwa = NazwaEntry.Text.Trim(), Cena = cena });
NazwaEntry.Text = "";
CenaEntry.Text = "";
Status2.Text = "Dodano produkt.";
}
private void UsunProdukt_Click(object sender, EventArgs e)
{
if (ListaProd.SelectedItem is Produkt p)
{
_produkty.Remove(p);
Status2.Text = $"Usunięto: {p.Nazwa}";
}
else
{
Status2.Text = "Zaznacz produkt na liście.";
}
}
private void WyczyscProdukty_Click(object sender, EventArgs e)
{
_produkty.Clear();
Status2.Text = "Wyczyszczono listę produktów.";
}
private void ListaProd_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection.FirstOrDefault() is Produkt p)
Status2.Text = $"Zaznaczono: {p.Nazwa} ({p.Cena:C})";
}
}
Co się stało (wersja +)
- Dodaliśmy model
Produkt(zwykłaClass) z dwiema właściwościami. - W
ItemTemplatebindowaliśmyNazwaiCenado dwóch etykiet (wGridzie). - Użyliśmy
decimal.TryParse, żeby zamienić tekst zEntryna liczbę (prosta walidacja). - Cała reszta identyczna: ObservableCollection → ItemsSource → operacje.
Najczęstsze potknięcia i szybkie naprawy
- Dodałem do
List<>, a UI się nie odświeża → użyj ObservableCollection. - Klikam „Usuń”, ale nic się nie dzieje → sprawdź, czy coś jest zaznaczone (
SelectedItem). - Błąd bindowania → nazwy właściwości w XAML muszą zgadzać się z nazwami w klasie (np.
Nazwa,Cena). - Format ceny →
StringFormat='{}{0:C}'formatuje na walutę wg języka systemu.
Podsumowanie
- Znasz CollectionView:
ItemsSource,ItemTemplate,SelectionChanged. - Wiesz, że do automatycznego odświeżania UI najlepiej służy ObservableCollection.
- Umiemy zobaczyć dwie wersje: lista stringów i lista obiektów z dwoma polami.
- To pokrywa typowe wymagania INF.04 dotyczące list i operacji na danych w UI.

