ListView w C# - konfiguracja, widoki i obsługa zaznaczenia

Bruno Krawczyk 24 lipca 2026
Przykłady implementacji c# listview: proste wiersze, szybkie przewijanie i indeks sekcji.

Spis treści

Gdy aplikacja desktopowa ma pokazać pliki, użytkowników, produkty albo wyniki wyszukiwania, zwykła lista szybko przestaje wystarczać. Kontrolka ListView w C# pozwala wyświetlać elementy jako prostą listę, tabelę z kolumnami lub widok z ikonami, dlatego dobrze sprawdza się w narzędziach Windows Forms i WPF. Pokażę, kiedy jej używać, jak ją skonfigurować, jak obsługiwać zaznaczenie oraz kiedy lepiej wybrać DataGridView albo inną kontrolkę.

Najważniejsze informacje o kontrolce ListView w C#

  • Windows Forms oferuje widoki listy, szczegółów, dużych ikon i małych ikon.
  • WPF wykorzystuje ListView głównie z wiązaniem danych i szablonami XAML.
  • Widok Details pozwala budować tabelę z kolumnami i dodatkowymi informacjami.
  • Przy dużych zbiorach danych lepiej użyć DataGridView, wirtualizacji albo stronicowania.
  • Najczęstsze błędy dotyczą braku konfiguracji widoku, niepoprawnego wiązania danych i obsługi zaznaczenia.

Do czego służy ListView w aplikacjach Windows

ListView służy do prezentowania kolekcji obiektów w formie, którą użytkownik może szybko przeglądać. W Windows Forms kontrolka przypomina rozwiązania znane z Eksploratora plików. Jeden element może mieć tekst, ikonę, stan zaznaczenia, pola dodatkowe oraz dane przechowywane w kolekcji SubItems.

W praktyce używam jej wtedy, gdy użytkownik ma nie tylko zobaczyć dane, lecz także wybrać konkretny element, otworzyć szczegóły, zaznaczyć kilka pozycji albo przełączyć sposób prezentacji. Dobrym przykładem jest lista plików, gdzie jedna kolumna pokazuje nazwę, druga rozmiar, a trzecia datę modyfikacji.

Trzeba jednak rozróżnić dwie kontrolki o tej samej nazwie. Windows Forms ListView jest nastawiony na ręczne zarządzanie elementami i klasyczny interfejs desktopowy. WPF ListView znacznie częściej działa z wiązaniem danych, szablonami oraz językiem XAML.

Technologia Najczęstszy sposób pracy Dobre zastosowanie
Windows Forms Dodawanie ListViewItem w kodzie C# Listy plików, proste panele administracyjne, starsze aplikacje firmowe
WPF ItemsSource, DataTemplate i binding Nowoczesne interfejsy, widoki MVVM, elastyczne układy danych
DataGridView Siatka danych z edycją i sortowaniem Duże tabele, formularze biznesowe, dane wymagające edycji

Moja praktyczna zasada jest prosta. Jeżeli ważny jest wybór elementu i sposób jego prezentacji, ListView będzie dobrym kandydatem. Jeżeli najważniejsza jest masowa edycja danych, sortowanie wielu kolumn i praca jak w arkuszu, zwykle szybciej osiągnę dobry efekt za pomocą DataGridView.

Jak skonfigurować ListView w Windows Forms

W projekcie Windows Forms dodaj kontrolkę ListView z przybornika Visual Studio albo utwórz ją w kodzie. Najważniejszą właściwością jest View, ponieważ określa sposób prezentacji danych. Dla układu tabelarycznego ustawiam wartość View.Details, a następnie dodaję kolumny.

using System;
using System.Drawing;
using System.Windows.Forms;

public partial class MainForm : Form
{
    private readonly ListView ordersListView = new();

    public MainForm()
    {
        InitializeComponent();
        ConfigureListView();
        LoadOrders();
    }

    private void ConfigureListView()
    {
        ordersListView.Dock = DockStyle.Fill;
        ordersListView.View = View.Details;
        ordersListView.FullRowSelect = true;
        ordersListView.GridLines = true;
        ordersListView.MultiSelect = false;
        ordersListView.HideSelection = false;

        ordersListView.Columns.Add("Numer", 90);
        ordersListView.Columns.Add("Klient", 180);
        ordersListView.Columns.Add("Status", 120);
        ordersListView.Columns.Add("Data", 120);

        ordersListView.SelectedIndexChanged += OrdersListView_SelectedIndexChanged;

        Controls.Add(ordersListView);
    }

    private void LoadOrders()
    {
        var firstOrder = new ListViewItem("1001");
        firstOrder.SubItems.Add("Anna Kowalska");
        firstOrder.SubItems.Add("Nowe");
        firstOrder.SubItems.Add("2026-09-16");

        var secondOrder = new ListViewItem("1002");
        secondOrder.SubItems.Add("Piotr Nowak");
        secondOrder.SubItems.Add("Wysłane");
        secondOrder.SubItems.Add("2026-09-15");

        ordersListView.Items.Add(firstOrder);
        ordersListView.Items.Add(secondOrder);
    }

    private void OrdersListView_SelectedIndexChanged(
        object? sender,
        EventArgs e)
    {
        if (ordersListView.SelectedItems.Count == 0)
        {
            return;
        }

        ListViewItem selectedItem = ordersListView.SelectedItems[0];
        string orderNumber = selectedItem.Text;

        Text = $"Wybrane zamówienie: {orderNumber}";
    }
}

Pierwszy tekst przekazany do konstruktora ListViewItem trafia do właściwości Text, czyli pierwszej kolumny. Każdy kolejny element dodany przez SubItems.Add odpowiada następnej kolumnie. To drobny szczegół, ale właśnie tutaj początkujący często próbują tworzyć osobne obiekty dla każdej komórki, choć w tym modelu komórki są częścią jednego elementu.

Właściwość FullRowSelect sprawia, że zaznaczenie obejmuje cały wiersz. HideSelection = false pozwala zachować widoczne zaznaczenie po przejściu fokusu na inną kontrolkę. W aplikacji z panelem szczegółów ustawienie obu właściwości zwykle wyraźnie poprawia czytelność.

Najważniejsze tryby widoku

  • View.List pokazuje prostą listę tekstową.
  • View.Details tworzy widok kolumnowy z nagłówkami.
  • View.LargeIcon prezentuje duże ikony i podpisy.
  • View.SmallIcon wykorzystuje mniejsze ikony, dzięki czemu mieści więcej elementów.
  • View.Tile pozwala pokazać element w formie kafelka z dodatkowymi informacjami.

Do danych biznesowych najczęściej wybieram View.Details. Widoki ikon mają sens przy plikach, zdjęciach, skrótach lub typach dokumentów. Nie stosowałbym ich do długiej listy zamówień, ponieważ użytkownik traci wtedy możliwość szybkiego porównania wartości w kolumnach.

Jak dodawać dane, ikony i reakcję na zaznaczenie

Najprostsze listy można wypełnić ręcznie, ale w prawdziwej aplikacji dane zwykle pochodzą z bazy, pliku JSON albo wywołania API. W takim przypadku warto oddzielić pobieranie danych od ich prezentacji. Dzięki temu kontrolka odpowiada za interfejs, a kod domenowy za przygotowanie informacji.

Przy większej liczbie elementów dodaję je partiami przez BeginUpdate i EndUpdate. Zapobiega to odświeżaniu interfejsu po każdym dodanym wierszu i ogranicza migotanie kontrolki.

private void DisplayProducts(IEnumerable products)
{
    ordersListView.BeginUpdate();

    try
    {
        ordersListView.Items.Clear();

        foreach (Product product in products)
        {
            var item = new ListViewItem(product.Code);
            item.SubItems.Add(product.Name);
            item.SubItems.Add(product.Price.ToString("C"));
            item.SubItems.Add(product.Stock.ToString());

            item.Tag = product;
            ordersListView.Items.Add(item);
        }
    }
    finally
    {
        ordersListView.EndUpdate();
    }
}

private sealed class Product
{
    public string Code { get; init; } = string.Empty;
    public string Name { get; init; } = string.Empty;
    public decimal Price { get; init; }
    public int Stock { get; init; }
}

Właściwość Tag jest bardzo użyteczna, bo pozwala przechować cały obiekt powiązany z wierszem. Dzięki temu po kliknięciu nie muszę odtwarzać produktu na podstawie tekstu w kolumnach. Pobieram go bezpośrednio z SelectedItems[0].Tag, co jest bezpieczniejsze i wygodniejsze przy dalszej rozbudowie aplikacji.

Ikony wymagają użycia ImageList. Do dużych i małych widoków można przypisać osobne listy obrazów, a konkretny element wskazuje ikonę przez ImageIndex albo ImageKey.

var icons = new ImageList
{
    ImageSize = new Size(20, 20)
};

icons.Images.Add("available", Properties.Resources.Available);
icons.Images.Add("unavailable", Properties.Resources.Unavailable);

ordersListView.SmallImageList = icons;

var item = new ListViewItem("Produkt A")
{
    ImageKey = "available"
};

item.SubItems.Add("Dostępny");
ordersListView.Items.Add(item);

Ikona powinna wspierać informację tekstową, a nie ją zastępować. Sam kolor lub symbol nie wystarczy, gdy użytkownik ma rozpoznać status zamówienia, szczególnie przy ustawieniach systemowych zwiększających kontrast.

Obsługa kliknięcia i dwukliku

Do zwykłego reagowania na zmianę zaznaczenia używam SelectedIndexChanged. Do otwierania formularza szczegółów wygodniejszy jest DoubleClick albo zdarzenie ItemActivate. Przed odczytem danych zawsze sprawdzam, czy element faktycznie został zaznaczony.

private void OrdersListView_ItemActivate(
    object? sender,
    EventArgs e)
{
    if (ordersListView.SelectedItems.Count == 0)
    {
        return;
    }

    if (ordersListView.SelectedItems[0].Tag is Product product)
    {
        using var detailsForm = new ProductDetailsForm(product);
        detailsForm.ShowDialog(this);
    }
}

Takie sprawdzenie ma znaczenie także przy usuwaniu elementu, zmianie filtrów i odświeżaniu listy. Zdarzenie może wywołać się w momencie, gdy zaznaczenie właśnie zniknęło, więc odwołanie do indeksu bez kontroli często kończy się wyjątkiem.

ListView w WPF i wiązanie danych

WPF korzysta z tej samej nazwy kontrolki, ale podejście jest inne. Zamiast tworzyć każdy wiersz ręcznie, zwykle ustawiam ItemsSource i pozwalam, aby XAML opisał sposób prezentacji obiektu. Taki model dobrze pasuje do architektury MVVM, w której widok nie powinien znać szczegółów pobierania danych.


    
        
            
            
            
            
        
    

W tym przykładzie GridView odpowiada za kolumny, a właściwości obiektu są wskazywane przez binding. Jeśli zmieni się kolekcja ObservableCollection, interfejs może automatycznie pokazać nowe elementy bez ręcznego wywoływania metod odświeżających.

public partial class ProductsViewModel : ObservableObject
{
    public ObservableCollection Products { get; } = new();

    [ObservableProperty]
    private Product? selectedProduct;

    public ProductsViewModel()
    {
        Products.Add(new Product
        {
            Code = "A-100",
            Name = "Klawiatura",
            Price = 199.00m,
            Stock = 14
        });
    }
}

WPF daje większą swobodę wyglądu. Za pomocą ItemTemplate można zbudować kartę produktu z ikoną, dwoma opisami i przyciskiem akcji. Z drugiej strony oznacza to więcej decyzji dotyczących bindingu, stylów i szablonów. Jeśli tworzę mały, klasyczny formularz, Windows Forms bywa po prostu szybsze.

Przeczytaj również: var w JavaScripcie - zakres, hoisting i pułapki starego kodu

Windows Forms czy WPF

Nie traktuję jednego podejścia jako zawsze lepszego. Wybór zależy od tego, czy ważniejsza jest szybkość zbudowania prostego narzędzia, czy elastyczność interfejsu i rozdzielenie warstw.

Kryterium Windows Forms ListView WPF ListView
Próg wejścia Niższy przy prostych formularzach Wyższy przez XAML i binding
Układ tabelaryczny Wbudowany widok Details GridView w XAML
Personalizacja wyglądu Ograniczona, ale szybka Bardzo duża dzięki szablonom
Architektura MVVM Możliwa, lecz mniej naturalna Dobrze wspierana
Ręczne zarządzanie elementami Proste i bezpośrednie Zwykle zastępowane bindingiem

Kiedy ListView nie jest najlepszym wyborem

ListView dobrze radzi sobie z prezentacją, ale nie jest uniwersalną tabelą. Gdy użytkownik ma edytować wiele komórek, sortować dane po kilku kolumnach, filtrować tysiące rekordów i korzystać z walidacji, DataGridView w Windows Forms będzie zwykle praktyczniejszy.

WPF-owy ListView także nie rozwiązuje automatycznie problemu dużych kolekcji. Przy kilku tysiącach elementów trzeba sprawdzić wirtualizację, sposób generowania kontenerów oraz koszt szablonów. Ciężki ItemTemplate z wieloma obrazami może spowolnić przewijanie bardziej niż sama liczba rekordów.

Potrzeba Najlepszy pierwszy wybór Dlaczego
Lista plików z ikonami ListView Obsługa ikon i kilku trybów widoku
Tabela z edycją komórek DataGridView Gotowe mechanizmy edycji, sortowania i walidacji
Prosta lista wyboru jednej wartości ListBox lub ComboBox Mniejszy narzut i prostszy interfejs
Hierarchia folderów i kategorii TreeView Naturalna prezentacja relacji rodzic-dziecko
Karty produktów i niestandardowy wygląd WPF ListView Szablony pozwalają zaprojektować własny układ

Najczęstszy błąd polega na wyborze ListView tylko dlatego, że potrafi pokazać kolumny. Sama obecność kolumn nie czyni z niego pełnoprawnego edytora danych. Zanim dodam kontrolkę, spisuję trzy podstawowe akcje użytkownika: czyta, wybiera czy edytuje. Jeśli odpowiedź brzmi głównie „edytuje”, zaczynam od DataGridView albo innego komponentu tabelarycznego.

Typowe błędy i sposoby ich uniknięcia

Najbardziej widoczny błąd w Windows Forms to dodanie kolumn bez ustawienia View.Details. Kolumny istnieją w kolekcji, ale użytkownik ich nie zobaczy, jeśli kontrolka pracuje w trybie List albo LargeIcon. Zawsze konfiguruję widok razem z kolumnami, najlepiej w jednym miejscu.

Drugi problem to traktowanie tekstu z kolumn jako głównego źródła danych. Gdy format ceny, daty albo statusu się zmieni, kod zaczyna parsować tekst interfejsu. Przechowywanie obiektu w Tag w Windows Forms lub użycie bindingu w WPF utrzymuje dane i prezentację w osobnych rolach.

  • Nie dodawaj tysięcy elementów pojedynczo bez BeginUpdate i EndUpdate.
  • Nie odwołuj się do SelectedItems[0] bez sprawdzenia, czy lista zawiera zaznaczenie.
  • Nie ustawiaj na sztywno zbyt wąskich kolumn, jeśli tekst pochodzi od użytkownika.
  • Nie używaj ikon jako jedynego oznaczenia statusu.
  • Nie pobieraj danych z bazy synchronicznie w wątku interfejsu, gdy lista może zawierać wiele rekordów.
  • Nie mieszaj ręcznego dodawania elementów z bindingiem w jednym widoku bez wyraźnego powodu.

Przy odświeżaniu danych szczególnie uważałbym na utratę zaznaczenia. Wyczyszczenie kolekcji i ponowne dodanie elementów może sprawić, że panel szczegółów nagle stanie się pusty. W aplikacjach, w których użytkownik często filtruje listę, przechowuję identyfikator wybranego obiektu i próbuję przywrócić zaznaczenie po zakończeniu odświeżania.

Kolumny powinny mieć sensowne szerokości, ale nie muszą być idealnie dopasowane do każdego tekstu. Funkcja automatycznego dopasowania do zawartości może być kosztowna przy dużych zbiorach, dlatego przy dynamicznych danych lepiej ustalić rozsądne minimum i pozwolić użytkownikowi zmienić szerokość.

Jak podjąć dobrą decyzję w konkretnym projekcie

Jeśli buduję panel z listą plików, ikonami, zaznaczeniem i dwuklikiem otwierającym szczegóły, wybieram ListView bez większych obaw. W Windows Forms konfiguruję widok Details, dodaję kolumny i przechowuję obiekt domenowy w Tag. W WPF zaczynam od modelu danych, ObservableCollection i ItemsSource.

Jeśli aplikacja ma prezentować kilkanaście tysięcy rekordów, od razu planuję filtrowanie po stronie źródła, stronicowanie albo wirtualizację. Sama kontrolka nie sprawi, że duży zbiór stanie się lekki. Największą różnicę robi sposób pobierania i aktualizowania danych, a dopiero później wybór konkretnego widoku.

Ostatecznie ListView jest dobrym narzędziem do list, które mają być czytelne, wybieralne i czasem wzbogacone o ikony lub dodatkowe kolumny. Wybieram je świadomie wtedy, gdy interfejs ma przede wszystkim prezentować dane. Gdy pojawia się intensywna edycja, rozbudowane sortowanie albo bardzo duża tabela, sięgam po kontrolkę zaprojektowaną dokładnie do tych zadań.

Najbezpieczniejszy pierwszy krok to przygotowanie małego prototypu z kilkunastoma prawdziwymi rekordami. Sprawdź w nim zaznaczanie, filtrowanie, zmianę rozmiaru okna i odświeżanie danych. Taki test szybko pokaże, czy ListView pasuje do aplikacji, zanim logika interfejsu zdąży rozrosnąć się wokół niewłaściwego wyboru.

FAQ - Najczęstsze pytania

Ustaw właściwość View na View.Details, a następnie dodaj kolumny przez Columns.Add. Dane umieszczaj w obiektach ListViewItem, używając Text dla pierwszej kolumny i SubItems dla kolejnych. Warto także ustawić FullRowSelect oraz HideSelection, aby zaznaczenie było czytelne.

Przed odczytem SelectedItems[0] sprawdź, czy kolekcja zawiera zaznaczony element. Powiązany obiekt można przechować we właściwości Tag, dzięki czemu nie trzeba odtwarzać danych na podstawie tekstu w kolumnach. Do reagowania na zmianę zaznaczenia służy SelectedIndexChanged, a do otwierania szczegółów można użyć ItemActivate lub dwukliku.

W Windows Forms elementy najczęściej dodaje się ręcznie jako ListViewItem, natomiast WPF zwykle korzysta z ItemsSource, bindingu i szablonów XAML. WPF pozwala łatwiej budować niestandardowy wygląd oraz architekturę MVVM, a Windows Forms szybciej sprawdza się w prostych, klasycznych formularzach.

DataGridView będzie lepszym wyborem, gdy użytkownik ma edytować wiele komórek, sortować dane po kilku kolumnach, filtrować tysiące rekordów lub korzystać z walidacji. ListView lepiej pasuje do prezentowania i wybierania elementów, na przykład listy plików z ikonami. Przy dużych kolekcjach warto dodatkowo rozważyć filtrowanie po stronie źródła, stronicowanie albo wirtualizację.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi

windows forms
wpf
mvvm
listview
datagridview
Autor Bruno Krawczyk
Bruno Krawczyk
Mam na imię Bruno i od 8 lat zgłębiam tajniki programowania w ekosystemie .NET, chmury Azure oraz sztucznej inteligencji. Moja przygoda z technologią zaczęła się od ciekawości, jak złożone systemy mogą ułatwiać codzienne życie i rozwiązywać realne problemy. Dziś moją misją jest dzielenie się tą wiedzą, starając się przybliżyć nawet najbardziej skomplikowane zagadnienia w sposób zrozumiały i przystępny dla każdego. W moich artykułach na kursdotnet.pl skupiam się na praktycznych aspektach, analizuję najnowsze trendy i weryfikuję informacje, aby dostarczyć Wam treści, które są nie tylko dokładne i aktualne, ale przede wszystkim użyteczne w Waszej własnej ścieżce rozwoju technologicznego.

Udostępnij artykuł

Napisz komentarz